Java 泛型和模板
Java Generics and templates
我有以下 Java
class 定义:
import java.util.*;
public class Test {
static public void copyTo(Iterator<? extends Number> it, List<? extends Number> out) {
while(it.hasNext())
out.add(it.next());
}
public static void main(String[] args) {
List<Integer> in = new ArrayList<Integer>();
for (int i = 1; i <= 3; i++) {
in.add(i);
}
Iterator<Integer> it = in.iterator();
List<Number> out = new ArrayList<Number>();
copyTo(it, out);
System.out.println(out.size());
}
}
就是这样,我在Java
中使用wildcards
定义了方法copyTo
。我定义 List<Number> out
但 Iterator<Integer> it
。我的想法是我可以将迭代器定义为 Iterator<? extends Number>
并且这将匹配类型。然而事实并非如此:
Test.java:13: error: no suitable method found for add(Number)
out.add(it.next());
^
method List.add(int,CAP#1) is not applicable
(actual and formal argument lists differ in length)
method List.add(CAP#1) is not applicable
(actual argument Number cannot be converted to CAP#1 by method invocation conversion)
method Collection.add(CAP#1) is not applicable
(actual argument Number cannot be converted to CAP#1 by method invocation conversion)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
1 error
所以我继续为 copyTo
方法定义了另一个定义:
static public void copyTo(Iterator<? super Integer> it, List<? super Integer> out) {
while(it.hasNext())
out.add(it.next());
}
也不行。在这种情况下使用 wildcards
的正确说法是什么?
如果一个方法签名涉及两个或多个通配符,而你的方法逻辑要求它们相同,你需要使用泛型类型参数而不是通配符。
static public <T extends Number> void copyTo(Iterator<? extends T> it, List<? super T> out) {
while(it.hasNext())
out.add(it.next());
}
这里我用到了PECS(producer extends,consumer super)。 out
消耗 T
s(所以 super
),而迭代器产生 T
s,所以 extends
.
编辑
正如@Cinnam 在评论中正确指出的那样,您可以不用
static void copyTo(Iterator<? extends Integer> it, List<? super Integer> out)
这些签名实际上是等价的,因为 Integer
是最终的,所以任何 class 是某些 class 扩展 Integer
的超级 class 必须是Integer
的超级 class。
但是,就编译器而言,这两个签名并不等同。你可以通过尝试
来测试这个
static <T extends Number> void copyTo1(Iterator<? extends T> it, List<? super T> out) {
copyTo2(it, out); // doesn't compile
}
static void copyTo2(Iterator<? extends Integer> it, List<? super Integer> out) {
copyTo1(it, out);
}
这个不编译,说明就编译器而言,带类型参数的版本更通用
首先你想通过向方法本身添加一个类型变量来施加约束,因为使用通配符你不能在两个参数之间施加约束,然后你必须考虑涉及的类型的变化你的方法:
- 你想要一个
Iterator<X>
作为输入,其中 X
至少是你想要复制的数字类型的类型(或子类型)
- 你想要一个列表作为输出,其中
Y
最多是数字类型(或超类型)的类型
这些约束是不同的,必须以不同的方式表达:
static public <T> void copyTo(Iterator<? extends T> it, List<? super T> out) {
while(it.hasNext())
out.add(it.next());
}
基本上就是"I accept an Iterator
of T
or a subtype of T
and I output to a List of T
or a supertype of T
"
我有以下 Java
class 定义:
import java.util.*;
public class Test {
static public void copyTo(Iterator<? extends Number> it, List<? extends Number> out) {
while(it.hasNext())
out.add(it.next());
}
public static void main(String[] args) {
List<Integer> in = new ArrayList<Integer>();
for (int i = 1; i <= 3; i++) {
in.add(i);
}
Iterator<Integer> it = in.iterator();
List<Number> out = new ArrayList<Number>();
copyTo(it, out);
System.out.println(out.size());
}
}
就是这样,我在Java
中使用wildcards
定义了方法copyTo
。我定义 List<Number> out
但 Iterator<Integer> it
。我的想法是我可以将迭代器定义为 Iterator<? extends Number>
并且这将匹配类型。然而事实并非如此:
Test.java:13: error: no suitable method found for add(Number)
out.add(it.next());
^
method List.add(int,CAP#1) is not applicable
(actual and formal argument lists differ in length)
method List.add(CAP#1) is not applicable
(actual argument Number cannot be converted to CAP#1 by method invocation conversion)
method Collection.add(CAP#1) is not applicable
(actual argument Number cannot be converted to CAP#1 by method invocation conversion)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
1 error
所以我继续为 copyTo
方法定义了另一个定义:
static public void copyTo(Iterator<? super Integer> it, List<? super Integer> out) {
while(it.hasNext())
out.add(it.next());
}
也不行。在这种情况下使用 wildcards
的正确说法是什么?
如果一个方法签名涉及两个或多个通配符,而你的方法逻辑要求它们相同,你需要使用泛型类型参数而不是通配符。
static public <T extends Number> void copyTo(Iterator<? extends T> it, List<? super T> out) {
while(it.hasNext())
out.add(it.next());
}
这里我用到了PECS(producer extends,consumer super)。 out
消耗 T
s(所以 super
),而迭代器产生 T
s,所以 extends
.
编辑
正如@Cinnam 在评论中正确指出的那样,您可以不用
static void copyTo(Iterator<? extends Integer> it, List<? super Integer> out)
这些签名实际上是等价的,因为 Integer
是最终的,所以任何 class 是某些 class 扩展 Integer
的超级 class 必须是Integer
的超级 class。
但是,就编译器而言,这两个签名并不等同。你可以通过尝试
来测试这个static <T extends Number> void copyTo1(Iterator<? extends T> it, List<? super T> out) {
copyTo2(it, out); // doesn't compile
}
static void copyTo2(Iterator<? extends Integer> it, List<? super Integer> out) {
copyTo1(it, out);
}
这个不编译,说明就编译器而言,带类型参数的版本更通用
首先你想通过向方法本身添加一个类型变量来施加约束,因为使用通配符你不能在两个参数之间施加约束,然后你必须考虑涉及的类型的变化你的方法:
- 你想要一个
Iterator<X>
作为输入,其中X
至少是你想要复制的数字类型的类型(或子类型) - 你想要一个列表作为输出,其中
Y
最多是数字类型(或超类型)的类型
这些约束是不同的,必须以不同的方式表达:
static public <T> void copyTo(Iterator<? extends T> it, List<? super T> out) {
while(it.hasNext())
out.add(it.next());
}
基本上就是"I accept an Iterator
of T
or a subtype of T
and I output to a List of T
or a supertype of T
"