我一直在尝试使用Generic,很快我遇到了一些我无法解释的问题 例如:
import java.util.*;
public class Main {
public static void main(String[] args) {
//We cannot create List<?> l = new ArrayList<?>();
List<?> l = MagicClass.factory();
}
static class MagicClass {
public static <T> List<T> factory() {
return new ArrayList<T>();
}
}
}
我不明白<T>
如何成为显式类型参数
<?>
未定义。
答案 0 :(得分:2)
<?>
是wildcard,它意味着:“您喜欢的任何类型”。现在,由于<?>
不对所使用的类型做出任何假设,T
只能更多专用。因此,Java将Foo<T>
视为Foo<?>
。另一方面这样做是不明智的,因为Java没有更少的方法来检查代码。因此,Java通常会拒绝调用任何将type参数用作输入的方法。
示例:以下代码无法编译:
ArrayList<?> foo = new ArrayList<String>(); foo.add("bar");
这是因为,Java不知道
T
中的foo
是什么。foo
可能是ArrayList<Integer>
,因此为了安全起见,不允许这样做。
如果您希望例如创建一个可以接受各种List<?>
实例的方法,而不管存储在其中的对象,可以使用它。
您永远不能使用通配符构建实例
以下代码无法编译:
ArrayList<?> foo = new ArrayList<?>();
这是因为在构建时,Java必须知道您将选择哪种类型。
但在许多情况下,它被用作有界通配符。以典型的例子:
public void drawAll(List<? extends Shape> shapes) {
//...
}
此方法有一个Shape
列表。现在,如果您将其写为List<Shape>
,则需要您提供ArrayList<Shape>
或LinkedList<Shape>
或其他集合。但也许你的专门你的集合只包含Quadrilateral
个实例(Rectangle
,Square
,Trapezium
)。如果您在函数ArrayList<Quadrilateral>
上使用drawAll(List<Shape> shapes)
,则会出现类型错误:实际上,List<Quadrilateral>
不是List<Shape>
,但是使用bounderies,您强制执行通用类型extends
,例如来自另一种类型。
答案 1 :(得分:2)
使用通配符实例化泛型类型会生成编译错误,因为通配符表示任何类型,而实例化的ArrayList
应具有具体类型。通配符可用于变量或参数类型,但不能在创建泛型类型的实例时使用。
this tutorial page中也提到了这一点:
在通用代码中,称为通配符的问号(
?
)表示未知类型。通配符可用于各种情况:作为参数,字段或局部变量的类型;有时作为返回类型(虽然更好的编程实践更具体)。 通配符永远不会用作泛型方法调用,泛型类实例创建或超类型的类型参数。
另一方面,在factory
方法中,T
是一个类型参数,将由编译器推断,并且由于List<?>
表示任何类型,因此它被接受编译器。
答案 2 :(得分:0)
我们无法创建List l = new ArrayList();
如果你真的想这样做,你可以做到
List<?> l = new ArrayList<Object>();
或
List<?> l = new ArrayList<String>();
或
List<?> l = new ArrayList<Math>();
或
List<?> l = new ArrayList<SomeBogusIrrelevantClass>();
都是正确的。只需挑选任何满足界限的类型;它甚至不必与你正在做的事情有关。
从这里,您可以看到为什么您尝试做的事情是荒谬的 - 您有一个可能是ArrayList<Math>
或可能是ArrayList<SomeBogusIrrelevantClass>
的列表(您不会这样做)知道)。那么你能用它做什么呢?不多。
您无法向其中添加任何内容(null
除外)。所以你有一个列表,其中元素只能有值null
;唯一可以改变的是null
个。但在这种情况下,您也可以使用整数。