我对Java泛型如何处理继承/多态性感到困惑.
假设以下层次结构 -
动物(父母)
狗 - 猫(儿童)
所以假设我有一个方法doSomething(List<Animal> animals).根据所有继承和多态的规则,我会假设a List<Dog> 是 a List<Animal>而a List<Cat> 是 a List<Animal>- 所以任何一个都可以传递给这个方法.不是这样.如果我想实现这种行为,我必须明确告诉该方法接受一个Animal的任何子类的列表doSomething(List<? extends Animal> animals).
我知道这是Java的行为.我的问题是为什么?为什么多态通常是隐含的,但是当涉及泛型时必须指定它?
让我们首先考虑一个简单的场景(请参阅ideone.com上的完整源代码):
import java.util.*;
public class TwoListsOfUnknowns {
static void doNothing(List<?> list1, List<?> list2) { }
public static void main(String[] args) {
List<String> list1 = null;
List<Integer> list2 = null;
doNothing(list1, list2); // compiles fine!
}
}
Run Code Online (Sandbox Code Playgroud)
这两个通配符是不相关的,这就是为什么你可以doNothing用a List<String>和a 调用List<Integer>.换句话说,两者?可以指代完全不同的类型.因此,以下不编译,这是预期的(也在ideone.com上):
import java.util.*;
public class TwoListsOfUnknowns2 {
static void doSomethingIllegal(List<?> list1, List<?> list2) {
list1.addAll(list2); // DOES NOT COMPILE!!!
// The method addAll(Collection<? extends capture#1-of ?>)
// in the type List<capture#1-of …Run Code Online (Sandbox Code Playgroud) 为什么java中我们做不到:
List<List<? extends Number>> aList = new ArrayList<List<Number>>();
Run Code Online (Sandbox Code Playgroud)
即使这样也可以:
List<? extends Number> aList = new ArrayList<Number>();
Run Code Online (Sandbox Code Playgroud)
编译器错误消息是:
Type mismatch: cannot convert from ArrayList<List<Number>> to List<List<? extends Number>>
我得到以下编译消息:
[javac] ... error: incompatible types
[javac] exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
[javac] ^
[javac] required: Holder<Class<? extends Exception>>
[javac] found: Holder<Class<CAP#1>>
[javac] where CAP#1 is a fresh type-variable:
[javac] CAP#1 extends Exception from capture of ? extends Exception
[javac] 1 error
Run Code Online (Sandbox Code Playgroud)
在我看来,根据信息所有应该是正确的.CAP#1确实扩展了Exception.那么如何理解上述信息呢?下面的SSCCE(最初没有发布,因为我希望在一般情况下理解错误消息本身):
class Holder<T> {
public T t;
public Holder(T t) {
this.t = t;
}
}
public class FooMain {
public static void main(String args[]) throws Exception {
Holder<Class<? extends Exception>> exceptionClassHolder;
exceptionClassHolder = …Run Code Online (Sandbox Code Playgroud) 我正在使用来自第三方库(Reflection)的方法,该方法应该找到给定类型的子类型并且看起来像
public <T> Set<Class<? extends T>> getSubTypesOf(final Class<T> type) {
...
Run Code Online (Sandbox Code Playgroud)
当调用者代码看起来像
Class<?> type = ...
Set<Class<?>> subTypes = reflections.getSubTypesOf(type);
Run Code Online (Sandbox Code Playgroud)
我收到编译错误:" cannot convert from Set<Class<? extends capture#19-of ?>> to Set<Class<?>>".以下修复了这种情况:
Class<?> type = ...
Set<?> subTypes = reflections.getSubTypesOf(ht);
Run Code Online (Sandbox Code Playgroud)
所以看起来不正确的唯一可能的补救措施Set<Class<? extends ?>>是Set<?>,但不是Set<Class<?>>.为什么会这样?谢谢你对此有任何解释.
我想知道这段代码有什么问题:
Map <? extends String, ? extends Integer> m = null;
Set<Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
Run Code Online (Sandbox Code Playgroud)
编译器抱怨错误消息:
类型不匹配:无法转换
Set<Map.Entry<capture#1-of ? extends String,capture#2-of ? extends Integer>>为Set<Map.Entry<? extends String,? extends Integer>>
应该是什么类型的s?Eclipse建议,Set<?>但我想尝试更具体.
继这个问题之后,它提供了一个解决方案,但没有解释它(不幸的是,答案中的链接现在已经死了):
采取以下方法:
void method(Map<?, ?> myMap) {
Set<Map.Entry<?, ?>> set = myMap.entrySet();
...
}
Run Code Online (Sandbox Code Playgroud)
简单,不是吗?但是,这无法在jdk1.7.0_25上编译:
Run Code Online (Sandbox Code Playgroud)incompatible types required: java.util.Set<java.util.Map.Entry<?,?>> found: java.util.Set<java.util.Map.Entry<capture#1 of ?,capture#2 of ?>>
WTF?Map.entrySet()被指定为返回一个类型的对象Set<Map.Entry<K, V>>,所以在上面的例子中,myMap.entrySet()返回一个Set<Map.Entry<?, ?>>.但它不编译!
甚至更奇怪,从顶部的链接问题,将方法更改为this使其编译:
void method(Map<?, ?> myMap) {
Set<? extends Map.Entry<?, ?>> set = myMap.entrySet();
...
}
Run Code Online (Sandbox Code Playgroud)
跆拳道??? 呼叫entrySet上的Map<?, ?>回报Set<Map.Entry<K, V>>,这是不能被分配给类型的变量Set<Map.Entry<K, V>>,但它可以以类型的变量Set<? extends Map.Entry<K, V>>?????
任何人都可以了解这里发生的事情吗?这是否意味着,每当我使用至少2级深度的通配符类型编写方法时,我必须记得将它? extends ...放在某处?