I've been trying to get my head around java generics for the last few days. From what I understand, Java generics are not covariant so List<Object>
is not assignment compatible with other generics List
s
But here in the following program, nameAndPhone.collect()
method returns a List of type List<NamePhone>
and when I replace reference variable List<NamePhone> npList
with List<Object> npList
the program still compiles without warnings.
I tried this with a similar method returning List<String>
as well, and using List<Object>
reference variable did not result in any error.
Why is List<Object>
assignment compatible with List<NamePhone>
here?
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", "[email protected]"));
myList.add(
new NamePhoneEmail("James", "555-4444", "[email protected]"));
Stream<NamePhone> nameAndPhone =
myList.stream().map((a) -> new NamePhone(a.name, a.phonenum));
List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());
}
}
Any Collection can be passed as an argument to the constructor as long as its type extends the type of the ArrayList , as String extends Object . The constructor takes a Collection , but List is a subinterface of Collection , so you can just use the List<String> .
The type parameter of what gets returned by the collect
method does not need to be the same type as the stream. Here, the result type R
is different than the stream type T
.
<R,A> R collect(Collector<? super T,A,R> collector)
Next, Java 8 and later have improved target type inference. That means that the compiler will use the target type to infer type parameters. In this case, when you have
List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());
the compiler sees NamePhone
and infers that type as the type parameter R
to collect
(and to Collectors.toList()
).
When you change it to
List<Object> npList = nameAndPhone.collect(Collectors.toList());
the compiler sees Object
and infers that type as the type parameter R
.
This compiles and works as expected, because you can certainly place any kind of object, including a NamePhone
, into a List<Object>
.
It's not that a List<NamePhone>
is assignment compatible with List<Object>
. What is happening is that when you say List<Object> npList
, there never was a List<NamePhone>
, only a List<Object>
.
Note that the objects in your list will have a runtime type of NamePhone
in either case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With