Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why nested classes behave differently between Java and C#?

Tags:

java

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?

like image 570
Pharap Avatar asked Feb 11 '23 15:02

Pharap


2 Answers

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...

like image 65
Jon Skeet Avatar answered Feb 15 '23 23:02

Jon Skeet


Java language does not allow an inner class to declare a member that is explicitly or implicitly static, unless the member is a constant variable, see JLS 8.1.3

like image 22
Evgeniy Dorofeev Avatar answered Feb 15 '23 23:02

Evgeniy Dorofeev