<? 之间有什么区别?扩展 Base> 和 <T extends Base>?
What is the difference between <? extends Base> and <T extends Base>?
在这个例子中:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile()
编译失败:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
而 compiles()
被编译器接受。
This answer 解释说,唯一的区别是与 <? ...>
不同,<T ...>
允许您稍后引用类型,但似乎并非如此。
在这种情况下,<? extends Number>
和 <T extends Number>
有什么区别,为什么第一个编译不通过?
通话中:
compiles(new HashMap<Integer, List<Integer>>());
T 与 Integer 相匹配,因此参数的类型是 Map<Integer,List<Integer>>
。方法 doesntCompile
不是这种情况:无论调用中的实际参数是什么,参数类型都保持 Map<Integer, List<? extends Number>>
;这不能从 HashMap<Integer, List<Integer>>
.
分配
更新
在 doesntCompile
方法中,没有什么能阻止你做这样的事情:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
map.put(1, new ArrayList<Double>());
}
很明显,它不能接受 HashMap<Integer, List<Integer>>
作为参数。
通过定义具有以下签名的方法:
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
并像这样调用它:
compiles(new HashMap<Integer, List<Integer>>());
您正在将 T
与您提供的类型相匹配。
在 jls §8.1.2 中,我们发现(有趣的部分由我加粗):
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments. All of these parameterized types share the same class at run time.
换句话说,类型 T
与输入类型匹配并分配 Integer
。签名将有效地变为 static void compiles(Map<Integer, List<Integer>> map)
.
关于doesntCompile
方法,jls定义了子类型规则(§4.5.1,由我加粗):
A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):
? extends T <= ? extends S if T <: S
? extends T <= ?
? super T <= ? super S if S <: T
? super T <= ?
? super T <= ? extends Object
T <= T
T <= ? extends T
T <= ? super T
这意味着 ? extends Number
确实包含 Integer
甚至 List<? extends Number>
包含 List<Integer>
,但 Map<Integer, List<? extends Number>>
和 [=23 并非如此=].有关该主题的更多信息,请参见 in this SO thread。您仍然可以通过声明使带有 ?
通配符的版本工作,您期望 List<? extends Number>
:
的子类型
public class Example {
// now it compiles
static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
public static void main(String[] args) {
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
我建议你看看 documentation of generic wildcards especially guidelines for wildcard use
坦率地说你的方法#doesntCompile
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
并像
一样打电话
doesntCompile(new HashMap<Integer, List<Integer>>());
根本不正确
让我们添加合法实施:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
List<Double> list = new ArrayList<>();
list.add(0.);
map.put(0, list);
}
真的很好,因为 Double 扩展了 Number,所以 List<Double>
和 List<Integer>
一样绝对没问题,对吧?
但是,根据你的示例,你仍然认为合法传递到这里new HashMap<Integer, List<Integer>>()
吗?
编译器不这么认为,并且正在尽力避免这种情况。
尝试使用方法#compile 执行相同的实现,编译器显然不允许您将双精度列表放入映射中。
static <T extends Number> void compiles(Map<Integer, List<T>> map) {
List<Double> list = new ArrayList<>();
list.add(10.);
map.put(10, list); // does not compile
}
基本上除了 List<T>
什么都不能放,这就是为什么用 new HashMap<Integer, List<Integer>>()
或 [=19= 调用该方法是安全的原因]或new HashMap<Integer, List<Long>>()
或new HashMap<Integer, List<Number>>()
.
所以简而言之,您正在尝试使用编译器进行作弊,它可以很好地抵御这种作弊。
注意:Maurice Perry 编辑 post 的回答绝对正确。我只是不确定它是否足够清楚,所以尝试(真的希望我设法)添加更广泛的 post.
演示的简单示例。同样的例子可以像下面这样形象化。
static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too
public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}
List<Pair<? extends Number>>
是多级通配符类型,而 List<? extends Number>
是标准通配符类型。
通配符类型 List<? extends Number>
的有效具体实例包括 Number
和 Number
的任何子类型,而 List<Pair<? extends Number>>
是类型参数的类型参数并且本身具有泛型类型的具体实例化。
泛型是不变的,所以 Pair<? extends Number>
通配符类型只能接受 Pair<? extends Number>>
。内部类型 ? extends Number
已经是协变的。您必须将封闭类型设为协变以允许协变。
在这个例子中:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile()
编译失败:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
而 compiles()
被编译器接受。
This answer 解释说,唯一的区别是与 <? ...>
不同,<T ...>
允许您稍后引用类型,但似乎并非如此。
在这种情况下,<? extends Number>
和 <T extends Number>
有什么区别,为什么第一个编译不通过?
通话中:
compiles(new HashMap<Integer, List<Integer>>());
T 与 Integer 相匹配,因此参数的类型是 Map<Integer,List<Integer>>
。方法 doesntCompile
不是这种情况:无论调用中的实际参数是什么,参数类型都保持 Map<Integer, List<? extends Number>>
;这不能从 HashMap<Integer, List<Integer>>
.
更新
在 doesntCompile
方法中,没有什么能阻止你做这样的事情:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
map.put(1, new ArrayList<Double>());
}
很明显,它不能接受 HashMap<Integer, List<Integer>>
作为参数。
通过定义具有以下签名的方法:
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
并像这样调用它:
compiles(new HashMap<Integer, List<Integer>>());
您正在将 T
与您提供的类型相匹配。
在 jls §8.1.2 中,我们发现(有趣的部分由我加粗):
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments. All of these parameterized types share the same class at run time.
换句话说,类型 T
与输入类型匹配并分配 Integer
。签名将有效地变为 static void compiles(Map<Integer, List<Integer>> map)
.
关于doesntCompile
方法,jls定义了子类型规则(§4.5.1,由我加粗):
A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):
? extends T <= ? extends S if T <: S
? extends T <= ?
? super T <= ? super S if S <: T
? super T <= ?
? super T <= ? extends Object
T <= T
T <= ? extends T
T <= ? super T
这意味着 ? extends Number
确实包含 Integer
甚至 List<? extends Number>
包含 List<Integer>
,但 Map<Integer, List<? extends Number>>
和 [=23 并非如此=].有关该主题的更多信息,请参见 in this SO thread。您仍然可以通过声明使带有 ?
通配符的版本工作,您期望 List<? extends Number>
:
public class Example {
// now it compiles
static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
public static void main(String[] args) {
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
我建议你看看 documentation of generic wildcards especially guidelines for wildcard use
坦率地说你的方法#doesntCompile
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
并像
一样打电话doesntCompile(new HashMap<Integer, List<Integer>>());
根本不正确
让我们添加合法实施:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
List<Double> list = new ArrayList<>();
list.add(0.);
map.put(0, list);
}
真的很好,因为 Double 扩展了 Number,所以 List<Double>
和 List<Integer>
一样绝对没问题,对吧?
但是,根据你的示例,你仍然认为合法传递到这里new HashMap<Integer, List<Integer>>()
吗?
编译器不这么认为,并且正在尽力避免这种情况。
尝试使用方法#compile 执行相同的实现,编译器显然不允许您将双精度列表放入映射中。
static <T extends Number> void compiles(Map<Integer, List<T>> map) {
List<Double> list = new ArrayList<>();
list.add(10.);
map.put(10, list); // does not compile
}
基本上除了 List<T>
什么都不能放,这就是为什么用 new HashMap<Integer, List<Integer>>()
或 [=19= 调用该方法是安全的原因]或new HashMap<Integer, List<Long>>()
或new HashMap<Integer, List<Number>>()
.
所以简而言之,您正在尝试使用编译器进行作弊,它可以很好地抵御这种作弊。
注意:Maurice Perry 编辑 post 的回答绝对正确。我只是不确定它是否足够清楚,所以尝试(真的希望我设法)添加更广泛的 post.
演示的简单示例。同样的例子可以像下面这样形象化。
static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too
public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}
List<Pair<? extends Number>>
是多级通配符类型,而 List<? extends Number>
是标准通配符类型。
通配符类型 List<? extends Number>
的有效具体实例包括 Number
和 Number
的任何子类型,而 List<Pair<? extends Number>>
是类型参数的类型参数并且本身具有泛型类型的具体实例化。
泛型是不变的,所以 Pair<? extends Number>
通配符类型只能接受 Pair<? extends Number>>
。内部类型 ? extends Number
已经是协变的。您必须将封闭类型设为协变以允许协变。