【问题标题】:Return class literal as intersection type将类文字作为交集类型返回
【发布时间】:2016-08-26 12:56:28
【问题描述】:

我有几个实现一些通用接口的枚举,我想从方法中返回类文字。但是我无法正确指定交集类型。请参阅下面说明问题的代码示例。

public class GenericsTest {    
  interface Iface {   
  }

  enum E1 implements Iface {    
  }

  enum E2 implements Iface {    
  }

  <E extends Enum<E> & Iface> Class<E> getEnum1() {
    return E1.class;  //ERROR incompatible types: java.lang.Class<GenericsTest.E1> cannot be converted to java.lang.Class<E>
  }

  Class<? extends Enum<?>> getEnum3() {
    return E1.class;  //OK
  }

  Class<? extends Iface> getEnum4() {
    return E1.class;  //OK
  }

  <E extends Enum<E> & Iface> void enumParam(Class<E> p) {
    enumParam(E1.class);  //OK
  }

}

#getEnum1 方法无法编译。有趣的是,它在#enumParam 中用作参数值。如何指定交集类型才能从方法返回类文字?

【问题讨论】:

  • 我认为这与有界通配符泛型类型中的多个接口无关。即使签名只有&lt;E extends Enum&lt;E&gt;&gt; Class&lt;E&gt; getEnum1(),它也不会编译。

标签: java generics enums


【解决方案1】:

getEnum1 方法无法编译,原因与此相同:

public <E> E foo() {
    return "bar"; // Compile time error: Incompatible types E and String
}

看到了吗?仅仅因为类型参数E 最终可能是一个字符串,并不意味着它与String兼容。可以将此函数称为Integer i = x.&lt;Integer&gt;foo();,因此在这里返回字符串没有意义。

getEnum1 完全一样。类型参数E 可以选择为E1 类型(此处命名非常糟糕),但这并不意味着E1 始终与E 兼容。考虑E2 e2 = x.&lt;E2&gt;getEnum1();。您可能想知道为什么编译器在尝试使用错误的类型参数调用它时不只是警告错误。问题是,E2 满足类型约束,因此没有理由在那里显示错误。

那么如何让你的代码正常工作呢?

最简单(也可能是唯一)的方法是在那里摆脱泛型:

Class<E1> getEnum1() {
  return E1.class;
}

如果你不想在这里暴露具体类型E1,那你就不走运了。您所能做的就是将常用功能提取到接口中,然后返回该类型。

【讨论】:

  • 很好解释,谢谢,我明白为什么它不能编译。所以解决方案是使用通配符类型,但是通配符类型不可能有交集,因为 Java 语言不允许这样做。
  • @saberduck 而且也没有定义递归通配符类型的语法。
【解决方案2】:

这里的问题是,鉴于每次使用的上下文,无法保证您的代码返回正确的 Class 类型实例。 例如

final Class<E2> e2var = new GenericsTest().getEnum1();

编译器在getEnum1 处失败,以防止当有人试图对 e2var 引用的对象执行一些E2 绑定操作时出现延迟的运行时错误,实际上总是Class&lt;E1&gt;

要提出解决方案,您需要更具体地说明此类集的预期用途。

相比之下,enumParam 工作并不奇怪,因为它不需要返回匹配的类对象。然而,很明显它会由于无限递归而导致堆栈溢出。

【讨论】:

  • 那么#getEnum3#getEnum4 怎么可能编译没有问题呢?关于向外部代码提供的保证,我看不出它们与交集类型之间的区别?
  • @saberduck 注意 getEnum3 和 getEnum4 返回类型不是由调用上下文固定的......只是分别扩展了 Enum 或 Iface 的 Class 而 E1.class 确实如此。尝试编写一行代码,将其值分配给 E2 类型变量...您需要将它们转换为 E2 因为从使用代码的角度来看,它只知道它返回一个 Enum 或 Iface 但它不知道什么是实际的班级。与 getEnum1 相比,您不必强制转换,因为它应该为接收变量的类返回一个实例......但它不会编译。
  • 我明白你的意思,所以解决方案是不固定类型并放置通配符?,但是不可能有交叉类型的通配符
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-26
  • 2015-06-29
  • 2022-07-07
  • 2020-08-19
相关资源
最近更新 更多