实例化通用类型ArrayList <t>

时间:2018-10-02 07:56:09

标签: java generics

我是泛型的新手,读过一篇文章“参数化类型,例如Intent,不可实例化-我们无法创建它们的实例”。

全引号,从Java概括地说:

  

参数化类型(例如ArrayList<T>)不可实例化-我们   无法创建它们的实例。这是因为ArrayList<T>只是一种类型   参数-只是真正类型的占位符。只有当   我们为type参数提供了一个具体的值(例如,   <T>),类型就完全形成了,我们可以   创建该类型的对象。

     

如果我们要使用的类型未知,这会带来问题   在编译时。幸运的是,Java类型系统能够   容纳这个概念。它通过具有明确的概念来做到这一点   未知类型,表示为ArrayList<String>

我知道它不应该实例化,因为具体(实际)类型是未知的。如果是这样,为什么下面的代码编译时没有错误?

<?>

我知道我对泛型的理解存在差距。有人可以指出我在这里想念什么吗?

4 个答案:

答案 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;
}

此外,书中该语句出现的部分与实例化没有任何关系-该部分与通配符有关,而通配符与对象实例化完全没有任何关系。所以我不太确定为什么他们在那里(错误地)提到它。