通用返回类型上界 - 接口与类 - 出人意料的有效代码 [英] Generic return type upper bound - interface vs. class - surprisingly valid code
问题描述
这是一个来自第三方库API的真实示例,但简化了。
$ b
用Oracle JDK 8u72 考虑以下两种方法: 两者均报告未经检查的投射警告 - 我明白了原因。让我感到困惑的是为什么我可以调用 并编译?编译器应该知道 给出错误(与预期的一样)
< / p> X扩展CharSequence> X getCharSequence(){
return(X)hello;
}
< X extends String> X getString(){
return(X)hello;
}
$ p code整数x = getCharSequence();
Integer
没有实现 CharSequence
。调用
Integer y = getString();
不兼容的类型:推理变量X具有不兼容的上限java.lang.Integer,java.lang.String
有人可以解释为什么这种行为被认为是有效的吗?它会如何有用?
客户端不知道这个调用是不安全的 - 客户端的代码在没有警告的情况下编译。为什么编译不会提出这样的警告?/ b>
另外,它与这个例子有什么不同:
< X扩展CharSequence> void doCharSequence(List< X> 1){
}
List< CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); //编译
List< Integer> intL = new ArrayList<>();
doCharSequence(intL); //错误
尝试传递 List< Integer>
提供了一个错误,正如预期的那样:
方法doCharSequence在类generic.GenericTest中不能应用给定类型;
$如果这被报告为错误,为什么
required:java.util.List< X>
found:java.util.List< java.lang.Integer>
reason:推理变量X具有不兼容的边界
等式约束:java.lang.Integer
上界:java.lang.CharSequence
Integer x = getCharSequence(); $ c $>
c>是不是?
接口
。因此即使 SomeClass
没有实现 CharSequence
,创建一个类也是完全可能的。
class SubClass extends SomeClass implements CharSequence
<因此你可以写成:
SomeClass c = getCharSequence();
是因为推断的类型 X
键入 SomeClass& CharSequence
。
在整数
的情况下,有点奇怪,因为 Integer
是最终的,但是 final
在这些规则中不起任何作用。例如,你可以写成:
< T extends Integer&的CharSequence>
另一方面, String
不是一个接口
,所以无法扩展 SomeClass
来获取的子类型String
,因为java不支持多类继承。
使用 List
示例,您需要记住泛型既不是协变也不是逆变。这意味着如果 X
是 Y
的子类型, List< X>
既不是子类型,也不是 List< Y>
的超类型。由于 Integer
没有实现 CharSequence
,所以不能使用 List< Integer>
在你的 doCharSequence
方法中。
然而,你可以编译它。
< T extends Integer&的CharSequence> void foo(List< T> list){
doCharSequence(list);
$ b $ p
$ b 如果你有一个方法返回一个 List< T>
像这样:
static< T extends CharSequence>列表与LT; T> foo()
你可以做
pre> 列表与LT ;?扩展Integer> list = foo();
同样,这是因为推断的类型是 Integer& CharSequence
,这是整型
的子类型。
交点类型隐式发生指定多个边界(例如,< T extends SomeClass& CharSequence>
)。 p
href =https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-TypeBound>这里是JLS的部分,它解释了类型边界工作。您可以包含多个接口,例如
< T extends String& CharSequence&列表&比较>
但只有第一个界限可能是非界面。
This is a real-world example from a 3rd party library API, but simplified.
Compiled with Oracle JDK 8u72
Consider these two methods:
<X extends CharSequence> X getCharSequence() {
return (X) "hello";
}
<X extends String> X getString() {
return (X) "hello";
}
Both report an "unchecked cast" warning - I get why. The thing that baffles me is why can I call
Integer x = getCharSequence();
and it compiles? The compiler should know that Integer
does not implement CharSequence
. The call to
Integer y = getString();
gives an error (as expected)
incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String
Can someone explain why would this behaviour be considered valid? How would it be useful?
The client does not know that this call is unsafe - the client's code compiles without warning. Why wouldn't the compile warn about that / issue an error?
Also, how is it different from this example:
<X extends CharSequence> void doCharSequence(List<X> l) {
}
List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles
List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error
Trying to pass List<Integer>
gives an error, as expected:
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
If that is reported as an error, why Integer x = getCharSequence();
isn't?
CharSequence
is an interface
. Therefore even if SomeClass
does not implement CharSequence
it would be perfectly possible to create a class
class SubClass extends SomeClass implements CharSequence
Therefore you can write
SomeClass c = getCharSequence();
because the inferred type X
is the intersection type SomeClass & CharSequence
.
This is a bit odd in the case of Integer
because Integer
is final, but final
doesn't play any role in these rules. For example you can write
<T extends Integer & CharSequence>
On the other hand, String
is not an interface
, so it would be impossible to extend SomeClass
to get a subtype of String
, because java does not support multiple-inheritance for classes.
With the List
example, you need to remember that generics are neither covariant nor contravariant. This means that if X
is a subtype of Y
, List<X>
is neither a subtype nor a supertype of List<Y>
. Since Integer
does not implement CharSequence
, you cannot use List<Integer>
in your doCharSequence
method.
You can, however get this to compile
<T extends Integer & CharSequence> void foo(List<T> list) {
doCharSequence(list);
}
If you have a method that returns a List<T>
like this:
static <T extends CharSequence> List<T> foo()
you can do
List<? extends Integer> list = foo();
Again, this is because the inferred type is Integer & CharSequence
and this is a subtype of Integer
.
Intersection types occur implicitly when you specify multiple bounds (e.g. <T extends SomeClass & CharSequence>
).
For further information, here is the part of the JLS where it explains how type bounds work. You can include multiple interfaces, e.g.
<T extends String & CharSequence & List & Comparator>
but only the first bound may be a non-interface.
这篇关于通用返回类型上界 - 接口与类 - 出人意料的有效代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!