我是泛型的新手,读过一篇文章“参数化类型,例如Intent
,不可实例化-我们无法创建它们的实例”。
全引号,从Java概括地说:
参数化类型(例如
ArrayList<T>
)不可实例化-我们 无法创建它们的实例。这是因为ArrayList<T>
只是一种类型 参数-只是真正类型的占位符。只有当 我们为type参数提供了一个具体的值(例如,<T>
),类型就完全形成了,我们可以 创建该类型的对象。如果我们要使用的类型未知,这会带来问题 在编译时。幸运的是,Java类型系统能够 容纳这个概念。它通过具有明确的概念来做到这一点 未知类型,表示为
ArrayList<String>
。
我知道它不应该实例化,因为具体(实际)类型是未知的。如果是这样,为什么下面的代码编译时没有错误?
<?>
我知道我对泛型的理解存在差距。有人可以指出我在这里想念什么吗?
答案 0 :(得分:2)
因为T
是另一个通用类型参数。
使类型可参数化是泛型的全部目的。因此,呼叫者可以指定类型。这可以在多个层次上完成:调用方也可以是通用的,并让其调用方指定类型。
public static void main(String[] args)
{
foo(7);
}
public static <T> void foo(T value)
{
bar(value);
}
public static <U> void bar(U value)
{
baz(value);
}
public static <V> void baz(V value)
{
System.out.println(value.getClass().getSimpleName());
}
它打印出来
Integer
参数化类型(例如
ArrayList<T>
)不可实例化
方法:您不能创建未知T的ArrayList。必须在编译时指定它。但这可以通过另一种通用方法间接完成。对于您而言,它是另一个 T
,将由您的通用getList
的调用者再次指定。
通配符<?>
有所不同。用于指定兼容性。 <?>
是避免指定类型的语法。您可以使用extends
来要求基本类型或接口。但是,您不能使用通配符创建实例。
List<?> list = new ArrayList<String>();
list = new ArrayList<Integer>();
否则将无法实现。在参数规范中使用它时最有意义,例如:
public static int foo(List<? extends Comparable> list)
{
return list.get(1).compareTo(list.get(2));
}
这本书非常令人困惑。假定<?>
以某种方式解决了无法实例化具有未知List
的{{1}}的问题。恕我直言,这是垃圾。必须指定T才能创建实例。
答案 1 :(得分:1)
您提到的代码可以编译,因为在调用该方法之前,实际上不会初始化对象“ lst”。由于该方法知道它将获取类型T的var-args参数,因此可以在这种情况下进行编译。以下面的示例Wrapper类为例:
public class Wrapper<T> {
public static <T> List<T> getList(T... elements){
List<T> lst = new ArrayList<>();
for(T element: elements) {
lst.add(element);
}
return lst;
}
}
此代码可以编译,因为尚未调用该方法。调用该方法时,类型T将是我们作为var-args参数传递的类型,并且代码不会编译。让我们在我们的主要方法中对此进行测试:
public static void main( String[] args ){
System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));
}
输出为:
[Hi, Hello, Yo]
但是,让我们生成一个编译时错误,以查看文章在我们的主要方法中正在谈论什么:
Wrapper<T> myWrap = new Wrapper<>();
我们实际上正在尝试在上面的代码中初始化Wrapper类的泛型对象,但是未知。由于即使我们调用该方法时,占位符的值也将是未知的,因此会导致编译时错误,而在getList方法中创建类型为T的List不会导致编译时错误,因为它将使用进行初始化。调用方法时的类型。
答案 2 :(得分:0)
一旦调用该方法->您正在使用具体值。
该方法定义T,然后在返回类型和参数列表中使用它。
public static <T> List<T> getList(T... elements)
一旦您将发送特定类型的第一个参数->合同将强制您输入下一个参数。
List<? extends Object> list = getList("", 1);
->在这种情况下,java在字符串和整数之间找不到共同点,因此它使用最基本的连接“对象”
List<String> list2 = getList("test", "test2");
->在这里您可以看到,因为所有参数都是Strings-java找到它的共同点,并将其用作T。
答案 3 :(得分:0)
这本书中的特定段落没有任何意义,而且是错误的。 new ArrayList<T>()
很好,只要我们处于名为T
的类型参数的范围内(或者我们所在的泛型类的类型参数,或者我们所在的泛型方法的类型参数) )。
new ArrayList<T>()
不能比new ArrayList<String>()
实例化-两者都编译为相同的字节码,并且都只在运行时实例化ArrayList
对象。该对象在运行时对其类型参数一无所知,因此无需在运行时实例化T
即可对其进行实例化。参数化类型(new ArrayList<T>
)的实例化表达式中的type参数仅由编译器用来对传递给构造函数的参数进行类型检查(在这种情况下没有),并找出由...返回的类型。表达方式;不能以其他任何方式使用。
并且顺便说一句,该方法不需要接收T
类型的任何参数或包含T
的任何类型的参数,该方法就可以起作用。接收到没有参数的方法仍可以实例化并返回完全正确的ArrayList<T>
:
public static <T> List<T> emptyList() {
List<T> lst = new ArrayList<T>();
return lst;
}
此外,书中该语句出现的部分与实例化没有任何关系-该部分与通配符有关,而通配符与对象实例化完全没有任何关系。所以我不太确定为什么他们在那里(错误地)提到它。