38

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!

Jacob G.
  • 26,421
  • 5
  • 47
  • 96
  • 3
    Well, the FAQ section on a mailing list by brian has [this as one of the questions.](https://stackoverflow.com/a/48429067/1746118) – Naman Jan 24 '18 at 20:11

3 Answers3

32

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<>() {};
ZhekaKozlov
  • 29,055
  • 16
  • 100
  • 138
23

"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.

Brian Goetz
  • 76,505
  • 17
  • 128
  • 138
13

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
Michael
  • 34,340
  • 9
  • 58
  • 100
Naman
  • 23,555
  • 22
  • 173
  • 290
  • 2
    How do you go from "In most cases, you'll get an informative compiler error" to "Yes, it would compile"? At best, I see Brian's description as ambiguous with regards to the case we're discussing. – Michael Jan 25 '18 at 07:51
  • 2
    @Michael *Yes, it would compile* was a part of my answer since [I've compiled and executed the code](https://github.com/namannigam/jdk10-localvariables-typeinferences/blob/master/src/com/stackoverflow/nullpointer/RHSInference.java). Brian's statement is not ambiguous since the exceptional case of falling back to inferring the Object is also mentioned in the answer from him. – Naman Jan 25 '18 at 08:22
  • 1
    I've been playing around with `var` this morning as well and haven't been able to find a single situation where I get a compiler error (falling back to Object is a mistake IMO, but whatever). His FAQ *is* ambiguous because he doesn't detail what the "most cases" and "some cases" actually are. – Michael Jan 25 '18 at 08:26
  • 1
    @Michael Did you actually made use of the statements mentioned in the Risks and Assumption of the JEP286? You get to see the different compiler errors there. Or maybe the interpretation of the quoted words is different in my perspective and yours. Though I agree the JEP also might improve and need to explain(elaborate) any such exclusive use case. – Naman Jan 25 '18 at 08:28
  • @nullpointer Okay, sure - I have tried those and they do fail. It seems misleading to me to give an example of the diamond operator and follow up with "you've not provided enough type information ... In most cases, you'll get a compiler error" when in fact no use of the diamond operator can fail because it can always default to Object if necessary. I guess what he was trying to get at was that most *types of* type inference (i.e. those in Risks) will fail. That definitely wasn't clear to me. – Michael Jan 25 '18 at 08:40
  • 2
    The raw type `ArrayList` is *not* the same as `ArrayList`. You will notice as soon as you try to assign that list to something that expects a generic list, other than `List`, i.e `var list = new ArrayList<>(); List numberList = list;` vs. `ArrayList list = new ArrayList<>(); List numberList = list;`… – Holger Jan 26 '18 at 11:59
  • @Holger I agree and I don't mean to say that its is the same but on the other hand assuming that the compiler infers it in the same way for both the initializations. `var list = new ArrayList<>();` and `ArrayList list = new ArrayList<>();`. Did you mean that would not hold true ? – Naman Jan 26 '18 at 21:15
  • 1
    I'm not sure whether the compiler would do any inference at all when the target type is a raw type. Your last line `Object o = new ArrayList<>()` is a better example. – Holger Jan 27 '18 at 00:38