Переменная List <Object>, совместимая по присваиванию с другими общими списками, такими как List <String>в Java - PullRequest
0 голосов
/ 23 мая 2018

Последние несколько дней я пытался разобраться в java-дженериках.Из того, что я понимаю, дженерики Java не являются ковариантными, поэтому List<Object> не совместим по присваиванию с другими дженериками List s

Но здесь, в следующей программе, метод nameAndPhone.collect() возвращает список типа List<NamePhone>и когда я заменяю ссылочную переменную List<NamePhone> npList на List<Object> 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());
    }
}

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Вы правы, что типы не совместимы с присвоением.

В сомнении, это легко проверить:

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, который выглядит следующим образом:

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

Он также является общим.Параметр типа в основном будет «замещен» на основе context , в котором вызывается метод.

Поэтому, когда вы напишите это:

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

, тогда у вас будут следующие назначения типа:

  1. T из Stream равно NamePhone
  2. T Collector равно NamePhone
  3. R collect метода равно List<NamePhone>

Но вы также можете написать

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

В этом случае

  1. T из Stream равно NamePhone
  2. T изCollector равно Object
  3. R метода collect равно List<Object>

Обратите внимание, что это возможно только потому, что метод collect принимаетCollector<? super T, ...>.Это не сработало бы, если бы оно ожидало Collector<T, ...>.Это в основном означает, что вы можете использовать элементы из Stream и собрать их в новый List, если параметр типа нужного списка представляет собой супертип элементов в потоке.


Концептуально, это имеет смысл, потому что это в некотором роде аналогично

List<Integer> integers = ...;
List<Number> numbers = ...;
for (Integer i : integers) numbers.add(i); // This should work as well!
0 голосов
/ 23 мая 2018

Параметр типа того, что возвращается методом collect , не обязательно должен быть того же типа, что и поток.Здесь тип результата R отличается от типа потока T.

<R,A> R collect​(Collector<? super T,A,R> collector)

Далее, Java 8 и более поздние версии имеют улучшенный вывод целевого типа.Это означает, что компилятор будет использовать целевой тип для вывода параметров типа.В этом случае, когда у вас есть

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

, компилятор видит NamePhone и выводит этот тип в качестве параметра типа от R до collect (и до 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 в любом из них.случай.

...