简体   繁体   English

实例化泛型类型ArrayList <T>

[英]Instantiating generic type ArrayList<T>

I am new to generics and read in a article "A parameterized type, such as ArrayList<T> , is not instantiable — we cannot create instances of them". 我是泛型的新手并在文章中读到“参数化类型,例如ArrayList<T> ,不可实例化 - 我们无法创建它们的实例”。

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

A parameterized type, such as ArrayList<T> , is not instantiable - we cannot create instances of them. 参数化类型(如ArrayList<T> )不可实例化 - 我们无法创建它们的实例。 This is because <T> is just a type parameter - merely a place-holder for a genuine type. 这是因为<T>只是一个类型参数 - 仅仅是真正类型的占位符。 It is only when we provide a concrete value for the type parameter, (eg, ArrayList<String> ), that the type becomes fully formed and we can create objects of that type. 只有当我们为type参数(例如, ArrayList<String> )提供具体值时,才会完全形成类型,并且我们可以创建该类型的对象。

This poses a problem if the type that we want to work with is unknown at compile time. 如果我们想要使用的类型在编译时未知,则会出现问题。 Fortunately, the Java type system is able to accommodate this concept. 幸运的是,Java类型系统能够适应这一概念。 It does so by having an explicit concept of the unknown type which is represented as <?> . 它通过具有未知类型的明确概念来表达,其表示为<?>

I understand that it should not be instantiable since the concrete (actual) type is not known. 我知道它不应该是可实例化的,因为具体(实际)类型是未知的。 If so, why does the below code compiles without an error? 如果是这样,为什么下面的代码编译没有错误?

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

I know there is a gap in my understanding of generics here. 我知道我对仿制药的理解存在差距。 Can someone point out what am i missing here? 有人能指出我在这里缺少什么吗?

Because T is given as another generic type argument. 因为T是另一个泛型类型参数。

It's the whole purpose of generics to make the type parameterizeable. 泛型的全部目的是使类型可参数化。 So the caller can specify the type. 因此调用者可以指定类型。 This can be done in multiple layers: the caller may also be generic and let its caller specify the type. 这可以在多个层中完成:调用者也可以是通用的,并让其调用者指定类型。

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());
}

It prints out 打印出来

Integer

A parameterized type, such as ArrayList<T> , is not instantiable 参数化类型(例如ArrayList<T> )不可实例化

Means: You cannot create ArrayList of an unknown T. It must be specified at compile time. 意思是:你不能创建未知T的ArrayList。它必须在编译时指定。 But it can be done indirectly, by another generic. 但它可以通过另一种通用方法间接完成。 In your case, it's another T , which will be specified again by the caller of your generic getList . 在你的情况下,它是另一个 T ,它将由你的通用getList的调用者再次指定。


The wildcard <?> is something different. 通配符<?>是不同的东西。 It is used to specify compatibility. 它用于指定兼容性。 <?> is the syntax to avoid specification of the type. <?>是避免指定类型的语法。 You can use extends to require a basetype or interface. 您可以使用extends来要求基本类型或接口。 However, you cannot create instances with wildcards. 但是,您无法使用通配符创建实例。

  List<?> list = new ArrayList<String>();
  list = new ArrayList<Integer>();

This wouldn't be possible otherwise. 否则这是不可能的。 It makes most sense when using it in parameter specifications, for instance: 在参数规范中使用它时最有意义,例如:

  public static int foo(List<? extends Comparable> list)
  {
     return list.get(1).compareTo(list.get(2));
  }

It's very confusing of this book. 这本书非常混乱。 It assumes that <?> somehow solves the problem that a List with unknown T cannot be instantiated. 它假定<?>以某种方式解决了无法实例化具有未知TList的问题。 IMHO, this is rubbish. 恕我直言,这是垃圾。 T must be specified to create an instance. 必须指定T才能创建实例。

The code that you mention can compile because the Object "lst" is not actually initialized until the method is called. 你提到的代码可以编译,因为在调用方法之前,实际上没有初始化Object“lst”。 Since the method knows that it will be getting a var-args argument of type T, it can compile in this scenario. 由于该方法知道它将获得类型为T的var-args参数,因此可以在此方案中进行编译。 Take the example Wrapper class below for example: 以下面的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;
}

} }

This code can compile because the method hasn't been called. 此代码可以编译,因为尚未调用该方法。 When the method is called, Type T will be the type that we pass as the var-args argument and the code will have no issue compiling. 调用该方法时,Type T将是我们作为var-args参数传递的类型,代码将没有问题编译。 Lets test this in our main method: 让我们在主要方法中测试一下:

 public static void main( String[] args ){

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

 }

And the output is: 输出是:

[Hi, Hello, Yo]

However, lets generate a compile-time error to see what the article is talking about within our main method: 但是,让我们生成编译时错误,以查看文章在我们的main方法中讨论的内容:

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

We are actually trying initialize a generic Object of the Wrapper class in the code above, but is unknown. 我们实际上是在上面的代码中尝试初始化Wrapper类的通用Object,但是未知。 Since the value for the placeholder will be unknown even when we call the method, it results in a compile-time error, whereas creating a List of type T within the getList method does not cause a compile-time error because it will be initialized with a type when the method is called. 由于占位符的值即使在调用方法时也是未知的,因此会导致编译时错误,而在getList方法中创建类型为T的List不会导致编译时错误,因为它将初始化为调用方法时的类型。

once you call the method -> you are using a concrete value. 一旦调用方法 - >您正在使用具体值。

the method defines T and later you use it in the return type and the parameter list. 该方法定义了T,稍后您将在返回类型和参数列表中使用它。

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

once you will send the first parameter from specific type -> the contract will force you for the next parameters. 一旦你将从特定类型发送第一个参数 - >合同将强制你下一个参数。

List<? extends Object> list = getList("", 1); -> in this case java doesnt find common between string and integer so it uses the most basic connection "Object" - >在这种情况下,java在字符串和整数之间找不到共同点,所以它使用最基本的连接“对象”

List<String> list2 = getList("test", "test2"); -> here you can see that because all of the parameters are Strings - java find that in common and use it as the T. - >在这里你可以看到,因为所有的参数都是字符串 - java发现它的共同点并用它作为T.

The specific passage from the book doesn't make any sense and is wrong. 书中的具体段落没有任何意义,也是错误的。 new ArrayList<T>() is perfectly fine provided that we are in the scope of a type parameter named T (either a type parameter of a generic class that we are in, or a type parameter of the generic method we are in). new ArrayList<T>()完全没问题,前提是我们在一个名为T的类型参数的范围内(我们所在的泛型类的类型参数,或者我们所在的泛型方法的类型参数)。

new ArrayList<T>() can no less be instantiated than new ArrayList<String>() -- both compile to the same bytecode and both just instantiate an ArrayList object at runtime. new ArrayList<T>()可以实例化,而不是new ArrayList<String>() - 两者都编译为相同的字节码,并且只是在运行时实例化一个ArrayList对象。 The object doesn't know anything about its type parameter at runtime, and therefore no knowledge of T at runtime is needed to instantiate it. 该对象在运行时对其类型参数一无所知,因此在运行时不需要知道T来实例化它。 The type parameter in an instantiation expression of a parameterized type ( new ArrayList<T> ) is just used by the compiler to type-check the parameters passed to the constructor (there are none in this case) and to figure out the type returned by the expression; 参数化类型( new ArrayList<T> )的实例化表达式中的type参数只是由编译器用于对传递给构造函数的参数进行类型检查(在这种情况下没有)并找出返回的类型表达方式; it is not used in any other way. 它不以任何其他方式使用。

And by the way the method does not need to receive any parameters of type T , or of any type containing T , in order for this to work. 顺便说一下,该方法不需要接收任何类型为T参数,也不需要接收包含T的任何类型的参数,以使其工作。 A method that receives no arguments can still instantiate and return an ArrayList<T> perfectly fine: 接收任何参数的方法仍然可以实例化并返回ArrayList<T>完全正常:

public static <T> List<T> emptyList() {
    List<T> lst = new ArrayList<T>();
    return lst;
}

Also, the section in the book where this statement appears in doesn't really have anything to do with instantiation -- the section is about wildcards, and wildcards don't really have anything to do with object instantiation at all. 此外,书中出现此语句的部分与实例化没有任何关系 - 该部分是关于通配符的,并且通配符实际上与对象实例化没有任何关系。 So I am not really sure why they are mentioning it (incorrectly) there. 所以我不确定为什么他们(错误地)在那里提到它。

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

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