收录日期:2020/12/05 22:02:34 时间:2010-09-07 20:09:58 标签:java

I've come across some odd behavior in assignment of final variables. You can assign a final varible in a constructor to initialize it, which makes sense. However you can't do the same in a subclass, even if the final variable is a member of the subclass -

public class FinalTest {
    public final String name;

    public FinalTest()
    {
        name = "FinalTest";
    }

    public static class FinalTestSubclass extends FinalTest {
        public FinalTestSubclass()
        {
            name = "FinalTestSubclass"; //<---- this won't compile, assignment to final variable.
        }
    }
}

Can someone think of a good reason why this should/would work this way?

Every constructor of a subclass must invoke a constructor of the superclass as its first operation. Every final member variable must be initialized before a constructor completes. A final variable can be assigned only once. Given those rules, it is impossible for a subclass constructor to directly assign a value to a final superclass' member.

Making exceptions would increase complexity and create "gotchas" in exchange for limited additional utility.

A practical solution is to provide a superclass constructor that takes a value to be assigned to the final member. This can be protected or package-private if desired. If the superclass is outside of your control, there's a good chance that allowing derived classes to break its assumptions about the finality of its members would cause other problems.

If you were allowed to assign a value to name in FinalTestSubClass it would mean that the value assigned in FinalTest was not actually the final value.

If your example was valid, then this would mean that name could have different values (based upon which class was instantiated), making the final modifier pretty much redundant.

A better question is, why should the behavior you desire be allowed?

informally, final fields should have been initialized when the constructor is finished.

in your subclass constructor, super() has been called implicitly, the constructor of the super class is finished, the final fields in the super class should not be modified.

you may want this instead:

class A
    final String s;
    A(String s){ this.s = s; }
    A() { this("default"); }

class B extends A
    B(){ super("B's default"); }

This is standard behavior in Java

The key word final can by used in multiple way, for class close the possibility to inherite from it, for method to override it, for variable allow to be assigned only once in simply words.

For your case this variable is allready assigned in super class,

what You can do is

public class FinalTest {
    public final String  name = "FinalTest";

    public FinalTest()
    {

    }

    public static class FinalTestSubclass extends FinalTest {

        public final String  name = "FinalTestSubclass";

        public FinalTestSubclass()
        {

        }
    }
}

Read more about final variables

In reply to your comment to matt's answer; you can achieve determining the constant in the subclass by passing it in the constructor:

public class FinalTest {
    public final String name;

    public FinalTest()
    {
        this("FinalTest");
    }

    protected FinalTest(String nameConstant)
    {
        name = nameConstant;
    }

    public static class FinalTestSubclass extends FinalTest {
        public FinalTestSubclass()
        {
            super("FinalTestSubclass");
        }
    }
}