generics返回types的上界 – 接口与类 – 令人惊讶的有效的代码

这是来自第三方库API的真实示例,但简化了。

用Oracle JDK 8u72编译

考虑这两种方法:

<X extends CharSequence> X getCharSequence() { return (X) "hello"; } <X extends String> X getString() { return (X) "hello"; } 

两人都报告“未经检查的演员”的警告 – 我明白了为什么。 让我感到困惑的是为什么我可以打电话

 Integer x = getCharSequence(); 

它编译? 编译器应该知道Integer没有实现CharSequence 。 打电话给

 Integer y = getString(); 

给出一个错误(如预期)

 incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String 

有人可以解释为什么这种行为被认为是有效的? 它将如何有用?

客户端不知道这个调用是不安全的 – 客户端的代码编译没有警告。 为什么编译不会提出这个问题呢?

另外,它与这个例子有什么不同:

 <X extends CharSequence> void doCharSequence(List<X> l) { } List<CharSequence> chsL = new ArrayList<>(); doCharSequence(chsL); // compiles List<Integer> intL = new ArrayList<>(); doCharSequence(intL); // error 

试图通过List<Integer>给出一个错误,如预期的那样:

 method doCharSequence in class generic.GenericTest cannot be applied to given types; required: java.util.List<X> found: java.util.List<java.lang.Integer> reason: inference variable X has incompatible bounds equality constraints: java.lang.Integer upper bounds: java.lang.CharSequence 

如果这被报告为错误,为什么Integer x = getCharSequence(); 是不是?

CharSequence是一个interface 。 因此,即使SomeClass没有实现CharSequence ,创build一个类也是完全可能的

 class SubClass extends SomeClass implements CharSequence 

所以你可以写

 SomeClass c = getCharSequence(); 

因为推断的typesXSomeClass & CharSequence的交集types。

Integer情况下,这有点奇怪,因为Integer是最终的,但final在这些规则中不起作用。 例如,你可以写

 <T extends Integer & CharSequence> 

另一方面, String不是一个interface ,所以不能扩展SomeClass来获取String的子types,因为java不支持类的多重inheritance。

对于List示例,您需要记住generics既不是协变也不是逆变。 这意味着如果XY的子types,那么List<X>既不是List<Y>的子types也不是超types。 由于Integer没有实现CharSequence ,所以你不能在你的doCharSequence方法中使用List<Integer>

你可以,但是得到这个编译

 <T extends Integer & CharSequence> void foo(List<T> list) { doCharSequence(list); } 

如果你有一个方法返回一个List<T>像这样:

 static <T extends CharSequence> List<T> foo() 

你可以做

 List<? extends Integer> list = foo(); 

同样,这是因为推断的types是Integer & CharSequence ,这是Integer的子types。

当指定多个边界时(例如, <T extends SomeClass & CharSequence> ),相交types隐式地发生。

有关更多信息, 这里是JLS的一部分,它解释了types边界是如何工作的。 你可以包含多个接口,例如

 <T extends String & CharSequence & List & Comparator> 

但只有第一个界限可能是一个非界面。

编译器在分配X之前所推断的types是Integer & CharSequence 。 这种types感觉很奇怪,因为Integer是最终的,但它是Java中完全有效的types。 然后投给Integer ,完全没问题。

Integer & CharSequencetypes只有一个可能的值: null 。 通过以下实施:

 <X extends CharSequence> X getCharSequence() { return null; } 

以下任务将起作用:

 Integer x = getCharSequence(); 

由于这个可能的价值,没有理由为什么这个任务应该是错误的,即使它显然是无用的。 警告将是有用的。

真正的问题是API,而不是调用网站

事实上,我最近在这个APIdevise反模式上发表了博客。 您应该(几乎)从不devisegenerics方法来返回任意types,因为您几乎不能保证推断types将被传递。 像Collections.emptyList()这样的方法是一个例外,在这种情况下,清单(和genericstypes擦除)的空白是为什么<T>任何推断都可以工作的原因:

 public static final <T> List<T> emptyList() { return (List<T>) EMPTY_LIST; }