15

Consider this example:

public final class Main<T extends Main<T>> {

    public static void main(String[] args) {
        Main<?> main = new Main<>();
    }
}

This compiles perfectly. However when I try to make this compile without using the diamond, the only way I can get it to work is by using a raw type.

Main<?> main = new Main();    

Attempts without raw types don't work:

Main<?> main = new Main<?>();              // None of
Main<?> main = new Main<Main<?>>();        // these
Main<?> main = new Main<Main<Main<?>>>();  // compile

So why does the original version with the diamond work? What is the inferred type when you write Main<?> main = new Main<>(); ?

Is it inferring a raw type, or is it inferring some kind of infinitely nested type like this?

Main<Main<Main<Main<...>>>>
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • 3
    I kind of think you should replace `Main` with `Buffalo` in this post. – djechlin Nov 05 '15 at 00:06
  • So, you've invented the endless recursion in the Java Compiler. Nice job, like it! – gaborsch Nov 05 '15 at 00:08
  • `Enum` has a similar declaration, so what you're encountering is nothing new. – Makoto Nov 05 '15 at 00:12
  • I don't think you _can_ explicitly specify the diamond type without extending `Main` with a `Foo extends Main`. I don't think the inferred type is syntactically representable. I don't think it's infinitely nested, but I think it's a magic `? extends Main>` where the `?`s are magically the same `?`. – Louis Wasserman Nov 05 '15 at 00:14
  • 1
    @Makoto The difference is that `Enum` is `abstract`. The standard self-referential types in the language (e.g. `Enum`, `Comparator` are `abstract`). A concrete self referential type is a very weird thing. – Paul Boddington Nov 05 '15 at 00:14
  • As far as I can tell everything in your code makes sense and just creates something unusable together undetectable by the compiler. Like writing `if (Boolean.FALSE) { }`. – djechlin Nov 05 '15 at 00:15
  • @LouisWasserman I think you're probably right. I was just hoping that somebody would confirm that there really are these weird syntactically unrepresentable types the compiler uses. It must be in the specification somewhere. – Paul Boddington Nov 05 '15 at 00:16
  • I don't actually know how something like `List> = new List<>()` would work, which seems to be the only mysterious part to me at least. – djechlin Nov 05 '15 at 00:16
  • @djechlin At least in that case you can just imagine that the compiler is able to substitute `Object`. – Paul Boddington Nov 05 '15 at 00:19
  • @djechlin: It's not unusable; it's just covariant (meaning that the type parameter is not very usable). – SLaks Nov 05 '15 at 15:18

2 Answers2

4

The ? in Main<?> is a placeholder that may be any type at bind-time.

Each ? that you write in source may be a different type (referred to in error messages as capture#2-of ?), so you cannot assign an expression of Main<?> to a variable of any expressible type.

The diamond operator works here because its type inference runs after the ?s are captured – your Main<> becomes not Main<?> but Main<capture#1 of ?> (assuming the Main<?> you assign it to is capture#1).

In other words, the diamond operator is the only syntax that can directly specify a specific capture, just like var in C# is the only syntax that can directly specify an anonymous type. (note that overload resolution with method type inference can also resolve to specific captures)


As for what the code means, new Main<?>() (for any capture of ?) is shorthand for Main<? extends Object>, or, in your case, Main<? extends Main<same ?>> (the compiler automatically constrains the ? to the constraints of the type). This becomes a covariant view of Main<>, where the type parameter is convertible only to Main<?> (since it may actually be any type, so you can't assume anything beyond the constraint).

Usually, there is no reason to actually create such a thing.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • So the compiler can create an artificial type `capture#1` that extends `Main` even though there isn't a real type like that? – Paul Boddington Nov 05 '15 at 15:27
  • 1
    @PaulBoddington: That's not a type, but a constraint (just like `List extends SomeInterface & OtherInterface>`) – SLaks Nov 05 '15 at 15:57
1

It's possible to make something that compiles without the diamond operator, by using a generic helper method (but of course, this then begs the question, what type argument is being inferred for the call to the helper method):

final class Main<T extends Main<T>> {

    public static void main(String[] args) {
        Main<?> main = helper();
    }
    private static <T extends Main<T>> Main<T> helper() {
        return new Main<T>();
    }
}
newacct
  • 119,665
  • 29
  • 163
  • 224