Java Generics: List, List<Object>, List<>

Difference between List, List ? , List T , List E , and List Object

1) Correct

2) You can think of that one as "read only" list, where you don't care about the type of the items.Could e.g. be used by a method that is returning the length of the list.

3) T, E and U are the same, but people tend to use e.g. T for type, E for Element, V for value and K for key. The method that compiles says that it took an array of a certain type, and returns an array of the same type.

4) You can't mix oranges and apples. You would be able to add an Object to your String list if you could pass a string list to a method that expects object lists. (And not all objects are strings)

List List ? and List List are incompatible types in java

Okay, so this is due to a subtle semantic difference.

List

This is the raw type of List, which equates to T being of type Object. So it's the same as saying:

List<Object>

Now, the Compiler knows for a fact, that whatever happens, this is a subclass of type Object. And if you do..

List myList = new ArrayList();
myList.add(new Object());

It will work fine! This is because Object is the same or it is some derivation of the type.

List<?>

This is literally a list of unknown (Java Docs). We don't even know that the subclass of the things in here are of type Object. In fact, the ? type is an unknown type all on its own. It has nothing to do with Object! This is why when you try and do..

List<?> myList = new ArrayList<?>();
myList.add(new Object());

You get a compile time error!

List Object variable being assignment compatible with other generic Lists like List String in Java

You are right that the types are not assignment compatible.

In doubt, this can easily be verified:

List<Object> a = null;
List<NamePhone> b = null;
a = b; // Error!

The reason of why it seems to be assignment compatible in this case is the target type inference. The inference process can be complicated - particularly in this case, which involves a Collector, which has three type parameters.

I'll try to flesh out the relevant parts here:

The signature of the collect method is as follows:

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

This is called on the Stream<T> instance. In your case, this is a Stream<NamePhone>. But note that the method itself has additional generic parameters, namely R and A. The relevant one here is R, which is the return type.

The Collector that is passed in there is the one created by the toList method, which looks as follows:

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

It is also generic. The type parameter will basically be "substituted", based on the context in which the method is called.

So when you write this:

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

then you will have the following type assignments:

  1. The T of the Stream is NamePhone
  2. The T of the Collector is NamePhone
  3. The R of the collect method is List<NamePhone>

But you could also write

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

In this case

  1. The T of the Stream is NamePhone
  2. The T of the Collector is Object
  3. The R of the collect method is List<Object>

Note that this is only possible because the collect method accepts a Collector<? super T, ...>. It would not work if it expected a Collector<T, ...>. This basically means that you can use the elements from the Stream and collect them into a new List, as long as the type parameter of the desired list is a supertype of the elements in the stream.


Conceptually, this makes sense, because it's in some way analogous to

List<Integer> integers = ...;
List<Number> numbers = ...;
for (Integer i : integers) numbers.add(i); // This should work as well!

How to have Java method return generic list of any type?

private Object actuallyT;

public <T> List<T> magicalListGetter(Class<T> klazz) {
List<T> list = new ArrayList<>();
list.add(klazz.cast(actuallyT));
try {
list.add(klazz.getConstructor().newInstance()); // If default constructor
} ...
return list;
}

One can give a generic type parameter to a method too. You have correctly deduced that one needs the correct class instance, to create things (klazz.getConstructor().newInstance()).

Get generic type of java.util.List

If those are actually fields of a certain class, then you can get them with a little help of reflection:

package test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test {

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

public static void main(String... args) throws Exception {
Field stringListField = Test.class.getDeclaredField("stringList");
ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
System.out.println(stringListClass); // class java.lang.String.

Field integerListField = Test.class.getDeclaredField("integerList");
ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
System.out.println(integerListClass); // class java.lang.Integer.
}
}

You can also do that for parameter types and return type of methods.

But if they're inside the same scope of the class/method where you need to know about them, then there's no point of knowing them, because you already have declared them yourself.

Adding Object to Generic List with two types

List<SubClaz> is not a subtype of List<SuperClaz> in Java. That's why the wildcards are used: List<SubClaz> is a subtype of List<? extends SuperClaz>.

Now for your A<B<?>> abcv=new A<B<String>>(); example:

By adding the wildcard, you're making B<String> a subtype of B<?>, but since these are also wrapped by another type A, we're back to the first problem:

A<B<String>> is not a subtype of A<B<?>>

(Notice B<?> is the SuperClaz and B<String> is the SubClaz in this case).

You can fix this the same way; by adding another wildcard:

A<B<String>>() is a subtype of A<? extends B<?>>.

Keep in mind that this doesn't allow you to read or manipulate the list as you want. Search for covariance and contravariance for more detail. Here is a good one: http://bayou.io/draft/Capturing_Wildcards.html

Using and declaring generic List T

You should either have a generic class or a generic method like below:

public class Test<T>  {
List<T> list = new ArrayList<T>();
public Test(){

}
public void populate(T t){
list.add(t);
}
public static void main(String[] args) {
new Test<String>().populate("abc");
}
}


Related Topics



Leave a reply



Submit