通用返回类型上界 - 接口与类 - 出人意料的有效代码 [英] Generic return type upper bound - interface vs. class - surprisingly valid code

查看:175
本文介绍了通用返回类型上界 - 接口与类 - 出人意料的有效代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个来自第三方库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>是不是?

解决方案

CharSequence 是接口。因此即使 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆