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 & Interf
s/T
s, but does not consume DTO & Interf
s/T
s. 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 & Interf
s, 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 DAO
s. It's just that the DAO
s in the list are producers of their T
s.
* except by passing null
s.