Java泛型:List, List, List>

时间:2021-03-04 15:51:21

Can someone explained, as detailed as possible, the differences between the following types?

有人能尽可能详细地解释一下以下类型之间的区别吗?

List
List<Object>
List<?>

Let me make this more specific. When would I want to use

我再具体一点。我想什么时候用

// 1 
public void CanYouGiveMeAnAnswer(List l) { }

// 2
public void CanYouGiveMeAnAnswer(List<Object> l) { }

// 3
public void CanYouGiveMeAnAnswer(List<?> l) { }

12 个解决方案

#1


98  

As the other posts have noted, you're asking about a Java feature called generics. In C++, this is called templates. The Java beasties are far tamer to deal with.

正如其他帖子所提到的,您正在询问一个名为泛型的Java特性。在c++中,这称为模板。要处理Java小动物要温和得多。

Let me answer your questions functionally (if that's not a naughty word for OO discussions).

让我在功能上回答你的问题(如果这对OO讨论来说不是一个顽皮的词)。

Before generics, you had good old concrete classes like Vector.

在泛型之前,您有很好的旧的具体类,比如Vector。

Vector V = new Vector();

Vectors hold any old object you give them.

向量持有你给它们的任何旧物体。

V.add("This is an element");
V.add(new Integer(2));
v.add(new Hashtable());

However, they do this by casting everything you give it into an Object (the root of all Java classes). That's OK until you attempt to retrieve the values stored in your Vector. When you do, you need to cast the value back into the original class (if you want to do anything meaningful with it).

但是,他们通过将所有的东西都转换成一个对象(所有Java类的根)来实现这一点。在尝试检索存储在向量中的值之前,这是可以的。当您这样做时,您需要将值转换回原始类(如果您想对它做任何有意义的事情)。

String s = (String) v.get(0);
Integer i = (Integer) v.get(1);
Hashtable h = (Hashtable) v.get(2);

Casting gets pretty old fast. More than that, the compiler whines at you about unchecked casts. For a vivid example of this, use the XML-RPC library from Apache (version 2 anyway). The most important problem with this is that consumers of your Vector have to know the exact class of its values at compile time in order to cast correctly. In cases where the producer of the Vector and the consumer are completely isolated from each other, this can be a fatal issue.

演员阵容变老得很快。不仅如此,编译器还会对您抱怨未检查的强制类型转换。对于一个生动的例子,使用Apache的XML-RPC库(无论如何是版本2)。最重要的问题是,向量的使用者必须在编译时知道它的值的确切类,以便正确地转换。在生产者和消费者完全隔离的情况下,这可能是一个致命的问题。

Enter generics. Generics attempt to create strongly typed classes to do generic operations.

进入仿制药。泛型尝试创建强类型类来执行泛型操作。

ArrayList<String> aList = new ArrayList<String>();
aList.add("One");
String element = aList.get(0); // no cast needed
System.out.println("Got one: " + element); 

Now, if you take a look at the infamous gang of four's Design Patterns book, you'll notice that there is some wisdom in divorcing variables from their implementing class. It's better to think of contracts rather than implementation. So, you might say that all List objects do the same things: add(), get(), size(), etc. However, there are many implementations of List operations that may choose to obey the contract in various ways (e.g. ArrayList). However, the type of data these object deal with is left as a runtime consideration to you, the user of the generic class. Put it all together and you'll see the following line of code very frequently:

现在,如果您查看声名狼藉的“*”设计模式书籍,您将注意到,将变量与实现类分离是有一些智慧的。最好是考虑契约而不是执行。因此,您可能会说所有的列表对象都做相同的事情:add()、get()、size()等。但是,有许多列表操作的实现可以选择以各种方式执行契约(例如ArrayList)。但是,这些对象处理的数据类型留给您作为泛型类的用户作为运行时考虑因素。把它们放在一起,你会经常看到下面一行代码:

List<String> L = new ArrayList<String>();

You should read that as "L is a kind of List that deals with String objects". When you start dealing with Factory classes, it is critical to deal with contracts rather than specific implementations. Factories produce objects of various types at runtime.

你应该把它读成“L是一种处理字符串对象的列表”。当您开始处理工厂类时,处理契约而不是特定的实现是至关重要的。工厂在运行时生成各种类型的对象。

Using generics is pretty easy (most of the time). However, one awful day you may decide you want to implement a generic class. Perhaps you've thought of a great new List implementation. When you define that class, you use <t> as a placeholder for the kind of object that will be manipulated by the methods. If you're confused, use the generic classes for List until you're comfortable. Then, you can dive into the implementation with a bit more confidence. Or you can look at the source code for the various List classes that ship with the JRE. Open source is great that way.

使用泛型是相当容易的(大部分时间)。然而,有一天你可能会决定要实现一个泛型类。也许您已经想到了一个很棒的新列表实现。当您定义该类时,您使用 作为方法将操作的对象类型的占位符。如果您感到困惑,请使用列表的泛型类,直到您感到舒服为止。然后,您可以更加自信地深入实现。或者您可以查看与JRE一起发布的各种列表类的源代码。开源就是这样。

Have a look at the Oracle/Sun docs about generics. Cheers.

看看Oracle/Sun关于泛型的文档。欢呼。

#2


39  

In my own simple terms:

用我自己简单的话说:

List

列表

Would declare an ordinary collection, can hold any type, and will always return Object.

将声明一个普通集合,可以保存任何类型,并将始终返回对象。

List<Object>

列表 <对象>

Will create a list that can hold any type of object, but can only get assigned a another List<Object>

将创建一个可以容纳任何类型对象的列表,但只能获得另一个列表< object >

For instance this doesn't work;

例如,这行不通;

List<Object> l = new ArrayList<String>();

Of course you can add anything but only can pull Object.

当然你可以添加任何东西,但只能拖动对象。

List<Object> l = new ArrayList<Object>();

l.add( new Employee() );
l.add( new String() );

Object o = l.get( 0 );
Object o2 = l.get( 1 );

Finally

最后

List<?>

List < ?

Will let you assign any type, including

会让你分配任何类型,包括?

List <?> l = new ArrayList(); 
List <?> l2 = new ArrayList<String>();

This would be called collection of unknown and since the common denominator of unknown is Object you will be able to fetch Objects ( a coincidence )

这将被称为未知量集合,因为未知量的公分母是Object你将能够获取对象(巧合)

The importance of unknown comes when its used with subclassing:

当它与子类化一起使用时,未知的重要性就显现出来了:

List<? extends Collection> l = new ArrayList<TreeSet>(); // compiles

List<? extends Collection> l = new ArrayList<String>(); // doesn't,
// because String is not part of *Collection* inheritance tree. 

I hope using Collection as the type doesn't create confusion, that was the only tree that came to my mind.

我希望使用集合作为类型不会造成混乱,这是我唯一想到的树。

The difference here, is that l is a collection of unknow that belongs to the Collection hierarchy.

这里的区别是,l是属于集合层次结构的未知集合。

#3


15  

I refer you to the excellent Java Generics tutorial, and the "advanced" Generics tutorial, both available from Sun Microsystems. Another great resource is the Java Generics and Collections book.

我将向您介绍优秀的Java泛型教程和“高级”泛型教程,它们都可以从Sun Microsystems获得。另一个伟大的资源是Java泛型和集合书籍。

#4


15  

To add to the already good answers here:

在这里加上已经很好的答案:

Method arguments:

方法参数:

List<? extends Foo>

列表< ?扩展Foo >

good choice if you don't intend to alter the list, and only care that everything in the list is assignable to type 'Foo'. This way, the caller can pass in a List<FooSubclass> and your method works. Usually the best choice.

如果您不打算更改列表,并且只关心列表中的所有内容都可以指定为“Foo”类型,那么这是一个很好的选择。这样,调用者可以传入一个列表 ,您的方法就可以工作了。通常是最好的选择。

List<Foo>

< Foo >列表

good choice if you intend to add Foo objects to the list in your method. The caller may not pass in a List<FooSubclass>, as you intend to add a Foo to the List.

如果您打算将Foo对象添加到方法中的列表,那么这是一个很好的选择。调用者可能不会传入列表 ,因为您打算向列表添加一个Foo。

List<? super Foo>

列表< ?超级Foo >

good choice if you intend to add Foo objects to the list, and it's not important what else is in the list (ie, you are ok getting a List<Object> that contains a 'Dog' that has nothing to do with Foo).

如果您打算将Foo对象添加到列表中,并且列表中的其他内容并不重要(例如,您可以获得一个包含与Foo无关的'Dog'的列表)。

Method return values

方法返回值

just like method arguments, but with the benefits reversed.

就像方法参数一样,但是好处是相反的。

List<? extends Foo>

列表< ?扩展Foo >

Guarantees that everything in the returned List has type 'Foo'. It might be List<FooSubclass> though. Caller cannot add to the List. This is your go-to choice and the most common case by far.

保证返回列表中的所有内容都具有“Foo”类型。它可能是List< foo子类>。调用者不能添加到列表中。这是你的首选,也是目前最常见的情况。

List<Foo>

< Foo >列表

Just like List<? extends Foo> but also allows the caller to add to the List. Less common.

就像列表< ?扩展Foo>,但也允许调用者添加到列表。不太常见的。

List<? super Foo>

列表< ?超级Foo >

allows the caller to add Foo objects to the List, but does not guarantee what will be returned from list.get(0)... it could be anything from Foo to Object. The only guarantee is that this won't be a list of 'Dog' or some other choice that would prevent list.add(foo) from being legal. Very rare use case.

允许调用者向列表中添加Foo对象,但不保证从列表中返回什么。它可以是任何东西,从Foo到Object。唯一的保证是,这不会是一个“狗”的列表,也不会是其他阻止list.add(foo)合法的选项。非常罕见的用例。

I hope that helps. Good luck!

我希望有帮助。好运!

ps. To sum up... two questions...

ps。总之…两个问题…

do you need to add to the List? Do you care what is in the list?

你需要添加到列表中吗?你关心列表里有什么吗?

yes yes - use List<Foo>.

是的,使用列表

yes no - use List<? super Foo>.

是否-使用列表 。

no yes - use <? extends Foo> --- most common.

no yes - use ——最常见的。

no no - use <?>.

不使用 。

#5


4  

Simplest explanation which is not "RTFM":

最简单的解释不是“RTFM”:

List

Will generate lots of compiler warnings, but is mostly equivalent to:

会产生许多编译器警告,但主要相当于:

List<Object>

While:

而:

List<?>

basically means its something generic, but you don't know what the generic type is. Its great for getting rid of compiler warnings when you cant modify the return types of other things that just returned List. Its much more useful in the form:

它的基本意思是泛型,但你不知道泛型类型是什么。当您不能修改刚刚返回列表的其他东西的返回类型时,它可以很好地消除编译器警告。它更有用的形式是:

List<? extends SomeOtherThing>

#6


4  

I will try to answer this in detail. Before generics we were having only List (a raw list) and it can hold almost anything we can think of.

我会尽量详细地回答这个问题。在泛型出现之前,我们只有一个列表(一个原始列表),它可以容纳几乎所有我们能想到的东西。

List rawList = new ArrayList();
rawList.add("String Item");
rawList.add(new Car("VW"));
rawList.add(new Runnable() {
            @Override
            public void run() {
               // do some work.
            }
        });

The major problem with the raw list is when we want to get any element out of such list it can only guarantee that it would be Object and for that reason we need to use casting as:

原始列表的主要问题是,当我们想从列表中获取任何元素时,它只能保证它是对象,因此我们需要使用角色转换:

   Object item = rawList.get(0); // we get object without casting.
   String sameItem = (String) rawList.get(0); // we can use casting which may fail at runtime.

So conclusion is a List can store Object (almost everything is Object in Java) and always returns an Object.

结论是列表可以存储对象(在Java中几乎所有东西都是对象),并且总是返回一个对象。

Generics

Now lets talk about generics. Consider the following example:

现在我们来谈谈泛型。考虑下面的例子:

List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
stringsList.add(new Car("Fiat")); //error
String stringItem = stringsList.get(0);

In the above case we cannot insert anything other than String in stringsList as Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. And we get error when we try to insert a Car instance in it. Also it eliminates cast as you can check when we invoke get method. Check this link for understanding why we should use generics.

在上面的例子中,我们不能在stringsList中插入除了字符串之外的任何东西,因为Java编译器对通用代码应用了强类型检查,如果代码违反了类型安全,就会发出错误。当我们试图插入一个汽车实例时,我们会得到错误。它还消除了cast,您可以在调用get方法时进行检查。检查这个链接以了解为什么我们应该使用泛型。

List<Object>

If you read about type erasure then you will understand that List<String>, List<Long>, List<Animal> etc. will be having different static types at compile time but will have same dynamic type List at run time.

如果您阅读了关于类型擦除的内容,您就会了解列表 ,列表 , List 等将在编译时具有不同的静态类型,但在运行时将具有相同的动态类型列表。

If we have List<Object> then it can store only Object in it and almost everything is Object in Java. So we can have:

如果我们有List,那么它只能存储其中的对象,几乎所有的东西都是Java中的对象。所以我们可以有:

 List<Object> objectList = new ArrayList<Object>();
 objectList.add("String Item");
 objectList.add(new Car("VW"));
 objectList.add(new Runnable() {
        @Override
        public void run() {

        }
 });
 Object item = objectList.get(0); // we get object without casting as list contains Object
 String sameItem = (String) objectList.get(0); // we can use casting which may fail at runtime.

It seems List<Object> and List are same but actually they are not. Consider the following case:

List和List看起来是一样的,其实不是。考虑下面的例子:

List<String> tempStringList = new ArrayList<>();
rawList = tempStringList; // Ok as we can assign any list to raw list.
objectList = tempStringList; // error as List<String> is not subtype of List<Obejct> becuase generics are not convariant.

You can see we can assign any list to raw list and major reason for that is to allow backward compatibility. Also List<String> will be converted to List at run time due to type erasure and assignment will be fine anyways.

可以看到,我们可以为原始列表分配任何列表,主要原因是允许向后兼容。此外,List 将在运行时转换为List,因为类型擦除和赋值无论如何都很好。

But List<Object> means it can only refer to a list of objects and can also store objects only. Even though String is subtype of Object we cannot assign List<String> to List<Object> as generics are not covariant like arrays. They are invariant. Also check this link for more. Also check the difference between List and List<Object> in this question.

而List表示只能引用对象列表,也只能存储对象。即使字符串是对象的子类型,我们也不能将List 赋给List ,因为泛型不像数组那样是协变的。他们是不变的。也检查这个链接更多。还要检查列表和列表之间的区别。

List<?>

Now we are left with List<?> which basically means list of unknown type and can refer to any list.

现在剩下List 基本上是指未知类型的列表,可以引用任何列表。

List<?> crazyList = new ArrayList<String>();
 List<String> stringsList = new ArrayList<>();
 stringsList.add("Apple");
 stringsList.add("Ball");
 crazyList = stringsList; // fine

The character ? is known as wildcard and List<?> is a list of unbounded wildcard. There are certain points to observe now.

这个角色?被称为通配符和列表 是一个*通配符的列表。现在有几个要点值得注意。

We cannot instantiate this list as the following code will not compile:

我们不能实例化这个列表,因为以下代码不会编译:

List<?> crazyList = new ArrayList<?>(); // any list.

We can say a wildcard parameterized type is more like an interface type as we can use it to refer to an object of compatible type but not for itself.

我们可以说通配符参数化类型更像接口类型,因为我们可以使用它来引用兼容类型的对象,但不能引用它本身。

List<?> crazyList2 = new ArrayList<String>();

We cannot insert any item to it as we don't know what actually the type would be.

我们不能插入任何项,因为我们不知道类型是什么。

crazyList2.add("Apple"); // error as you dont actually know what is that type.

Now question arises When would I want to use List<?>?

现在问题来了,我想什么时候使用List ?

You can think of this as a read-only list where you don't care about the type of the items. You can use it to invoke methods like returning the length of the list, printing it etc.

您可以将其视为一个只读列表,其中不关心项的类型。您可以使用它来调用方法,例如返回列表的长度、打印它等等。

 public static void print(List<?> list){
        System.out.println(list);
    }

You can also check the difference between List, List<?>, List<T>, List<E>, and List<Object> here.

您还可以检查List和List 列表 ,列表 ,列表

#7


3  

The shortest possible explanation is: The second item is a list that can hold any type, and you can add objects to it:

最简短的解释是:第二项是一个可以容纳任何类型的列表,你可以向其中添加对象:

List<Object>

The first item you list is treated as essentially equivalent to this, except you will get compiler warnings because it is a "raw type".

您列出的第一个项本质上与此相同,但是您将得到编译器警告,因为它是“原始类型”。

List

The third is a list that can hold any type, but you cannot add anything to it:

第三种是可以容纳任何类型的列表,但是不能添加任何内容:

List<?> 

Basically, you use the second form (List<Object>) when you truly have a list that can contain any object and you want to be able to add elements to the list. You use the third form (List<?>)when you receive the list as a method return value and you will iterate over the list but never add anything to it Never use the first form (List) in new code compiling under Java 5 or later.

基本上,当您真正拥有一个可以包含任何对象的列表时,您将使用第二个表单(List),您希望能够将元素添加到列表中。当您将列表作为方法返回值接收时,您将使用第三个表单(List ),并且您将对列表进行迭代,但从不向它添加任何内容,永远不要在Java 5或更高版本的新代码编译中使用第一个表单(List)。

#8


1  

I'd put it this way: While List and List<Object> can contain any type of objects, List<?> contains elements of an unknown type, but once that type is captured, it can only contain elements of that type. Which is why it is the only type safe variant of those three, and therefore generally preferable.

我这样写:List和List可以包含任何类型的对象,List 包含未知类型的元素,但一旦捕获该类型,它只能包含该类型的元素。这就是为什么它是这三种类型中唯一的类型安全变体,因此通常更可取。

#9


0  

To complement the tutorials mentioned by Rob, here's a wikibook explaining the topic:
http://en.wikibooks.org/wiki/Java_Programming/Generics

为了补充Rob提到的教程,这里有一本wikibook来解释这个主题:http://en.wikibooks.org/wiki/Java_Programming/Generics


Edit:

编辑:

  1. No restrictions on type of items in list

    对列表中的项目类型没有限制

  2. Items in list must extend Object

    列表中的项目必须扩展对象

  3. Wildcard used by itself, so it matches anything

    通配符本身使用,因此它匹配任何东西

Would it be naive of me to conclude at this point that there's hardly any/no difference at all?

在这一点上,我是否天真地认为几乎没有任何区别?

#10


0  

When would I want to use

我想什么时候用

public void CanYouGiveMeAnAnswer( List l ){}

When you cant to do all the casting your self.

当你不能做所有的选择时。

When would I want to use

我想什么时候用

public void CanYouGiveMeAnAnswer( List l<Object> ){}

When you want to restrict the type of the List. For instance, this would be an invalid argument.

当您想要限制列表的类型时。例如,这是一个无效的参数。

 new ArrayList<String>();

When would I want to use

我想什么时候用

public void CanYouGiveMeAnAnswer( List l<?> ){}

Mostly never.

主要是永远。

#11


0  

List, List<?>, and List<? extends Object> are the same thing. The second is more explicit. For a list of this type, you cannot know what types are legal to put into it, and you don't know anything about the types you can get out of it, except that they will be objects.

列表,列表< ?列表>,< ?扩展对象>是一样的。第二个更明确。对于这种类型的列表,您不知道将哪些类型放入其中是合法的,您也不知道可以从中获得哪些类型,除了它们将是对象。

List<Object> specifically means that the list contains any sort of object.

List明确表示该列表包含任何类型的对象。

Let's say we make a list of Foo:

假设我们做了一个Foo列表

List<Foo> foos= new ArrayList<Foo>();

It is not legal to put a Bar into foos.

在foos中设置栏是不合法的。

foos.add(new Bar()); // NOT OK!

It is always legal to put anything into a List<Object>.

将任何内容放入列表始终是合法的。

List<Object> objs = new ArrayList<Object>();
objs.add(new Foo());
objs.add(new Bar());

But you mustn't be allowed to put a Bar into a List<Foo> - that's the whole point. So that means that this:

但是你不能把一个条放到一个列表中,这就是全部的要点。这意味着

List<Object> objs = foos; // NOT OK!

is not legal.

是不合法的。

But it's ok to say that foos is a list of something but we don't know specifically what it is:

但可以这么说,foos是一个列表但我们不知道具体是什么

List<?> dontKnows = foos;

But that then means that it must be prohibited to go

但那意味着它必须被禁止。

dontKnows.add(new Foo()); // NOT OK
dontKnows.add(new Bar()); // NOT OK

because the variable dontKnows does't know what types are legal.

因为变量dontknow不知道什么类型是合法的。

#12


0  

List < Object > is meant to pass input type parameter of an Object. While List < ? > represents Wildcard type. The wildcard < ? > is of Unknown parameter type. The wildcard cannot be used as a type argument for a generic method and cannot be used to create a generic instance of a class. Wildcard can be used to extend a subtype class, List < ? extends Number >. To relax the restriction of an Object type and in this case to relax "Number" Object type.

List < Object >是指传递对象的输入类型参数。当列表< ?>表示通配符类型。通配符< ?>是未知的参数类型。通配符不能用作泛型方法的类型参数,不能用于创建类的通用实例。通配符可用于扩展子类List < ?扩展数量>。放宽对象类型的限制,在本例中放宽“Number”对象类型。

#1


98  

As the other posts have noted, you're asking about a Java feature called generics. In C++, this is called templates. The Java beasties are far tamer to deal with.

正如其他帖子所提到的,您正在询问一个名为泛型的Java特性。在c++中,这称为模板。要处理Java小动物要温和得多。

Let me answer your questions functionally (if that's not a naughty word for OO discussions).

让我在功能上回答你的问题(如果这对OO讨论来说不是一个顽皮的词)。

Before generics, you had good old concrete classes like Vector.

在泛型之前,您有很好的旧的具体类,比如Vector。

Vector V = new Vector();

Vectors hold any old object you give them.

向量持有你给它们的任何旧物体。

V.add("This is an element");
V.add(new Integer(2));
v.add(new Hashtable());

However, they do this by casting everything you give it into an Object (the root of all Java classes). That's OK until you attempt to retrieve the values stored in your Vector. When you do, you need to cast the value back into the original class (if you want to do anything meaningful with it).

但是,他们通过将所有的东西都转换成一个对象(所有Java类的根)来实现这一点。在尝试检索存储在向量中的值之前,这是可以的。当您这样做时,您需要将值转换回原始类(如果您想对它做任何有意义的事情)。

String s = (String) v.get(0);
Integer i = (Integer) v.get(1);
Hashtable h = (Hashtable) v.get(2);

Casting gets pretty old fast. More than that, the compiler whines at you about unchecked casts. For a vivid example of this, use the XML-RPC library from Apache (version 2 anyway). The most important problem with this is that consumers of your Vector have to know the exact class of its values at compile time in order to cast correctly. In cases where the producer of the Vector and the consumer are completely isolated from each other, this can be a fatal issue.

演员阵容变老得很快。不仅如此,编译器还会对您抱怨未检查的强制类型转换。对于一个生动的例子,使用Apache的XML-RPC库(无论如何是版本2)。最重要的问题是,向量的使用者必须在编译时知道它的值的确切类,以便正确地转换。在生产者和消费者完全隔离的情况下,这可能是一个致命的问题。

Enter generics. Generics attempt to create strongly typed classes to do generic operations.

进入仿制药。泛型尝试创建强类型类来执行泛型操作。

ArrayList<String> aList = new ArrayList<String>();
aList.add("One");
String element = aList.get(0); // no cast needed
System.out.println("Got one: " + element); 

Now, if you take a look at the infamous gang of four's Design Patterns book, you'll notice that there is some wisdom in divorcing variables from their implementing class. It's better to think of contracts rather than implementation. So, you might say that all List objects do the same things: add(), get(), size(), etc. However, there are many implementations of List operations that may choose to obey the contract in various ways (e.g. ArrayList). However, the type of data these object deal with is left as a runtime consideration to you, the user of the generic class. Put it all together and you'll see the following line of code very frequently:

现在,如果您查看声名狼藉的“*”设计模式书籍,您将注意到,将变量与实现类分离是有一些智慧的。最好是考虑契约而不是执行。因此,您可能会说所有的列表对象都做相同的事情:add()、get()、size()等。但是,有许多列表操作的实现可以选择以各种方式执行契约(例如ArrayList)。但是,这些对象处理的数据类型留给您作为泛型类的用户作为运行时考虑因素。把它们放在一起,你会经常看到下面一行代码:

List<String> L = new ArrayList<String>();

You should read that as "L is a kind of List that deals with String objects". When you start dealing with Factory classes, it is critical to deal with contracts rather than specific implementations. Factories produce objects of various types at runtime.

你应该把它读成“L是一种处理字符串对象的列表”。当您开始处理工厂类时,处理契约而不是特定的实现是至关重要的。工厂在运行时生成各种类型的对象。

Using generics is pretty easy (most of the time). However, one awful day you may decide you want to implement a generic class. Perhaps you've thought of a great new List implementation. When you define that class, you use <t> as a placeholder for the kind of object that will be manipulated by the methods. If you're confused, use the generic classes for List until you're comfortable. Then, you can dive into the implementation with a bit more confidence. Or you can look at the source code for the various List classes that ship with the JRE. Open source is great that way.

使用泛型是相当容易的(大部分时间)。然而,有一天你可能会决定要实现一个泛型类。也许您已经想到了一个很棒的新列表实现。当您定义该类时,您使用 作为方法将操作的对象类型的占位符。如果您感到困惑,请使用列表的泛型类,直到您感到舒服为止。然后,您可以更加自信地深入实现。或者您可以查看与JRE一起发布的各种列表类的源代码。开源就是这样。

Have a look at the Oracle/Sun docs about generics. Cheers.

看看Oracle/Sun关于泛型的文档。欢呼。

#2


39  

In my own simple terms:

用我自己简单的话说:

List

列表

Would declare an ordinary collection, can hold any type, and will always return Object.

将声明一个普通集合,可以保存任何类型,并将始终返回对象。

List<Object>

列表 <对象>

Will create a list that can hold any type of object, but can only get assigned a another List<Object>

将创建一个可以容纳任何类型对象的列表,但只能获得另一个列表< object >

For instance this doesn't work;

例如,这行不通;

List<Object> l = new ArrayList<String>();

Of course you can add anything but only can pull Object.

当然你可以添加任何东西,但只能拖动对象。

List<Object> l = new ArrayList<Object>();

l.add( new Employee() );
l.add( new String() );

Object o = l.get( 0 );
Object o2 = l.get( 1 );

Finally

最后

List<?>

List < ?

Will let you assign any type, including

会让你分配任何类型,包括?

List <?> l = new ArrayList(); 
List <?> l2 = new ArrayList<String>();

This would be called collection of unknown and since the common denominator of unknown is Object you will be able to fetch Objects ( a coincidence )

这将被称为未知量集合,因为未知量的公分母是Object你将能够获取对象(巧合)

The importance of unknown comes when its used with subclassing:

当它与子类化一起使用时,未知的重要性就显现出来了:

List<? extends Collection> l = new ArrayList<TreeSet>(); // compiles

List<? extends Collection> l = new ArrayList<String>(); // doesn't,
// because String is not part of *Collection* inheritance tree. 

I hope using Collection as the type doesn't create confusion, that was the only tree that came to my mind.

我希望使用集合作为类型不会造成混乱,这是我唯一想到的树。

The difference here, is that l is a collection of unknow that belongs to the Collection hierarchy.

这里的区别是,l是属于集合层次结构的未知集合。

#3


15  

I refer you to the excellent Java Generics tutorial, and the "advanced" Generics tutorial, both available from Sun Microsystems. Another great resource is the Java Generics and Collections book.

我将向您介绍优秀的Java泛型教程和“高级”泛型教程,它们都可以从Sun Microsystems获得。另一个伟大的资源是Java泛型和集合书籍。

#4


15  

To add to the already good answers here:

在这里加上已经很好的答案:

Method arguments:

方法参数:

List<? extends Foo>

列表< ?扩展Foo >

good choice if you don't intend to alter the list, and only care that everything in the list is assignable to type 'Foo'. This way, the caller can pass in a List<FooSubclass> and your method works. Usually the best choice.

如果您不打算更改列表,并且只关心列表中的所有内容都可以指定为“Foo”类型,那么这是一个很好的选择。这样,调用者可以传入一个列表 ,您的方法就可以工作了。通常是最好的选择。

List<Foo>

< Foo >列表

good choice if you intend to add Foo objects to the list in your method. The caller may not pass in a List<FooSubclass>, as you intend to add a Foo to the List.

如果您打算将Foo对象添加到方法中的列表,那么这是一个很好的选择。调用者可能不会传入列表 ,因为您打算向列表添加一个Foo。

List<? super Foo>

列表< ?超级Foo >

good choice if you intend to add Foo objects to the list, and it's not important what else is in the list (ie, you are ok getting a List<Object> that contains a 'Dog' that has nothing to do with Foo).

如果您打算将Foo对象添加到列表中,并且列表中的其他内容并不重要(例如,您可以获得一个包含与Foo无关的'Dog'的列表)。

Method return values

方法返回值

just like method arguments, but with the benefits reversed.

就像方法参数一样,但是好处是相反的。

List<? extends Foo>

列表< ?扩展Foo >

Guarantees that everything in the returned List has type 'Foo'. It might be List<FooSubclass> though. Caller cannot add to the List. This is your go-to choice and the most common case by far.

保证返回列表中的所有内容都具有“Foo”类型。它可能是List< foo子类>。调用者不能添加到列表中。这是你的首选,也是目前最常见的情况。

List<Foo>

< Foo >列表

Just like List<? extends Foo> but also allows the caller to add to the List. Less common.

就像列表< ?扩展Foo>,但也允许调用者添加到列表。不太常见的。

List<? super Foo>

列表< ?超级Foo >

allows the caller to add Foo objects to the List, but does not guarantee what will be returned from list.get(0)... it could be anything from Foo to Object. The only guarantee is that this won't be a list of 'Dog' or some other choice that would prevent list.add(foo) from being legal. Very rare use case.

允许调用者向列表中添加Foo对象,但不保证从列表中返回什么。它可以是任何东西,从Foo到Object。唯一的保证是,这不会是一个“狗”的列表,也不会是其他阻止list.add(foo)合法的选项。非常罕见的用例。

I hope that helps. Good luck!

我希望有帮助。好运!

ps. To sum up... two questions...

ps。总之…两个问题…

do you need to add to the List? Do you care what is in the list?

你需要添加到列表中吗?你关心列表里有什么吗?

yes yes - use List<Foo>.

是的,使用列表

yes no - use List<? super Foo>.

是否-使用列表 。

no yes - use <? extends Foo> --- most common.

no yes - use ——最常见的。

no no - use <?>.

不使用 。

#5


4  

Simplest explanation which is not "RTFM":

最简单的解释不是“RTFM”:

List

Will generate lots of compiler warnings, but is mostly equivalent to:

会产生许多编译器警告,但主要相当于:

List<Object>

While:

而:

List<?>

basically means its something generic, but you don't know what the generic type is. Its great for getting rid of compiler warnings when you cant modify the return types of other things that just returned List. Its much more useful in the form:

它的基本意思是泛型,但你不知道泛型类型是什么。当您不能修改刚刚返回列表的其他东西的返回类型时,它可以很好地消除编译器警告。它更有用的形式是:

List<? extends SomeOtherThing>

#6


4  

I will try to answer this in detail. Before generics we were having only List (a raw list) and it can hold almost anything we can think of.

我会尽量详细地回答这个问题。在泛型出现之前,我们只有一个列表(一个原始列表),它可以容纳几乎所有我们能想到的东西。

List rawList = new ArrayList();
rawList.add("String Item");
rawList.add(new Car("VW"));
rawList.add(new Runnable() {
            @Override
            public void run() {
               // do some work.
            }
        });

The major problem with the raw list is when we want to get any element out of such list it can only guarantee that it would be Object and for that reason we need to use casting as:

原始列表的主要问题是,当我们想从列表中获取任何元素时,它只能保证它是对象,因此我们需要使用角色转换:

   Object item = rawList.get(0); // we get object without casting.
   String sameItem = (String) rawList.get(0); // we can use casting which may fail at runtime.

So conclusion is a List can store Object (almost everything is Object in Java) and always returns an Object.

结论是列表可以存储对象(在Java中几乎所有东西都是对象),并且总是返回一个对象。

Generics

Now lets talk about generics. Consider the following example:

现在我们来谈谈泛型。考虑下面的例子:

List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
stringsList.add(new Car("Fiat")); //error
String stringItem = stringsList.get(0);

In the above case we cannot insert anything other than String in stringsList as Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. And we get error when we try to insert a Car instance in it. Also it eliminates cast as you can check when we invoke get method. Check this link for understanding why we should use generics.

在上面的例子中,我们不能在stringsList中插入除了字符串之外的任何东西,因为Java编译器对通用代码应用了强类型检查,如果代码违反了类型安全,就会发出错误。当我们试图插入一个汽车实例时,我们会得到错误。它还消除了cast,您可以在调用get方法时进行检查。检查这个链接以了解为什么我们应该使用泛型。

List<Object>

If you read about type erasure then you will understand that List<String>, List<Long>, List<Animal> etc. will be having different static types at compile time but will have same dynamic type List at run time.

如果您阅读了关于类型擦除的内容,您就会了解列表 ,列表 , List 等将在编译时具有不同的静态类型,但在运行时将具有相同的动态类型列表。

If we have List<Object> then it can store only Object in it and almost everything is Object in Java. So we can have:

如果我们有List,那么它只能存储其中的对象,几乎所有的东西都是Java中的对象。所以我们可以有:

 List<Object> objectList = new ArrayList<Object>();
 objectList.add("String Item");
 objectList.add(new Car("VW"));
 objectList.add(new Runnable() {
        @Override
        public void run() {

        }
 });
 Object item = objectList.get(0); // we get object without casting as list contains Object
 String sameItem = (String) objectList.get(0); // we can use casting which may fail at runtime.

It seems List<Object> and List are same but actually they are not. Consider the following case:

List和List看起来是一样的,其实不是。考虑下面的例子:

List<String> tempStringList = new ArrayList<>();
rawList = tempStringList; // Ok as we can assign any list to raw list.
objectList = tempStringList; // error as List<String> is not subtype of List<Obejct> becuase generics are not convariant.

You can see we can assign any list to raw list and major reason for that is to allow backward compatibility. Also List<String> will be converted to List at run time due to type erasure and assignment will be fine anyways.

可以看到,我们可以为原始列表分配任何列表,主要原因是允许向后兼容。此外,List 将在运行时转换为List,因为类型擦除和赋值无论如何都很好。

But List<Object> means it can only refer to a list of objects and can also store objects only. Even though String is subtype of Object we cannot assign List<String> to List<Object> as generics are not covariant like arrays. They are invariant. Also check this link for more. Also check the difference between List and List<Object> in this question.

而List表示只能引用对象列表,也只能存储对象。即使字符串是对象的子类型,我们也不能将List 赋给List ,因为泛型不像数组那样是协变的。他们是不变的。也检查这个链接更多。还要检查列表和列表之间的区别。

List<?>

Now we are left with List<?> which basically means list of unknown type and can refer to any list.

现在剩下List 基本上是指未知类型的列表,可以引用任何列表。

List<?> crazyList = new ArrayList<String>();
 List<String> stringsList = new ArrayList<>();
 stringsList.add("Apple");
 stringsList.add("Ball");
 crazyList = stringsList; // fine

The character ? is known as wildcard and List<?> is a list of unbounded wildcard. There are certain points to observe now.

这个角色?被称为通配符和列表 是一个*通配符的列表。现在有几个要点值得注意。

We cannot instantiate this list as the following code will not compile:

我们不能实例化这个列表,因为以下代码不会编译:

List<?> crazyList = new ArrayList<?>(); // any list.

We can say a wildcard parameterized type is more like an interface type as we can use it to refer to an object of compatible type but not for itself.

我们可以说通配符参数化类型更像接口类型,因为我们可以使用它来引用兼容类型的对象,但不能引用它本身。

List<?> crazyList2 = new ArrayList<String>();

We cannot insert any item to it as we don't know what actually the type would be.

我们不能插入任何项,因为我们不知道类型是什么。

crazyList2.add("Apple"); // error as you dont actually know what is that type.

Now question arises When would I want to use List<?>?

现在问题来了,我想什么时候使用List ?

You can think of this as a read-only list where you don't care about the type of the items. You can use it to invoke methods like returning the length of the list, printing it etc.

您可以将其视为一个只读列表,其中不关心项的类型。您可以使用它来调用方法,例如返回列表的长度、打印它等等。

 public static void print(List<?> list){
        System.out.println(list);
    }

You can also check the difference between List, List<?>, List<T>, List<E>, and List<Object> here.

您还可以检查List和List 列表 ,列表 ,列表

#7


3  

The shortest possible explanation is: The second item is a list that can hold any type, and you can add objects to it:

最简短的解释是:第二项是一个可以容纳任何类型的列表,你可以向其中添加对象:

List<Object>

The first item you list is treated as essentially equivalent to this, except you will get compiler warnings because it is a "raw type".

您列出的第一个项本质上与此相同,但是您将得到编译器警告,因为它是“原始类型”。

List

The third is a list that can hold any type, but you cannot add anything to it:

第三种是可以容纳任何类型的列表,但是不能添加任何内容:

List<?> 

Basically, you use the second form (List<Object>) when you truly have a list that can contain any object and you want to be able to add elements to the list. You use the third form (List<?>)when you receive the list as a method return value and you will iterate over the list but never add anything to it Never use the first form (List) in new code compiling under Java 5 or later.

基本上,当您真正拥有一个可以包含任何对象的列表时,您将使用第二个表单(List),您希望能够将元素添加到列表中。当您将列表作为方法返回值接收时,您将使用第三个表单(List ),并且您将对列表进行迭代,但从不向它添加任何内容,永远不要在Java 5或更高版本的新代码编译中使用第一个表单(List)。

#8


1  

I'd put it this way: While List and List<Object> can contain any type of objects, List<?> contains elements of an unknown type, but once that type is captured, it can only contain elements of that type. Which is why it is the only type safe variant of those three, and therefore generally preferable.

我这样写:List和List可以包含任何类型的对象,List 包含未知类型的元素,但一旦捕获该类型,它只能包含该类型的元素。这就是为什么它是这三种类型中唯一的类型安全变体,因此通常更可取。

#9


0  

To complement the tutorials mentioned by Rob, here's a wikibook explaining the topic:
http://en.wikibooks.org/wiki/Java_Programming/Generics

为了补充Rob提到的教程,这里有一本wikibook来解释这个主题:http://en.wikibooks.org/wiki/Java_Programming/Generics


Edit:

编辑:

  1. No restrictions on type of items in list

    对列表中的项目类型没有限制

  2. Items in list must extend Object

    列表中的项目必须扩展对象

  3. Wildcard used by itself, so it matches anything

    通配符本身使用,因此它匹配任何东西

Would it be naive of me to conclude at this point that there's hardly any/no difference at all?

在这一点上,我是否天真地认为几乎没有任何区别?

#10


0  

When would I want to use

我想什么时候用

public void CanYouGiveMeAnAnswer( List l ){}

When you cant to do all the casting your self.

当你不能做所有的选择时。

When would I want to use

我想什么时候用

public void CanYouGiveMeAnAnswer( List l<Object> ){}

When you want to restrict the type of the List. For instance, this would be an invalid argument.

当您想要限制列表的类型时。例如,这是一个无效的参数。

 new ArrayList<String>();

When would I want to use

我想什么时候用

public void CanYouGiveMeAnAnswer( List l<?> ){}

Mostly never.

主要是永远。

#11


0  

List, List<?>, and List<? extends Object> are the same thing. The second is more explicit. For a list of this type, you cannot know what types are legal to put into it, and you don't know anything about the types you can get out of it, except that they will be objects.

列表,列表< ?列表>,< ?扩展对象>是一样的。第二个更明确。对于这种类型的列表,您不知道将哪些类型放入其中是合法的,您也不知道可以从中获得哪些类型,除了它们将是对象。

List<Object> specifically means that the list contains any sort of object.

List明确表示该列表包含任何类型的对象。

Let's say we make a list of Foo:

假设我们做了一个Foo列表

List<Foo> foos= new ArrayList<Foo>();

It is not legal to put a Bar into foos.

在foos中设置栏是不合法的。

foos.add(new Bar()); // NOT OK!

It is always legal to put anything into a List<Object>.

将任何内容放入列表始终是合法的。

List<Object> objs = new ArrayList<Object>();
objs.add(new Foo());
objs.add(new Bar());

But you mustn't be allowed to put a Bar into a List<Foo> - that's the whole point. So that means that this:

但是你不能把一个条放到一个列表中,这就是全部的要点。这意味着

List<Object> objs = foos; // NOT OK!

is not legal.

是不合法的。

But it's ok to say that foos is a list of something but we don't know specifically what it is:

但可以这么说,foos是一个列表但我们不知道具体是什么

List<?> dontKnows = foos;

But that then means that it must be prohibited to go

但那意味着它必须被禁止。

dontKnows.add(new Foo()); // NOT OK
dontKnows.add(new Bar()); // NOT OK

because the variable dontKnows does't know what types are legal.

因为变量dontknow不知道什么类型是合法的。

#12


0  

List < Object > is meant to pass input type parameter of an Object. While List < ? > represents Wildcard type. The wildcard < ? > is of Unknown parameter type. The wildcard cannot be used as a type argument for a generic method and cannot be used to create a generic instance of a class. Wildcard can be used to extend a subtype class, List < ? extends Number >. To relax the restriction of an Object type and in this case to relax "Number" Object type.

List < Object >是指传递对象的输入类型参数。当列表< ?>表示通配符类型。通配符< ?>是未知的参数类型。通配符不能用作泛型方法的类型参数,不能用于创建类的通用实例。通配符可用于扩展子类List < ?扩展数量>。放宽对象类型的限制,在本例中放宽“Number”对象类型。