Domanda

I am trying to understand how Lists.newArrayList() knows the type of list to return. I saw the source code for function newArrayList(), but it simply returns the ArrayList of generic type E.

public static <E> ArrayList<E> newArrayList() {
  return new ArrayList<E>();
}

However, when I call the function, I don't pass any such information.

List<String> testList = Lists.newArrayList();

How does it know what type of ArrayList I want?

I read about generics and TypeToken but could not relate to it through the code.

È stato utile?

Soluzione

Because the compiler can infer the type from the variable declaration.

Example:

List<String> list = Lists.newArrayList(),

the compiler will understand that the type of the collection (the type of E) will be <String>, since you expect to get a List of String.

It was useful to avoid rewriting entire <> arguments, but with Java7 and diamond operator you can avoid it using

List<String> list = new ArrayList<>();

(Imaginate it was a List<List<String>> you should rewrite List<List<String>>)

I found this for you:

The Java compiler takes advantage of target typing to infer the type parameters of a generic method invocation. The target type of an expression is the data type that the Java compiler expects depending on where the expression appears. Consider the method Collections.emptyList, which is declared as follows:

static <T> List<T> emptyList();

Consider the following assignment statement:

List<String> listOne = Collections.emptyList();

This statement is expecting an instance of List; this data type is the target type. Because the method emptyList returns a value of type List, the compiler infers that the type argument T must be the value String. This works in both Java SE 7 and 8. Alternatively, you could use a type witness and specify the value of T as follows:

List<String> listOne = Collections.<String>emptyList();

However, this is not necessary in this context. It was necessary in other contexts, though. Consider the following method:

void processStringList(List<String> stringList) {
    // process stringList
}

Suppose you want to invoke the method processStringList with an empty list. In Java SE 7, the following statement does not compile:

processStringList(Collections.emptyList());

The Java SE 7 compiler generates an error message similar to the following:

List<Object> cannot be converted to List<String>

The compiler requires a value for the type argument T so it starts with the value Object. Consequently, the invocation of Collections.emptyList returns a value of type List, which is incompatible with the method processStringList. Thus, in Java SE 7, you must specify the value of the value of the type argument as follows:

processStringList(Collections.<String>emptyList());

This is no longer necessary in Java SE 8. The notion of what is a target type has been expanded to include method arguments, such as the argument to the method processStringList. In this case, processStringList requires an argument of type List. The method Collections.emptyList returns a value of List, so using the target type of List, the compiler infers that the type argument T has a value of String. Thus, in Java SE 8, the following statement compiles:

processStringList(Collections.emptyList());

See Target Typing in Lambda Expressions for more information.

Read this

Altri suggerimenti

It can do so via type inference.

Simple put, you can have an assignment like:

List<String> list = newArrayList();

where newArrayList() probably is something along the lines of:

static <E> List<E> newArrayList() {
    return new ArrayList<>();
}

Here the compiler will need to determine of what type the E type variable is. For this it will use the left hand side of the assignment, which consists of List<String>, hence E == String. Now it knows the type variable and everything is alright.

In contrary to using guava for such simple tasks, I would advise you to simply write:

List<String> list = new ArrayList<>();

I would call it cleaner and no need for an (unnecessary) external dependence.

The short answer is Type Inference.

Here's what's going on. You're not simply calling Lists.newArrayList(); you're also assigning it to a variable, like

List<Person> people = Lists.newArrayList();

Since you've specified the type arguments in the variable declaration, Java is able to "infer" (figure out) the type arguments you'd want in your new ArrayList, so you don't need to specify them.

The compiler infers the type from the variable/method declaration. See http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top