Java 列表中不兼容的等式约束

Java Incompatible equality constraint in list

我有一个多级 class 结构,我想将它们的实现传递给一个可以调用它们的函数,但是我得到一个 Incompatible equality constraint: Test.SubDTO2 and Test.SubDTO 错误。

代码如下:

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) {
  }
}

我尝试实现的更详细的示例:

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
  }
}

确切的错误信息:

[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

我需要函数 func 中的列表来扩展 DTO 并实现 Interf,因为我在它们上面调用了某些函数。

为什么会这样?如果我更改 func 的签名并仅传递一个 DAO,它工作正常,但我需要它与多个一起工作。

这里我有什么选择?

我试过多个java版本(1.8+),都一样。

你的函数应该这样声明:

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

请注意,我将 List<DAO<T>> 更改为 List<DAO<? extends T>>。这是因为表达式 Arrays.asList(daoImpl1, daoImpl2) 产生类型

的值
List<DAO<? extends DTO & Interf>>

(当然,这不是 Java 中类型的真正语法。Java 中没有交集类型的语法,但 Java 在执行类型时确实知道它们推理,如果你使用 var,你可以在你的代码中使用这些类型。我在这里使用这个符号只是为了说明目的。)

如果你知道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

这样做的原因很直观。想象一下,如果 DAO 只是 T.

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

    public T getT() {
        return t;
    }

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

如果 Arrays.asList(daoImpl1, daoImpl2) 生成了 DAO<DTO & Interf> 的列表(没有 extendssuper),您可以调用 getT andsetT关于元素列表!能够调用 setT 是特别危险的——你可以这样做:

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

如果 someRandomNumber 恰好是 1,我们得到第二个元素,即 DAO<SubDTO2> 怎么办?将 SubDTO 放入其中会破坏整个 type-safety 泛型。

对像 [daoImpl1, daoImpl2] 这样的列表的元素唯一 type-safe 的事情就是将它们用作 DTO & Interfs 的生产者,因此类型被标记为 ? extends DTO & Interf.这意味着如果您在 DAO 上有任何接受 T 的方法,您将无法在此列表 * 的元素上调用它们。

另请注意,以防万一我不清楚,不仅仅是列表是生产者 - 该列表既是 DAO 的生产者又是消费者。只是列表中的 DAO 是他们 T 的生产者。


* 除了传递 nulls.