-2

I thought the Java compiler (Java 11) could infer by itself the actual generic type, if I gave it enough hints, for example when the generic type is a method parameter and I provide as a parameter an instance of the actual type.

For example, I have the following class:

public class Var<T>
{
    public T value;

    public Var(T value)
    {
        this.value = value;
    }
}

Then, I try the following 3 attempts, which I expected all to compile:

//(1) Compilation error!
Var v = new Var(0);
++v.value;

//(2) Compilation error!
Var v = new Var<Integer>(0);
++v.value;

//(3) Compiles!
Var<Integer> v = new Var(0);
++v.value;

1) I would expect (1) to compile, because by using an Integer (or int) parameter, it may be enough for the compiler to know the actual type. So in ++v.value; I would expect the compiler to know that the variable is an Integer, but it does not. It still thinks it is an Object.

2) Adds some explicit information. But still the compiler does not understand.

3) Compiles, as expected.

Then, I try type inference with the var keyword:

//(4) Compilation error!
var v = new Var(0);
++v.value;

//(5) Compiles!
var v = new Var<Integer>(0);
++v.value;

4) Again, I would expect (4) to compile, since the type can be inferred from the parameter.

5) (After correcting my syntax:) Compiles, as expected.

Questions:

Could you explain why this code fails in the cases (1), (2), (4)?

Is there a way to make the var keyword type inference work with such a class?

Naman
  • 27,789
  • 26
  • 218
  • 353
rapt
  • 11,810
  • 35
  • 103
  • 145

2 Answers2

8

I would expect (1) to compile,

This is a raw type and is similar to writing

Var<Object> v = new Var<Object>();

but Object doesn't support ++

This is implied based on the class Var<T> which is shorthand for class Var<T extends Object> so if you have a raw type, it assumes the type T extends.

Adds some explicit information. But still the compiler does not understand.

The code doesn't compile I suggest adding the information in the right place.

Var<Integer> v = new Var<>();

or

Var<Integer> v = new Var<Integer>();

Is there a way to make the var keyword type inference work with such a class

The var is just a shorthand, and if the code wouldn't work without it, adding it won't help.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • I did not understand your "raw type" point. Here is a raw type: `List list = new ArrayList();`. It does not provide any information on the actual type. But `new Var(0)` does provide some information on what the actual parameter type is. I thought I did see examples in the past where in a generic method, the complier figured out by itself the actual generic type, if it was a method parameter, so it was not required to explicitly specify it by . – rapt Oct 29 '18 at 11:25
  • 1
    @rapt For constructors, no type inference for the class is taken from the arguments, raw or otherwise. If you use a generic class, it is validated, but no inference occurs. – Peter Lawrey Oct 29 '18 at 11:30
  • I thought type validation and type inference are tightly related when dealing with generic classes? First the actual generic type is identified - either inferred or specified explicitly - and then all uses of the generic instance are validated. If I have instead the following generic method ` Var var(U value) { return new Var<>(value); }` - How could I make `var v = var(0); ++v.value;` compile? – rapt Oct 29 '18 at 12:00
  • 1
    You would need ` Var var(U value)` but even then, I am not sure it will work as you expect. – Peter Lawrey Oct 29 '18 at 12:39
  • You are right... without that syntax it was interpreted again as a raw type. With this change `++v.value;` does compile. – rapt Oct 29 '18 at 12:47
2

The correct syntax would be:

var v = new Var<Integer>(0);

new <Integer>Var(0) is the obscure syntax for using a generic constructor (much the same as a generic method). You have a generic type.

The original code should be generating a ton of raw type warning messages.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • My bad, you are right... although that specific line did compile with the `new Var(0)` syntax, I wonder why. Anyway, I will correct my syntax in (2) & (5) in my question. But still my question stands - isn't `new Var(0)` supposed to hint the compiler that the type is `Integer`, for example I thought that would make (4) work? – rapt Oct 29 '18 at 11:05
  • `new Var(0)` would be a raw type. Do you mean `new Var<>(0)`? – Tom Hawtin - tackline Oct 29 '18 at 12:25
  • Yes... but how do I specify such syntax when I call the generic constructor through a generic method? For example ` Var var(U value) { return new Var<>(value); }` and then I want to do something like `var v = var(0);` – rapt Oct 29 '18 at 12:37
  • There is no generic constructor here. Generic methods don't require the diamond syntax of constructors of generic classes. (Also of note, the usual terminology doesn't match the JLS.) – Tom Hawtin - tackline Oct 29 '18 at 12:47
  • 1
    @rapt you simply have to stop your habit of using *raw types*. Since `Var` is generic, you have to provide a type argument instead of writing `Var`. So when you want to write a generic method, you have to write ` Var var(U value) { return new Var<>(value); }` (note the `` at the return type). Then, you can write `var v = var(0);` without any problems. Likewise, you can write `var v = new Var<>(0);` without any problems, the compiler will infer the correct type from the constructor argument. *When* you use `Var<>` instead of `Var`… – Holger Oct 29 '18 at 18:40