Home > Blockchain >  Java Incompatible equality constraint in list
Java Incompatible equality constraint in list

Time:03-29

I have a multi level class structure and want to pass their implementation to a function that can call functions on them, but I get an Incompatible equality constraint: Test.SubDTO2 and Test.SubDTO error.

Here is the code:

public class Test {

  abstract class DTO { }

  class SubDTO extends DTO implements Interf{ }

  class SubDTO2 extends DTO implements Interf{ }

  class DAO<T extends DTO> { }

  interface Interf { }

  static DAO<SubDTO> daoImpl1;
  static DAO<SubDTO2> daoImpl2;

  public static void main(String... args) {
    func(Arrays.asList(daoImpl1, daoImpl2)); // <- error is in this line
  }

  static <T extends DTO & Interf> void func(List<DAO<T>> arg) {
  }
}

A more detailed example on what I try to achieve:

public class Test {

  abstract class DTO {
    abstract void func1();
  }

  class SubDTO extends DTO implements Interf{
    @Override
    public void func2() {
      // comes from Interf
    }

    @Override
    public void func1() {
      // comes from DTO
    }
  }

  class SubDTO2 extends DTO implements Interf{
    @Override
    public void func2() {
      // comes from Interf
    }

    @Override
    public void func1() {
      // comes from DTO
    }
  }

  class DAO<T extends DTO> {
    public T dto() {
      return null;
    }
  }

  interface Interf {
    void func2();
  }

  static DAO<SubDTO> daoImpl1;
  static DAO<SubDTO2> daoImpl2;

  public static void main(String... args) {
    func(Arrays.asList(daoImpl1, daoImpl2));
  }

  static <T extends DTO & Interf> void func(List<? extends DAO<? extends DTO>> arg) {
    arg.get(0).dto().func1(); // <- I can't call func2() here
  }
}

exact error message:

[ERROR]   required: java.util.List<Test.DAO<T>>
[ERROR]   found:    java.util.List<Test.DAO<? extends Test.DTO>>
[ERROR]   reason: inference variable T has incompatible equality constraints Test.SubDTO2,Test.SubDTO

I need the list in the function func to extend DTO and also implement Interf as well, because I call certain functions on them.

Why is this happening? It works fine if I change the signature of the func and pass only one DAO, but I need it to work with multiple.

What are my options here?

I tried it with multiple java versions (1.8 ), all the same.

CodePudding user response:

Your function should be declared like this:

static <T extends DTO & Interf> void func(List<DAO<? extends T>> arg) {

Notice that I changed List<DAO<T>> to List<DAO<? extends T>>. This is because the expression Arrays.asList(daoImpl1, daoImpl2) produces a value of type

List<DAO<? extends DTO & Interf>>

(Of course, this isn't real syntax for a type in Java. There's no syntax for intersection types in Java but Java does know about them when doing type inference, and you could have these types in your code if you use var. I use this notation here just for illustrative purposes.)

If you know PECS, you'll know that this is a list of DAOs that produces DTO & Interfs/Ts, but does not consume DTO & Interfs/Ts. If you are lost at this point, please go read the PECS post - it's great. See also: Difference between <? super T> and <? extends T> in Java

The reason why it does this is quite intuitive. Imagine if DAO is just a container for a T.

static class DAO<T extends DTO> {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
   }
}

If Arrays.asList(daoImpl1, daoImpl2) had produced a list of DAO<DTO & Interf> (with no extends or super), you'd be able to call getT and setT on elements of the list! And being able to call setT is especially dangerous you see - you'd be able to do this:

// suppose arg is a List<DAO<DTO & Interf>>
arg.get(someRandomNumber).setT(new SubDTO());

What if someRandomNumber happens to be 1, and we get the second element, which is a DAO<SubDTO2>? Putting a SubDTO inside that destroys the whole type-safety of generics.

The only type-safe thing to do on elements of such a list like [daoImpl1, daoImpl2] is to use them as producers of DTO & Interfs, hence the type is marked ? extends DTO & Interf. This means that if you have any methods on DAO that takes in a T, you won't be able to call them on elements of this list*.

Also note that, just in case I was not clear, it is not the list that is only a producer - the list is both a producer and consumer of DAOs. It's just that the DAOs in the list are producers of their Ts.


* except by passing nulls.

  • Related