有人可以尽可能详细地解释以下类型之间的差异吗?
List
List<Object>
List<?>
Run Code Online (Sandbox Code Playgroud)
让我更具体一点.我什么时候想用
// 1
public void CanYouGiveMeAnAnswer(List l) { }
// 2
public void CanYouGiveMeAnAnswer(List<Object> l) { }
// 3
public void CanYouGiveMeAnAnswer(List<?> l) { }
Run Code Online (Sandbox Code Playgroud)
jjo*_*ohn 102
正如其他帖子所指出的那样,您要问的是一个名为泛型的Java功能.在C++中,这称为模板.Java野兽的处理方式更加温和.
让我在功能上回答你的问题(如果这不是OO讨论的顽皮词).
在泛型之前,你有像Vector这样的旧混凝土类.
Vector V = new Vector();
Run Code Online (Sandbox Code Playgroud)
向量包含您给它们的任何旧对象.
V.add("This is an element");
V.add(new Integer(2));
v.add(new Hashtable());
Run Code Online (Sandbox Code Playgroud)
但是,他们通过将您提供的所有内容都转换为Object(所有Java类的根目录)来实现此目的.在您尝试检索存储在Vector中的值之前,这没关系.当你这样做时,你需要将值转换回原始类(如果你想用它做任何有意义的事情).
String s = (String) v.get(0);
Integer i = (Integer) v.get(1);
Hashtable h = (Hashtable) v.get(2);
Run Code Online (Sandbox Code Playgroud)
铸造变得非常快.更重要的是,编译器向你抱怨未经检查的强制转换.有关这方面的生动示例,请使用Apache的XML-RPC库(无论如何都是版本2).最重要的问题是,Vector的使用者必须在编译时知道其值的确切类,才能正确转换.如果Vector的生产者和消费者彼此完全隔离,这可能是一个致命的问题.
输入泛型.泛型尝试创建强类型类来执行泛型操作.
ArrayList<String> aList = new ArrayList<String>();
aList.add("One");
String element = aList.get(0); // no cast needed
System.out.println("Got one: " + element);
Run Code Online (Sandbox Code Playgroud)
现在,如果你看看臭名昭着的四人组合设计模式书,你会注意到从他们的实现类中脱离变量有一些智慧.考虑合同而不是实施更好.所以,你可能会说,所有清单对象做同样的事情:add(),get(),size()等.然而,也有列表的操作是可以选择服从以各种方式(如合同的许多实现ArrayList).但是,这些对象处理的数据类型将作为运行时考虑因素留给您(泛型类的用户).把它们放在一起,你会经常看到以下代码行:
List<String> L = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)
您应该读作"L是一种处理String对象的List".当您开始处理Factory类时,处理合同而不是特定的实现是至关重要的.工厂在运行时生成各种类型的对象.
使用泛型很容易(大多数时候).但是,有一天你可能决定要实现泛型类.也许您已经想到了一个很棒的新List实现.定义该类时,可以将其<t>用作将由方法操作的对象类型的占位符.如果您感到困惑,请使用List的通用类,直到您感到舒服为止.然后,您可以更自信地深入了解实施.或者,您可以查看JRE附带的各种List类的源代码.开源很棒.
Osc*_*Ryz 40
用我自己的简单术语:
名单
将声明一个普通的集合,可以保存任何类型,并将始终返回Object.
列表<对象>
将创建一个可以容纳任何类型对象的列表,但只能分配另一个List <Object>
例如,这不起作用;
List<Object> l = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)
当然你可以添加任何东西,但只能拉对象.
List<Object> l = new ArrayList<Object>();
l.add( new Employee() );
l.add( new String() );
Object o = l.get( 0 );
Object o2 = l.get( 1 );
Run Code Online (Sandbox Code Playgroud)
最后
名单<?>
会让你指定任何类型,包括
List <?> l = new ArrayList();
List <?> l2 = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)
这被称为收集未知以来的共同点未知的对象,你就能获取对象(巧合)
当它与子类化一起使用时,未知的重要性来自:
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.
Run Code Online (Sandbox Code Playgroud)
我希望使用Collection作为类型不会产生混淆,这是我想到的唯一一棵树.
这里的不同之处在于l是属于Collection层次结构的unknow 集合.
Rob*_*Rob 15
我将向您介绍优秀的Java Generics教程,以及Sun Microsystems提供的"高级"泛型教程.另一个很好的资源是Java Generics and Collections一书.
Eri*_*uer 15
要在这里添加已经很好的答案:
方法参数:
List<? extends Foo>
如果您不打算更改列表,那么这是一个很好的选择,只关心列表中的所有内容都可以分配给'Foo'.这样,调用者可以传入List <FooSubclass>并且您的方法可以正常工作.通常是最好的选择.
List<Foo>
如果您打算将Foo对象添加到方法的列表中,那么这是一个不错的选择.调用者可能无法传入List <FooSubclass>,因为您打算将Foo添加到List中.
List<? super Foo>
如果你打算将Foo对象添加到列表中,这是一个很好的选择,并且列表中的其他内容并不重要(例如,您可以获得包含与Foo无关的'Dog'的List <Object>).
方法返回值
就像方法论证一样,但好处却逆转了.
List<? extends Foo>
保证返回List中的所有内容都有'Foo'类型.它可能是List <FooSubclass>.来电者无法添加到列表中.这是您的首选,也是迄今为止最常见的案例.
List<Foo>
就像List <?扩展Foo>但也允许调用者添加到List.不常见.
List<? super Foo>
允许调用者将Foo对象添加到List中,但不保证将从list.get(0)返回的内容......它可以是从Foo到Object的任何内容.唯一的保证是这不会是"狗"列表或其他一些阻止list.add(foo)合法的选择.非常罕见的用例.
我希望有所帮助.祝好运!
PS.总结......两个问题......
你需要添加到列表?你关心列表中的内容吗?
是是 - 使用List <Foo>.
是否 - 使用清单<?超级富豪>.
不是 - 使用<?扩展Foo> ---最常见.
不能 - 使用<?>.
我会尽量详细回答这个问题。在泛型之前,我们只有List(一个原始列表),它几乎可以容纳我们能想到的任何东西。
List rawList = new ArrayList();
rawList.add("String Item");
rawList.add(new Car("VW"));
rawList.add(new Runnable() {
@Override
public void run() {
// do some work.
}
});
Run Code Online (Sandbox Code Playgroud)
原始列表的主要问题是当我们想要从这样的列表中获取任何元素时,它只能保证它会是Object这样,因此我们需要使用强制转换为:
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.
Run Code Online (Sandbox Code Playgroud)
所以结论是 List可以存储的对象(Java 中几乎所有东西都是对象)并且总是返回一个对象。
现在让我们谈谈泛型。考虑以下示例:
List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
stringsList.add(new Car("Fiat")); //error
String stringItem = stringsList.get(0);
Run Code Online (Sandbox Code Playgroud)
在上述情况下,我们不能插入除Stringin之外的任何内容,stringsList因为 Java 编译器对泛型代码应用强类型检查,如果代码违反类型安全,则会发出错误。当我们尝试在其中插入一个Car实例时会出错。它还消除了强制转换,因为您可以在我们invoke获取方法时检查。检查此链接以了解为什么我们应该使用泛型。
List<Object>如果您阅读有关类型擦除的内容,那么您将了解List<String>, List<Long>, List<Animal>等在编译时将具有不同的静态类型,但List在运行时将具有相同的动态类型。
如果我们有,List<Object>那么它只能存储Object在其中,并且几乎所有内容都Object在 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.
Run Code Online (Sandbox Code Playgroud)
看起来List<Object>和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.
Run Code Online (Sandbox Code Playgroud)
您可以看到我们可以将任何列表分配给原始列表,主要原因是允许向后兼容。由于类型擦除,也List<String>将List在运行时转换为,无论如何分配都可以。
但是List<Object>意味着它只能引用一个对象列表,也只能存储对象。即使String是子类型Object,我们不能分配List<String>给List<Object>泛型不是协变的像阵列。它们是不变的。另请查看此链接以获取更多信息。还要检查这个问题中List和之间的区别。List<Object>
List<?>现在我们剩下List<?>which 基本上意味着未知类型的列表,可以引用任何列表。
List<?> crazyList = new ArrayList<String>();
List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
crazyList = stringsList; // fine
Run Code Online (Sandbox Code Playgroud)
该字符?称为通配符,List<?>是无界通配符列表。现在有一些需要注意的地方。
我们无法实例化此列表,因为以下代码将无法编译:
List<?> crazyList = new ArrayList<?>(); // any list.
Run Code Online (Sandbox Code Playgroud)
我们可以说通配符参数化类型更像是接口类型,因为我们可以使用它来引用兼容类型的对象,但不能引用它本身。
List<?> crazyList2 = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)
我们无法向其中插入任何项目,因为我们不知道实际类型是什么。
crazyList2.add("Apple"); // error as you dont actually know what is that type.
Run Code Online (Sandbox Code Playgroud)
现在问题出现了我什么时候想使用List<?>?
您可以将其视为只读列表,其中您不关心项目的类型。您可以使用它来调用方法,例如返回列表的长度、打印它等。
public static void print(List<?> list){
System.out.println(list);
}
Run Code Online (Sandbox Code Playgroud)
您还可以在List, List<?>, List<T>, List<E>, and List<Object> 此处查看 之间的区别。
| 归档时间: |
|
| 查看次数: |
37067 次 |
| 最近记录: |