Java 分配给多级通配符
简单类:Java 分配给多级通配符,java,generics,bounded-wildcard,Java,Generics,Bounded Wildcard,简单类: class Pair<K,V> { } 类对{ } 还有一些作业: Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>(); Collection<Pair<String,Long>> c2 = c1; // ok Collection<Pair<String,?>> c3 = c1; //
class Pair<K,V> {
}
类对{
}
还有一些作业:
Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<Pair<String,Long>> c2 = c1; // ok
Collection<Pair<String,?>> c3 = c1; // this does not compile
Collection<? extends Pair<String,?>> c4 = c1; // ok
Collection c1=新的ArrayList();
集合c2=c1;//好啊
集合c3=c1;//这是不可编译的
集合>c4=c1;//好啊
为什么第三个项目没有编译,而第四个项目完全合法
编译器错误:
Type mismatch: cannot convert from Collection<Pair<String,Long>> to Collection<Pair<String,?>>
类型不匹配:无法从集合转换为集合
Java泛型的优点之一是在编译时进行更强的类型检查。因此,泛型中声明的任何内容都应该完全匹配。为了让它成为一个简单的答案,我将使用一些例子
要初始化数字列表,您可以这样做
List<Number> numbers = new ArrayList<Number>();
通配符也是一个例外。通配符用于接受任何参数。因此,以下陈述是有效的
List<?> numbers = new ArrayList<Number>();
Map<?, ?> unKnownMap = new HashMap<String, String>();
// OR
List<Integer> intNumbers = new ArrayList<Integer>();
List<?> numbers = intNumbers;
此外,通配符推理仅在一个级别上发生。任何嵌套都应该与通常的泛型完全匹配。所以
List<List<?>> list = new ArrayList<List<String>>(); // Compile error because nested List<String> doesn not exactly match List<?>
List<List<?>> list = new ArrayList<List<?>>(); // OK - Valid
List<List<Integer>> intList = new ArrayList<List<Integer>>();
List<List<?>> numbers = intList; // Compile error because nested List<Integer> doesn not exactly match List<?>
列表
列表>();//好-有效
List intList=new ArrayList();
列表
这就是你的情况
Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<Pair<String,Long>> c2 = c1; // ok
Collection<Pair<String,?>> c3 = c1; // Compile error because nested Pair<String,?> doesn not exactly match Pair<String,Long>
Collection c1=新的ArrayList();
集合c2=c1;//好啊
集合c3=c1;//编译错误,因为嵌套对不完全匹配对
所以你可以做一些像
Collection<Pair<String,?>> c3 = new ArrayList<Pair<String,?>>(c2);
//OR
Collection<Pair<String,?>> c3 = new ArrayList<Pair<String,?>>();
c3.addAll(c2);
Collection c3=新的数组列表(c2);
//或
集合c3=新的ArrayList();
c3.addAll(c2);
案例4:当你说列表>
时,它又是第一级。这意味着它将检查所讨论的元素是否扩展了列表
List>List=new ArrayList();//工作原理与列表l=新ArrayList()类似;
列表>>编号=新的ArrayList();//编译错误-嵌套级别类似于List lst=new ArrayList();
所以
Collection c3=new ArrayList()代码>类似于对p=新对()代码>我将尝试使用两个简单规则解释Java泛型。这些规则足以回答您的问题,并且基本上足以记住几乎任何情况:
除非A=B
,否则两种通用类型X
和X
永远不可分配。也就是说,泛型在默认情况下是不变的
通配符允许分配X
:
- 到
X
- 到
X>
。也就是说,在您的情况下,A=Pair
和B=Pair
。由于这些类型不相等,因此它们是不可分配的;他们违反了规则1
问题是,为什么通配符没有帮助?答案很简单:
规则2是不可传递的。也就是说,X
不能分配给X
可分配给B=?扩展对
(同样是因为规则2)。因此,这是合法的
一般方法
下面是检查任何复杂泛型类型的方法:只需使用两个规则逐级检查每个泛型类型。从最外层开始。一旦某个级别违反了规则,您就知道分配是非法的;如果所有级别都遵守规则,则该分配是合法的。让我们再次考虑你的类型:
X = Collection<Pair<String,Long>>
Y = Collection<Pair<String,?>>
Z = Collection<? extends Pair<String,?>>
X=Collection
Y=收集
Z=收集>
X可分配给Y吗
// Outermost level:
A = Pair<String,Long>, B = Pair<String,?>
=> B is no wildcard and A != B (Rule 1), so this is illegal!
//最外层:
A=对,B=对
=>B不是通配符,A!=B(规则1),所以这是非法的!
X可分配给Z吗
// Outermost level:
A = Pair<String,Long>, B = ? extends Pair<String,?>
=> We got a wildcard, so Rule 2 states this is legal if the inner level is legal
// Inner level: (we have to check both parameters)
A = String, B = String => Equal, Rule 1 applies, fine!
A = Long, B = ? => B is wildcard, Rule 2 applies, fine!
//最外层:
A=配对,B=?延伸对
=>我们得到了一个通配符,因此规则2规定,如果内部级别是合法的,则这是合法的
//内部级别:(我们必须检查两个参数)
A=String,B=String=>Equal,规则1适用,很好!
A=长,B=?=>B是通配符,规则2适用,很好!
记住的简单规则
每个级别的泛型嵌套要么需要完全相同(A=B
),要么B
需要在此级别中包含通配符。首先,让我们通过删除额外的类型参数来简化代码:
Collection<List<Long>> c1 = new ArrayList<List<Long>>();
Collection<List<Long>> c2 = c1; // ok
Collection<List<?>> c3 = c1; // this does not compile
Collection<? extends List<?>> c4 = c1; // ok
现在,它c3=c1
是允许的,您可以看到c3.add(new ArrayList())
将破坏c1
的类型安全性:
Collection<List<Long>> c1 = new ArrayList<List<Long>>();
Collection<List<?>> c3 = c1;
c3.add(Arrays.asList("foo"));
for (List<Long> l: c1) {
for (Long value: l) {
// Oops, value is not a Long!
}
}
Collection c1=新的ArrayList();
Collection这里要记住的关键是嵌套的通配符不会捕获
这意味着顶级通配符(即通配符代表一个特定的东西)的“正常”行为不适用于嵌套通配符。相反,嵌套通配符代表任何类型
例如,以以下声明为例:
List<?> l;
这不是一个特定类型的列表的集合。这是列表的集合
,每个列表都是一种特定类型
例如,您预期会发生类似的情况:
Collection<List<?>> c = new ArrayList<List<Long>>(); // Not valid, but pretend it is
c.add(new ArrayList<Long>()); // Valid
c.add(new ArrayList<Integer>()); // Invalid, because c is a Collection of Lists of Long
该返回哪种类型iterator()
返回一个iterator
,而next()
返回E
,这意味着c.iterator().next()
返回一个列表
。这不是您期望的列表。为什么呢
因为嵌套通配符不会捕获。这就是这里的关键区别。列表
中的通配符不捕获单一类型的“总体”。它为集合中的每个元素捕获一种类型
因此,这是完全有效的代码:
Collection<List<?>> odd = new ArrayList<List<?>>();
odd.add(new ArrayList<String>());
odd.add(new ArrayList<Long>());
List<?> l = odd.iterator().next();
// returns the ArrayList<String>, but because odd is parameterized with
// List<?> we can technically end up with a list of anything
现在,让我们回顾一下。集合
不是由对
s的集合
和单个未知类型组成的集合
。它是一个集合
,由对
s组成,每个对都是一对字符串
和一些未知类型,可能与集合中的另一对类型相同,也可能不同。所以这是有效的:
// Assume an appropriate object was assigned to c3
Pair<String, ?> p1 = new Pair<String, String>("Hello", "World");
Pair<String, ?> p2 = new Pair<String, List<String>>("Lorem", new ArrayList<>());
Pair<String, ?> p3 = new Pair<String, Map<String, Integer>>("Ispum", new HashMap<>());
c3.add(p1);
c3.add(p2);
c3.add(p3);
现在,这有点棘手。顶层捕获一个扩展对的特定类型。因为通配符是特定类型的超类型(例如,列表
是列表
的超类型),所以可以捕获对
?扩展对
,因为前者扩展了后者。因此,因为对
的赋值与兼容吗?扩展对
,即assig
// The following line compiles,
// because `ArrayList<String>` is a `List` you can get `Object`s from
c3.add(new ArrayList<String>());
// The following line does not compile,
// because type of c4 doesn't allow you to put anything into it
c4.add(new ArrayList<String>());
Collection<List<Long>> c1 = new ArrayList<List<Long>>();
Collection<List<?>> c3 = c1;
c3.add(Arrays.asList("foo"));
for (List<Long> l: c1) {
for (Long value: l) {
// Oops, value is not a Long!
}
}
List<?> l;
Collection<List<?>> c;
Collection<List<?>> c = new ArrayList<List<Long>>(); // Not valid, but pretend it is
c.add(new ArrayList<Long>()); // Valid
c.add(new ArrayList<Integer>()); // Invalid, because c is a Collection of Lists of Long
List<?> l = new ArrayList<String>();
c.add(l); // Should this compile?
c.iterator().next(); // Assume there is an element to return
Collection<List<?>> odd = new ArrayList<List<?>>();
odd.add(new ArrayList<String>());
odd.add(new ArrayList<Long>());
List<?> l = odd.iterator().next();
// returns the ArrayList<String>, but because odd is parameterized with
// List<?> we can technically end up with a list of anything
Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<Pair<String,Long>> c2 = c1;
Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<Pair<String,?>> c3 = c1;
// Assume an appropriate object was assigned to c3
Pair<String, ?> p1 = new Pair<String, String>("Hello", "World");
Pair<String, ?> p2 = new Pair<String, List<String>>("Lorem", new ArrayList<>());
Pair<String, ?> p3 = new Pair<String, Map<String, Integer>>("Ispum", new HashMap<>());
c3.add(p1);
c3.add(p2);
c3.add(p3);
Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<? extends Pair<String,?>> c4 = c1;