繁体   English   中英

名单 <Object> 变量与其他通用列表(如List)兼容 <String> 在Java中

[英]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> 但请注意, 该方法本身具有其他通用参数,即RA 这里相关的是R ,它是返回类型

传入的Collector是由toList方法创建的Collector ,如下所示:

public static <T> Collector<T, ?, List<T>> toList()

它也是通用的。 类型参数基本上将被“替换”,基于调用该方法的上下文

所以当你写这个:

List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());

那么你将有以下类型的作业:

  1. StreamTNamePhone
  2. CollectorTNamePhone
  3. collect方法的RList<NamePhone>

但你也可以写

List<Object> npList = nameAndPhone.collect(Collectors.toList());

在这种情况下

  1. StreamTNamePhone
  2. CollectorTObject
  3. collect方法的RList<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.

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