8

When I attempt to compile the following the code, I get the compilation error:

unexpected type System.out.println( new Test().C.i );
                                    ^
required: class,package 
found: value

class Test {

    class C {
        static final int i = 0;    
    }

    public static void main(String... z) {
        System.out.println( new Test().C.i  );
    }

}

But, if I change new Test().C.i to new Test().new C().i it compiles just fine.

Why? If i is static in C, then I shouldn't have to instantiate C. I should just be able to call it through the class C, not a C object.

What am I missing?

6
  • You can't access inner Type (static or not) via variable. If you create Test t = new Test(); then t.C is inappropriate call to type C. What you can only do is either use outer type like Test.C.i or use new to actually create instance of C which will let you access i (I am sure that there is some Java Language Specification which explains it, hope someone will find it and post it). Commented Dec 28, 2014 at 2:06
  • You can access static nested class through variable. Test.C.i works just fine if both C and i are static. Commented Dec 28, 2014 at 2:08
  • In Test.C Test is not variable, but type. In my example t is variable. Commented Dec 28, 2014 at 2:09
  • And the winner for today's question with the most downvoted answers goes to... Commented Dec 28, 2014 at 2:17
  • Ah, you're right. Sorry. Commented Dec 28, 2014 at 2:23

2 Answers 2

5

The problem is that "." Identifier in the Java syntax expects the identifier to refer to a variable and not a type.

This is specified in 6.5.6.2. Qualified Expression Names of the JLS (among other places):

If Q is a type name that names a class type (§8 (Classes)), then:

If there is not exactly one accessible (§6.6) member of the class type that is a field named Id, then a compile-time error occurs.

Otherwise, if the single accessible member field is not a class variable (that is, it is not declared static), then a compile-time error occurs.

Otherwise, if the class variable is declared final, then Q.Id denotes the value of the class variable.

The type of the expression Q.Id is the declared type of the class variable after capture conversion (§5.1.10).

If Q.Id appears in a context that requires a variable and not a value, then a compile-time error occurs.

Otherwise, Q.Id denotes the class variable.

The type of the expression Q.Id is the declared type of the class variable after capture conversion (§5.1.10).

Note that this clause covers the use of enum constants (§8.9), since these always have a corresponding final class variable.

While I can definitely appreciate the logic of why you'd think that it'd work like that - I actually expected it to work as well - it's not a big deal: Since there is always exactly only one i you can refer to it by Test.C.i. If i is non-static new Test().new C().i would be the correct way to access it.

Another way to look at it is to read 15.8. Primary Expressions which has the actual syntax for primary expressions (which is what we deal with here): It allows ClassInstanceCreationExpression (which is why new Test().new C().i works) as well as FieldAccess (which works for Test.C.i because the "class" is resolved recursively - only the last identifier has to refer to a field then).

Sign up to request clarification or add additional context in comments.

4 Comments

I believe you have the wrong JLS entry. You want the one where Q is an expression name, not a type name.
@Sotirios I'd reasonably need both and probably quite a few other places to cover all combinations. I think 15.8 is very much clearer on the whole - a question about the syntax of a language is in the end always best answered by just looking at the grammar. I might expand on the later point more and remove the first part though when I get to it.
I would rather stop focusing o syntactical issues (that most of modern IDEs will fix for yourself) and explain why it is really not working - see answer below
@rgasiore The OP knows what static/non static classes do and has repeatedly expressed her academic interest in why the snippet doesn't work (it would be reasonable after all to expect to be only able to refer to a non static inner class through an instance of the outer one)
-2

I think the reason why new Test().new C().i works is because class Test is a top-level class and is treated as static. If you were to change your inner class C to be static then new C().i would work.

However, you should NOT access static members in a non-static way.

To access your static field do:

System.out.println(C.i);

Edit:

For those saying that class Test is not static please refer to this stackoverflow answer.

All top-level classes are, by definition, static.

What the static boils down to is that an instance of the class can stand on its own. Or, the other way around: a non-static inner class (= instance inner class) cannot exist without an instance of the outer class. Since a top-level class does not have an outer class, it can't be anything but static.

Because all top-level classes are static, having the static keyword in a top-level class definition is pointless.


Just to show you how dumb of an idea it is to access a static field this way I created the following project:

class Test {

    class C {
        static final int i = 0;
    }

    public static void main(String[] args) {
        // BAD:
        System.out.println(new Test().new C().i);
        // Correct:
        System.out.println(C.i);
    }

}

If you compile the class and view it in jd-gui you can see how it was compiled:

class Test {

  public static void main(String[] args) {
    void tmp13_10 = new Test(); tmp13_10.getClass(); new C(); System.out.println(0);
    System.out.println(0);
  }

  class C {
    static final int i = 0;

    C() {
    }
  }
}

10 Comments

There is no need to change C to static to be able to access i via Test.C.i. Also this is not what OP is asking for.
That's not what I'm asking. I already know that. I'm asking for academic reasons.
@Pshemo My bad, you are correct. Was answering from my phone.
@frankie added an explanation as to why it works for new Test().new C().i
"new Test().new C().i works is because class Test is static" doesn't look good since Test is not static. "* If you were to change your inner class C to be static then new C().i would work*" true, but only inside static method of Test class because compiler would change it into new Test.C().i but question is why new Test().C.i doesn't work so it looks like your answer still doesn't answer the question.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.