编译器如何在泛型方法上推断类型?
How is compiler inferring type on a generic method?
我有一个Storage
class:
class Storage<E> {
void add(E e) {
// add element
};
void addAll(Iterable<? extends E> src) {
for (E e : src)
add(e);
}
}
有两个 classes 其中 class Child
extends
Parent
:
Parent
class Parent implements Comparable<Parent> {
@Override
public int compareTo(Parent o) {
return 0; // some comparison logic
}
}
Child
class Child extends Parent {
}
Driver class:
import java.util.Arrays;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
/******** CASE 1 *********/
Storage<Parent> ds = new Storage<Parent>();
ds.add(new Parent());
ds.addAll(Arrays.asList(new Parent()));
// Type params are invariant.
// But List<Child> is possible due to bounded wildcard type on addAll
ds.addAll(Arrays.asList(new Child())); // Makes sense
/******** CASE 2 *********/
List<Child> t = Arrays.asList();
max(t);
}
static <T extends Comparable<T>> T max(List<T> list) {
return null; // Return null so code can compile
}
}
因为Storage
是泛型class,对其方法的操作是有意义的;我知道情况 1 是如何工作的。
在情况 2 中,在 GenericTest
中使用上述 max
的签名,我得到编译错误:
The method max(List<T>) in the type GenericTest is not applicable for the arguments (List<Child>)
我知道 Comparable<Child>
不是 Comparable<Parent>
的 sub-type(类型化参数是不变的)。
所以,我将签名更新为
// update Comparable<T> to Comparable<? super T>
static <T extends Comparable<? super T>> T max(List<T> list)
现在,代码编译并推断签名为
<Child> Child GenericTest.max(List<Child> list)
这是有道理的。
当我将签名更新为
// update List<T> to List<? extends T>
static <T extends Comparable<T>> T max(List<? extends T> list)
代码编译并推断签名为
<Parent> Parent GenericTest.max(List<? extends Parent> list)
我无法理解将 List<T>
更新为 List<? extends T>
是如何让编译器推断类型 Parent
的。
我的意思是,对于泛型方法(使用 Child
类型直接调用),? extends T
如何帮助编译器引用 parent class Child
?请注意,此签名具有 Comparable<T>
(而不是 Comparable<? super T
)。
extends
不是关于 T
本身及其 sub-classes 的类型吗?
编辑:
Java 8 带有更新的 type-inference 规则。
以下对于 Java 8 及更高版本中的 type-inference 已足够,但不适用于 Java 7 及以下版本:
static <T extends Comparable<T>> T max(List<? extends T> list)
它给出了编译错误:
Bound mismatch: The generic method max(List<? extends T>) of type GenericTest is not applicable for the arguments (List<Child>). The inferred type Child is not a valid substitute for the bounded parameter <T extends Comparable<T>>
签名
static <T extends Comparable<? super T>> void max(List<T> list)
不过 足够 Java 7 了。
为了让事情更清楚,class Child
:
extends Parent
implements Comparable<Parent>
1) static <T extends Comparable<T>> T max(List<T> list)
T
是 Child
- 失败,因为
Child
不是 Comparable<Child>
2) static <T extends Comparable<? super T>> T max(List<T> list)
T
是 Child
?
是 Parent
- 之所以有效,是因为
Child implements Comparable<Parent super Child>
3) static <T extends Comparable<T>> T max(List<? extends T> list)
T
是 Parent
?
是 Child
- 之所以有效,是因为
Child extends Parent implements Comparable<Parent>
这里的情况3),找到一个有效的T
class可以看做:”找到Child
的第一个superclass实现了自身的 Comparable
".
与情况 1) 一样,它不能是 Child
,因为它不是 Comparable<Child>
。
Child
的第一个(也是唯一一个)实现自身 Comparable
的超级 class 是 Parent
.
I couldn't understand how updating List<T>
to List<? extends T>
made the compiler infer the type Parent
.
List<T>
强制 T
为 Child
List<? extends T>
强制 ?
为 Child
,而不是 T
对我来说,JDK 8 和 JDK 7 都失败了,所以我没有看到你在这些 JDks
之间描述的不一致
JDK 8 编译
JDK 7 编译
至于失败的原因叫做PECS(Producer Extends Consumer Super)
following answer 对我的理解帮助很大。
If you say <? extends SomeType>, then you wanna describe a ‘box’ that
is the same size or smaller than the ‘SomeType’ box.
考虑到上述情况,当您将 max 方法描述为
static <T extends Comparable<T>> T max(List<? extends T> list)
根据您的声明 List<? extends T> list
,您需要一个与 T 相同或小于 T 的容器。您使用它时可以是 Child。
List<Child> t = Arrays.asList();
所以这应该已经编译了。
然而 return 类型是 <T extends Comparable<T>> T
然后被翻译成 Child extends Comparable<Child>
.
Java 但是有一个限制,阻止您使用不同的类型参数实现相同的通用接口(在您的情况下是可比较的)。
这就是错误显示
的原因
required: java.util.List<? extends T>
found: java.util.List<Child> reason: inferred type does not
conform to declared bound(s)
inferred: Child
bound(s): java.lang.Comparable<Child>
解决方案
您应该使用 <T extends Comparable<? super T>>
声明 return 类型
Contra-variance: ? super T ( the family of all types that are
supertypes of T) - a wildcard with a lower bound. T is the lower-most
class in the inheritance hierarchy.
from a previous answer on PECS
在那种情况下,它将编译为将绑定设置为 Comparable<Parent>
。
我有一个Storage
class:
class Storage<E> {
void add(E e) {
// add element
};
void addAll(Iterable<? extends E> src) {
for (E e : src)
add(e);
}
}
有两个 classes 其中 class Child
extends
Parent
:
Parent
class Parent implements Comparable<Parent> {
@Override
public int compareTo(Parent o) {
return 0; // some comparison logic
}
}
Child
class Child extends Parent {
}
Driver class:
import java.util.Arrays;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
/******** CASE 1 *********/
Storage<Parent> ds = new Storage<Parent>();
ds.add(new Parent());
ds.addAll(Arrays.asList(new Parent()));
// Type params are invariant.
// But List<Child> is possible due to bounded wildcard type on addAll
ds.addAll(Arrays.asList(new Child())); // Makes sense
/******** CASE 2 *********/
List<Child> t = Arrays.asList();
max(t);
}
static <T extends Comparable<T>> T max(List<T> list) {
return null; // Return null so code can compile
}
}
因为Storage
是泛型class,对其方法的操作是有意义的;我知道情况 1 是如何工作的。
在情况 2 中,在 GenericTest
中使用上述 max
的签名,我得到编译错误:
The method max(List<T>) in the type GenericTest is not applicable for the arguments (List<Child>)
我知道 Comparable<Child>
不是 Comparable<Parent>
的 sub-type(类型化参数是不变的)。
所以,我将签名更新为
// update Comparable<T> to Comparable<? super T>
static <T extends Comparable<? super T>> T max(List<T> list)
现在,代码编译并推断签名为
<Child> Child GenericTest.max(List<Child> list)
这是有道理的。
当我将签名更新为
// update List<T> to List<? extends T>
static <T extends Comparable<T>> T max(List<? extends T> list)
代码编译并推断签名为
<Parent> Parent GenericTest.max(List<? extends Parent> list)
我无法理解将 List<T>
更新为 List<? extends T>
是如何让编译器推断类型 Parent
的。
我的意思是,对于泛型方法(使用 Child
类型直接调用),? extends T
如何帮助编译器引用 parent class Child
?请注意,此签名具有 Comparable<T>
(而不是 Comparable<? super T
)。
extends
不是关于 T
本身及其 sub-classes 的类型吗?
编辑:
Java 8 带有更新的 type-inference 规则。
以下对于 Java 8 及更高版本中的 type-inference 已足够,但不适用于 Java 7 及以下版本:
static <T extends Comparable<T>> T max(List<? extends T> list)
它给出了编译错误:
Bound mismatch: The generic method max(List<? extends T>) of type GenericTest is not applicable for the arguments (List<Child>). The inferred type Child is not a valid substitute for the bounded parameter <T extends Comparable<T>>
签名
static <T extends Comparable<? super T>> void max(List<T> list)
不过 足够 Java 7 了。
为了让事情更清楚,class Child
:
extends Parent
implements Comparable<Parent>
1) static <T extends Comparable<T>> T max(List<T> list)
T
是Child
- 失败,因为
Child
不是Comparable<Child>
2) static <T extends Comparable<? super T>> T max(List<T> list)
T
是Child
?
是Parent
- 之所以有效,是因为
Child implements Comparable<Parent super Child>
3) static <T extends Comparable<T>> T max(List<? extends T> list)
T
是Parent
?
是Child
- 之所以有效,是因为
Child extends Parent implements Comparable<Parent>
这里的情况3),找到一个有效的T
class可以看做:”找到Child
的第一个superclass实现了自身的 Comparable
".
与情况 1) 一样,它不能是 Child
,因为它不是 Comparable<Child>
。
Child
的第一个(也是唯一一个)实现自身 Comparable
的超级 class 是 Parent
.
I couldn't understand how updating
List<T>
toList<? extends T>
made the compiler infer the typeParent
.
List<T>
强制T
为Child
List<? extends T>
强制?
为Child
,而不是T
对我来说,JDK 8 和 JDK 7 都失败了,所以我没有看到你在这些 JDks
之间描述的不一致JDK 8 编译
JDK 7 编译
至于失败的原因叫做PECS(Producer Extends Consumer Super)
following answer 对我的理解帮助很大。
If you say <? extends SomeType>, then you wanna describe a ‘box’ that is the same size or smaller than the ‘SomeType’ box.
考虑到上述情况,当您将 max 方法描述为
static <T extends Comparable<T>> T max(List<? extends T> list)
根据您的声明 List<? extends T> list
,您需要一个与 T 相同或小于 T 的容器。您使用它时可以是 Child。
List<Child> t = Arrays.asList();
所以这应该已经编译了。
然而 return 类型是 <T extends Comparable<T>> T
然后被翻译成 Child extends Comparable<Child>
.
Java 但是有一个限制,阻止您使用不同的类型参数实现相同的通用接口(在您的情况下是可比较的)。
这就是错误显示
的原因required: java.util.List<? extends T>
found: java.util.List<Child> reason: inferred type does not
conform to declared bound(s)
inferred: Child
bound(s): java.lang.Comparable<Child>
解决方案
您应该使用 <T extends Comparable<? super T>>
Contra-variance: ? super T ( the family of all types that are supertypes of T) - a wildcard with a lower bound. T is the lower-most class in the inheritance hierarchy. from a previous answer on PECS
在那种情况下,它将编译为将绑定设置为 Comparable<Parent>
。