[英]List<Object> variable being assignment compatible with other generic Lists like List<String> in Java
在过去的几天里,我一直试图了解java泛型。 根据我的理解,Java泛型不是协变的,因此List<Object>
不与其他泛型List
s分配兼容
但是在下面的程序中, nameAndPhone.collect()
方法返回List<NamePhone>
类型的List<NamePhone>
,当我用List<Object> npList
替换引用变量List<NamePhone> npList
,程序仍然编译而没有警告。
我尝试使用类似的方法返回List<String>
,并使用List<Object>
引用变量不会导致任何错误。
为什么List<Object>
赋值与List<NamePhone>
兼容?
import java.util.*;
import java.util.stream.*;
class NamePhoneEmail
{
String name;
String phonenum;
String email;
NamePhoneEmail(String n, String p, String e)
{
name = n;
phonenum = p;
email = e;
}
}
class NamePhone
{
String name;
String phonenum;
NamePhone(String n, String p)
{
name = n;
phonenum = p;
}
}
public class CollectDemo
{
public static void main(String[] args)
{
ArrayList<NamePhoneEmail> myList = new ArrayList<>();
myList.add(
new NamePhoneEmail("Larry", "555-5555", "Larry@HerbSchildt.com"));
myList.add(
new NamePhoneEmail("James", "555-4444", "James@HerbSchildt.com"));
Stream<NamePhone> nameAndPhone =
myList.stream().map((a) -> new NamePhone(a.name, a.phonenum));
List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());
}
}
collect
方法返回的类型参数不需要与流类型相同。 这里,结果类型R
与流类型T
。
<R,A> R collect(Collector<? super T,A,R> collector)
接下来,Java 8及更高版本改进了目标类型推断。 这意味着编译器将使用目标类型来推断类型参数。 在这种情况下,当你有
List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());
编译器看到NamePhone
并将该类型推断为要collect
的类型参数R
(以及Collectors.toList()
)。
当你改变它
List<Object> npList = nameAndPhone.collect(Collectors.toList());
编译器看到Object
并将该类型推断为类型参数R
这可以按预期编译和工作,因为您当然可以将任何类型的对象(包括NamePhone
)放入List<Object>
。
这不是List<NamePhone>
与List<Object>
兼容的赋值。 发生的事情是当你说List<Object> npList
,从来没有List<NamePhone>
,只有List<Object>
。
请注意,在任何一种情况下,列表中的对象都将具有运行时类型NamePhone
。
你是对的,类型不兼容。
有疑问,这很容易验证:
List<Object> a = null;
List<NamePhone> b = null;
a = b; // Error!
在这种情况下,它似乎与赋值兼容的原因是目标类型推断 。 推理过程可能很复杂 - 特别是在这种情况下,它涉及一个Collector
,它有三个类型参数。
我会尝试在这里充实相关部分:
collect
方法的签名如下:
<R, A> R collect(Collector<? super T, A, R> collector);
这在Stream<T>
实例上调用。 在您的情况下,这是一个Stream<NamePhone>
。 但请注意, 该方法本身具有其他通用参数,即R
和A
这里相关的是R
,它是返回类型 。
传入的Collector
是由toList
方法创建的Collector
,如下所示:
public static <T> Collector<T, ?, List<T>> toList()
它也是通用的。 类型参数基本上将被“替换”,基于调用该方法的上下文 。
所以当你写这个:
List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());
那么你将有以下类型的作业:
Stream
的T
是NamePhone
Collector
的T
是NamePhone
collect
方法的R
是List<NamePhone>
但你也可以写
List<Object> npList = nameAndPhone.collect(Collectors.toList());
在这种情况下
Stream
的T
是NamePhone
Collector
的T
是Object
collect
方法的R
是List<Object>
请注意,这是唯一可能的,因为collect
方法接受Collector<? super T, ...>
Collector<? super T, ...>
。 如果它期望Collector<T, ...>
,它将无法工作。 这基本上意味着您可以使用Stream
的元素并将它们收集到新的List
,只要所需列表的type参数是流中元素的超类型即可 。
从概念上讲,这是有道理的,因为它在某种程度上类似于
List<Integer> integers = ...;
List<Number> numbers = ...;
for (Integer i : integers) numbers.add(i); // This should work as well!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.