19

Consider the following set of expressions:

class T {{
/*1*/   Object o = T.super; // error: '.' expected
/*2*/   o.toString();
}}

An attempt to compile this will fail on line /*1*/ with the error:

error: '.' expected
    o = T.super;
               ^

both when using OpenJDK 1.8.0 (Ubuntu) or Oracle JDK 1.8 (Windows).

However, Eclipse 4.5.0 (Mars) compiles this without any error and it results in:

class T {
    T();
     0  aload_0 [this]
     1  invokespecial java.lang.Object() [8] // super()
     4  aload_0 [this]
     5  astore_1 [o]  // o = T.super
     7  invokevirtual java.lang.Object.toString() : java.lang.String [10]
    10  pop           // ^-- o.toString()
    11  return
}

From this you can see that the line /*1*/ of the java code (line 5 of the result) correctly stores this casted as Object (Eclipse's understanding of T.super) into the local variable o. When the code is executed, it completes normally and the line /*2*/ produces a correct result.

So far I failed to find anything relevant to o = T.super; in the Java 8 Language Specification, i.e. whether it's legal or not. Since it doesn't explicitly state that it's a legal expression, my guess is that it means it's illegal. But then, why Eclipse considers it legal? Hence my question:

Is T.super a legal expression as per JLS?


Edit: Simplified the code by removing a wrapping inner class.
charlie
  • 1,478
  • 10
  • 20
  • Eclise Mars 4.5.1 compiles it fine also apparently. But javac 1.8.0_66 indeed fails. – Tunaki Jan 07 '16 at 17:57
  • I can't speak for the JLS but the code compiled should still print `toString` for the `T` instance if `toString` is overridden. – samczsun Jan 07 '16 at 18:00
  • [JLS 15.11.2](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#d5e24643) only defines "super.identifier" and "T.super.identifier". To me that means that "T.super" is not defined (and "super" can't be an identifier since it's a keyword). So I'd treat "T.super" as undefined (and therefore as a compile time error). – Joachim Sauer Jan 07 '16 at 18:07
  • What're the double brackets for? Am I missing something? – Nic Jan 07 '16 at 18:11
  • 1
    @QPaysTaxes, it's an initializer. Sometimes the braces are placed this way, so it can be hard to recognize if you haven't seen it. – Sergei Tachenov Jan 07 '16 at 18:13
  • 3
    @QPaysTaxes, often used to populate a collection, by the way: `new ArrayList<>() {{add(something);}}`. Not a very elegant way, though, since it creates an unnecessary anonymous class, but still. – Sergei Tachenov Jan 07 '16 at 18:14

4 Answers4

13

No, it's not. Refer to Chapter 19. Searching for the keyword super yields the following constructs:

  • wildcard bounds: extends T / super T;
  • explicit constructor invocation: super(args);
  • field access: [Typename.]super.field;
  • method invocation: [Typename.]super.method();
  • method reference: super::method.

The fact that it compiles may be considered a bug or a language extension, although there is no real difference between the two.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • Yes, I did a search on whole JLS and also didn't find a relevant syntax. But does that mean that it's **illegal**, or just **unspecified**? – charlie Jan 07 '16 at 19:41
  • 2
    @charlie, Chapter 19 is the full grammar. If it's not there, it's not a valid syntax. If it was technically allowed by the grammar, but was not described in other chapters, then we could say it's unspecified. But as it is, it's just not a valid language construct. – Sergei Tachenov Jan 07 '16 at 19:47
  • 1
    A "language extension" *is* a bug. Especially in this case as it offers no benefit. In any scenario where `WhateverType x=Outer.super;` compiles, a standard conforming `WhateverType x=Outer.this;` will do exactly the same. – Holger Jan 07 '16 at 19:55
  • 1
    @Holger, I agree. And even if it did something like making `x` acting later like `super` (that is, really affecting method resolution), then it would be even more dangerous and confusing. So when I'm saying "bug _or_ language extension", I essentially mean that these are different names of the same thing. Better edit to clarify that, though. – Sergei Tachenov Jan 07 '16 at 20:02
  • Well, then, is there any official statement about the need of a strict adherence to the syntax, e.g.: `A compliant compiler must fail on syntax extensions.` Please link to it if you can. Then it would be the time to file a bug against Eclipse (presumably). – charlie Jan 07 '16 at 20:28
  • @charlie, is't it pretty much obvious? I don't think there is any reason to put a statement like this somewhere. You can come up with a lot of totally crazy constructs and implement them in your compiler, would that thing still be Java? This particular one looks harmless enough, but since there is no definition of "harmless" in the JLS, where would the border be? – Sergei Tachenov Jan 08 '16 at 04:55
  • @SergeyTachenov: OK, I'll stick with that. – charlie Jan 11 '16 at 10:14
3

T.super is not a legal expression because it does not make sense. The super (with or without explicit type) syntax is only used to call methods from the super class. T.super does not call any method (if it were legal syntax), it only references a class instance. In your case, it references the outer T instance. The correct syntax would be T.this, which would point to the outer this.

Your class definition can be viewed like this (with a named inner class):

class Outer
{
    // Constructor Body
    {
        class Inner
        {{
            /*1*/   Outer o = Outer.super; // error: '.' expected
            /*2*/   o.toString();
        }};
        new Inner();
    }
}

The correct way to reference the outer class would be

Outer o = Outer.this; // valid
Clashsoft
  • 11,553
  • 5
  • 40
  • 79
  • Not quite, while `Inner.super` and `Outer.this` *(in pseudocode)* are both of the type `Outer`, `Outer.super` is of type `Object` in your example, i.e. of a superclass of Outer. – charlie Jan 07 '16 at 18:58
  • Why would `Inner.super` be `Outer`? `Inner` doesn't extend `Outer`. Also, in my example the *variable `o`* is of type `Object`, *not* the expression that is assigned to it. – Clashsoft Jan 07 '16 at 19:07
  • Sorry, I missed the fact that `Inner` doesn't inherit from `Outer`. Still then, the second part holds, `Outer.this` is of type `Outer`, while `Outer.super` is of type `Object` (or a superclass, if any). So your example, after you changed `Object o` to `Outer o`, no longer compiles in Eclipse. – charlie Jan 07 '16 at 19:24
  • Also, `Outer.super.toString()` is legal and different from `Outer.this.toString()`, since the former resolves to `Object.toString()` and the latter to `Outer.toString()`, although both are applied to an Outer instance (maybe a static method would be a better example). And both are different from `super.toString()`, since that is applied to an Inner instance. – charlie Jan 07 '16 at 19:37
  • 1
    @charlie: this discussion is a waste of resources. Since `Outer.super` is not a valid expression, there is no sense in saying that it has the type `Object`. The construct `Outer.super.identifier` can be used to select members of a super type of `Outer`, but they are still accessed in an instance of type `Outer`. There is no widening conversion happening. – Holger Jan 07 '16 at 19:47
  • @Holger: I agree, my point was that I can't simply substitute it by `Outer.this`, since `Outer.super` behaves like a cast `(Object) Outer.this`. And the heart of the question wasn't to find a valid replacement, but whether it's **illegal**, or just **unspecified**, when Eclipse is so fine with it. – charlie Jan 07 '16 at 20:10
  • 1
    @charlie, there is no reason to believe that it behaves like a widening cast. Because if you do `Object x = Outer.this`, widening conversion happens anyway. Anything can be assigned to `Object` with no casting. And besides, such casting would have no effect anyway since all functions in Java are virtual anyway. – Sergei Tachenov Jan 08 '16 at 04:48
  • @SergeyTachenov: It's not about the assignment, `Outer.super` just doesn't let you refer to anything from `Outer`, only from a parent. `Outer o = Outer.super;` is **not valid** even in Eclipse. – charlie Jan 08 '16 at 09:04
  • 1
    @charlie, I see. And yet, `String s = Outer.super.toString()` and `Object x = Outer.super; String s = x.toString();` are _not_ the same things, are they, even in Eclipse? That's one more reason to regard it as a bug. – Sergei Tachenov Jan 08 '16 at 09:22
  • @SergeyTachenov: They **are** the same things, with the same output. The former for some reason uses a synthetic accessor, so it translates to `Outer.access$0(Outer.this)` where `String access$0(Outer arg 0) {return arg0.toString();}`. The latter translates to `(x = (Object) Outer.this).toString()`. – charlie Jan 08 '16 at 10:21
  • 3
    @charlie, they may be the same in this particular example, but I'm talking about general meaning. Suppose `Outer` overrides `toString()`. Then `System.out.println(Outer.super.toString())` would ignore the override and print something like `package.Outer@12345` while `Object x = Outer.super; System.out.println(x.toString());` would probably print whatever the overriding method returns. – Sergei Tachenov Jan 08 '16 at 10:29
  • 3
    @Sergey Tachenov: that's right. The construct `Outer.super.toString()` allows to ignore a `toString()` implementation contained int the `Outer` class. That's why the synthetic accessor method has been generated in the `Outer` class as only the declaring class itself can ignore an overriding method, thus, the inner class needs this help to do that. In contrast, it doesn't matter whether the variable `x` has the type `Outer` or `Object`, the invocation `x.toString()` will always invoke the most specific method, i.e. the one in `Outer`, if there is one. – Holger Jan 08 '16 at 10:44
  • @Holger: Ah, I see, that's why the synthetic accessor uses `invokespecial` on `Object::toString` with the `Outer.this` as a target, while `x.toString()` uses `invokevirtual`, though again on `Object::toString` with the `Outer.this` as a target. Thanks, that answers a [related question](http://stackoverflow.com/questions/34675012/why-t-super-tostring-and-supertostring-use-a-synthetic-accessor-method) which I just posted. ;o) – charlie Jan 08 '16 at 10:51
  • Well, no, it doesn't seem to answer the other question, since all the `super` calls **are** in the outer (declaring) class. There must be another reason there... – charlie Jan 08 '16 at 11:02
3

Accepting this syntax was a long-standing bug in Eclipse, which has been fixed for milestone 5 towards Eclipse 4.6.

Stephan Herrmann
  • 7,963
  • 2
  • 27
  • 38
  • Thanks for the fix! Just a short question: the bug report states: `problem is specific to field initialization (in an initializer or ctor)`. But at least in 4.5.0M3 it was a legal assignment anywhere in the code, e.g. `public String toString() {Object o = ""; return (o = T.super).toString();}`. So to be sure, does the fix apply to all such illegal occurences? – charlie Jan 11 '16 at 10:18
  • @charlie, the statement about field initialization was a red herring. With the fix, `T.super` can only be mentioned as the receiver of a field, method invocation or method reference, so I think this solution is complete (I just tested your `toString()` example and it is correctly rejected in HEAD). – Stephan Herrmann Jan 11 '16 at 21:52
-1

This is correct according to JLS. JLS specification "The form super.Identifier refers to the field named Identifier of the current object, but with the current object viewed as an instance of the superclass of the current class. "

Based on this super is the instance of the superclass.

Also JLS states that "It is a compile-time error if the current class is not an inner class of class T or T itself."

In your case ,it is clearly an inner class. Can you find out the version of Java used in eclipse.

Ashraff Ali Wahab
  • 1,088
  • 9
  • 19
  • 2
    The question is not about `super.Identifier`, it's about the form without the identifier, which is not correct. – Sergei Tachenov Jan 07 '16 at 18:11
  • Correct. But if you want to get the Identifier from super, super should contain the superclass instance implicitly. Do you agree? – Ashraff Ali Wahab Jan 07 '16 at 18:18
  • 1
    No. Because the `super.Identifier` and `Primary.Identifier` are syntactically different constructs. The latter actually evaluates to something and you access that something's member. The former is a special one. In it, `super` actually refers to `this`, only the member selection algorithm is different. – Sergei Tachenov Jan 07 '16 at 18:22
  • 1
    @AshraffAliWahab "super should contain the superclass instance " not quite. `super` doesn't contain superclass instance because there is only one instance (`this` one). `super` is keyword which simply tells compiler to use code from superclass (depending on context it is used). – Pshemo Jan 07 '16 at 18:23
  • May be I understood it differently "The form super.Identifier refers to the field named Identifier of the current object, but with the **"current object viewed as an instance of the superclass of the current class"**. The statement is from JLS. If you look at @Sam Sun comments in the question, it printed instance of T correctly. – Ashraff Ali Wahab Jan 07 '16 at 18:26
  • 1
    That's just means that `this` object is interpreted as if it was actually an instance of the superclass, without considering overrides. But the words "current object" refer to `this`, and not to the `super` keyword, which is a keyword, not a special variable of some kind. – Sergei Tachenov Jan 07 '16 at 18:34
  • 1
    In other words, it really determines _how the current object is viewed_, but it doesn't mean that it evaluates to the current object. – Sergei Tachenov Jan 07 '16 at 18:44
  • But the form `T.super.Identifier` really refers to a different instance -- an instance of the enclosing class, which is not necessarilly a super class of `this`. And event if it is a super class, it is is a different instance than `super`. – charlie Jan 07 '16 at 18:44
  • In other words, if you have an `Outer` class and an inner `Inner` class, which doesn't inherit from `Outer`, then `Outher.this != super`. I'll update the question to make it clear the inheritance (as in case of an anonymous class) is not crucial. – charlie Jan 07 '16 at 19:03
  • 1
    @charlie, we were discussing the `super.Identifier` form here, so `Outer` is not exactly relevant to this particular discussion. It only means that this answer is even less relevant. – Sergei Tachenov Jan 07 '16 at 19:51
  • @SergeyTachenov: Yes, it was the initial confusion of `T.super` and `super` in this answer which I tried to stress out. The former goes **out** to **enclosing** classes, the latter **up** to **superclasses** in the inheritance hierarchy. Maybe using class names from another answer wasn't the best idea. – charlie Jan 07 '16 at 20:17