Java 10: Will Java 7's Diamond Inference Work

2019-01-22 11:40发布

问题:

From JEP 286, we see that we'll be able to utilize local type inference (var) in JDK 10 (18.3). The JEP states that the following compiles, which is expected:

var list = new ArrayList<String>();  // infers ArrayList<String>

I'm curious to know what would happen if we attempt the following:

var list = new ArrayList<>();

Will what I proposed in the second snippet even compile? If so (which I doubt), would the ArrayList accept Object as its generic type?

I'd try this myself, but I don't have access to any machines which I can install early releases on.

Thanks!

回答1:

Yes, var and the diamond operator can be combined together. The compiler will infer the most specific generic type:

var list = new ArrayList<>(); // Infers ArrayList<Object>
var list = new ArrayList<>(List.of(1, 2, 3)); // Infers ArrayList<Integer>

And you can even combine them with an anonymous class:

var list = new ArrayList<>() {};


回答2:

"Work with" is a vague question, so you're likely to get vague answers.

Type inference is not mind reading; it's just constraint solving. The fewer type constraints available, the more likely you are to encounter failure or a surprising result (inferring a type that you didn't expect, such as Object.)

Diamond says: the types I need are probably already present on the left hand side, why repeat them on the right.

Local variable type inference says: the types I need are probably already present on the right hand side, why repeat them on the left.

Generic method invocation says: the types I need are probably already present in the arguments, why repeat them as witnesses.

If sufficient type information is available in the program without either manifest constructor type arguments or a target type on the left, everything will be fine. For example:

List<String> anotherList = ...
var list = new ArrayList<>(anotherList);

Here, the compiler is able to infer the type parameter of ArrayList by looking at the type of the argument to the constructor (the one that takes Collection<? extends E>). So it infers T=String on the RHS, and then is able to infer ArrayList<String> on the LHS.

In other words, the compiler is going to do what it can given the information you've given it. The less information you give it, the more likely it will either fail, or not do what you want.

That said, I think you've asked the wrong question. The question of how much you can leave out should not be driven by "how much will the compiler let me leave out", but "how much damage am I doing to the readability of my program." Reading code is more important than writing code. Leaving out everything you can possibly leave out is unlikely to maximize readability. You should strive to leave in enough to ensure that no reader is confused when confronted with your program.



回答3:

Yes, it would compile. The var in the code

var list = new ArrayList<>();

shall be inferred as type ArrayList<Object> (I believe one can precisely not determine the exact type of the element there due to erasure) which would be same as making use of a code such as:-

ArrayList list = new ArrayList<>(); 
// without the type of the element of list specified

where list is eventually ended up inferred as ArrayList<Object>.


From the FAQ on the mailing list by Brian :-

What happens if we ask for inference on both sides?

If you say:

var x = new ArrayList<>() 

then you're asking for the compiler to infer both the type argument to List, and the type of x.

But you've not provided enough type information for the compiler to do a good job.

In most cases, you'll get an informative compiler error telling you that you're asking for your mind to be read. In some cases, we'll fall back to inferring Object, as we currently do with:

Object o = new ArrayList<>()  // always inferred ArrayList<Object> here