繁体   English   中英

实例化泛型类型ArrayList <T>

[英]Instantiating generic type ArrayList<T>

我是泛型的新手并在文章中读到“参数化类型,例如ArrayList<T> ,不可实例化 - 我们无法创建它们的实例”。

完全引用,来自Java in a Nutshell:

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

如果我们想要使用的类型在编译时未知,则会出现问题。 幸运的是,Java类型系统能够适应这一概念。 它通过具有未知类型的明确概念来表达,其表示为<?>

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

public class SampleTest {

    public static <T> List<T> getList(T... elements) {

        List<T> lst = new ArrayList<>(); // shouldn't this line return an error? 

        return lst;
    }
}

我知道我对仿制药的理解存在差距。 有人能指出我在这里缺少什么吗?

因为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));
  }

这本书非常混乱。 它假定<?>以某种方式解决了无法实例化具有未知TList的问题。 恕我直言,这是垃圾。 必须指定T才能创建实例。

你提到的代码可以编译,因为在调用方法之前,实际上没有初始化Object“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;
}

}

此代码可以编译,因为尚未调用该方法。 调用该方法时,Type T将是我们作为var-args参数传递的类型,代码将没有问题编译。 让我们在主要方法中测试一下:

 public static void main( String[] args ){

      System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));

 }

输出是:

[Hi, Hello, Yo]

但是,让我们生成编译时错误,以查看文章在我们的main方法中讨论的内容:

Wrapper<T> myWrap = new Wrapper<>();

我们实际上是在上面的代码中尝试初始化Wrapper类的通用Object,但是未知。 由于占位符的值即使在调用方法时也是未知的,因此会导致编译时错误,而在getList方法中创建类型为T的List不会导致编译时错误,因为它将初始化为调用方法时的类型。

一旦调用方法 - >您正在使用具体值。

该方法定义了T,稍后您将在返回类型和参数列表中使用它。

public static <T> List<T> getList(T... elements)

一旦你将从特定类型发送第一个参数 - >合同将强制你下一个参数。

List<? extends Object> list = getList("", 1); - >在这种情况下,java在字符串和整数之间找不到共同点,所以它使用最基本的连接“对象”

List<String> list2 = getList("test", "test2"); - >在这里你可以看到,因为所有的参数都是字符串 - java发现它的共同点并用它作为T.

书中的具体段落没有任何意义,也是错误的。 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;
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM