Why nested classes behave differently between Java and C#?

I don't understand why the following code errors in Java:

public abstract class TestClass
{
    private final int data;

    protected TestClass(int data) { this.data = data; }

    public final class InnerClass extends TestClass
    {
    private InnerClass(int data) { super(data); }

    public static final TestClass CONSTANT = new InnerClass(5);
    }
}

The error is on the public static final TestClass CONSTANT = new InnerClass(5); part. The errors are:

I:\Documents\NetBeansProjects\TestingGround\src\testingground\TestClass.java:22: error: non-static variable this cannot be referenced from a static context public static final TestClass CONSTANT = new InnerClass(5); ^ I:\Documents\NetBeansProjects\TestingGround\src\testingground\TestClass.java:22: error: Illegal static declaration in inner class TestClass.InnerClass public static final TestClass CONSTANT = new InnerClass(5); ^ modifier 'static' is only allowed in constant variable declarations 2 errors

If I try to achieve the same in C#, it works fine.

public abstract class TestClass
{
    private readonly int data;

    protected TestClass(int data) { this.data = data; }

    public sealed class InnerClass : TestClass
    {
        private InnerClass(int data) : base(data) { }

        public static readonly TestClass CONSTANT = new InnerClass(5);
    }
}

Why does Java not allow this?

Jon Skeet
people
quotationmark

To create an inner class (as opposed to a nested static class) you need an instance of the enclosing class - you don't have one in this case. Note that there's no direct equivalent of inner classes in C# - a nested class in C# is more like a nested static class in Java, and the meaning of static in a class declaration in C# is entirely different to the meaning of static in a class declaration in Java.

Option 1

If you really want it to be an inner class, you can write:

public static final TestClass CONSTANT = new SomeConcreteTestClass().new InnerClass(5);

(Where SomeConcreteTestClass is a concrete class extending TestClass)

or, horribly:

public static final TestClass CONSTANT = ((TestClass) null).new InnerClass(5);

Note that you'd need to move the declaration out of InnerClass though, as inner classes can't declare static variables other than compile-time constant expressions:

It is a compile-time error if an inner class declares a member that is explicitly or implicitly static, unless the member is a constant variable (ยง4.12.4).

So you'd end up with:

public abstract class TestClass
{
    private final int data;

    protected TestClass(int data) { this.data = data; }

    public final class InnerClass extends TestClass
    {
        private InnerClass(int data) { super(data); }        
    }
    public static final TestClass CONSTANT = ((TestClass) null).new InnerClass(5);
}

Option 2

Just make InnerClass static:

public static final class InnerClass extends TestClass

The addition of static there is the only change required. That's now much more like a C# nested class (although generics behave differently, as always...) Ideally at that point you'd rename the class, as it's not an inner class...

people

See more on this question at Stackoverflow