Search code examples
javaoopstaticdefaultsuper

How to declare default member values in Java that are passed to super class but cannot be inline


I have code that I think is using static members incorrectly.

An example is below. Variable types and names might not make sense.

public class subclass extends superclass {
    private static Boolean varBool = new Boolean(true);
    private static Double  varDoub = new Double(4.3);
    private static CustomType varCustom = new CustomType(varBool, varDoub);

    public subclass() { super(varCustom); }
}

I think this is an incorrect use of static. I see that the author wanted to define default values that can be passed to the super class, and used static to do so. The way this class was used, I believe did not cause issues since the values didn't change, but now I want them to for different instances.

I'm instead considering using static methods to define these defaults.

public class subclass extends superclass {

    public subclass() { super(getVarCustom()); }

    private static getVarBool() { return new Boolean(true); }

    private static getVarDoub() { return new Double(4.3); }

    private static getVarCustom() { return new CustomType(getVarBool(), getVarDoub()); }
}

Which will allow me to change the defaults by passing in values to new constructors without affecting other instances.

For example:

public class subclass extends superclass {

    public subclass() { super(getVarCustom()); }

    public subclass(double dub) { super(getVarCustom(dub)); }

    private static getVarBool() { return new Boolean(true); }

    private static getVarDoub() { return new Double(4.3); }

    private static getVarCustom() { return new CustomType(getVarBool(), getVarDoub()); }

    private static getVarCustom(double dub) { return new CustomType(getVarBool(), dub); }
}

I guess this isn't perfect, but I'm trying to minimize the impact in the codebase by not changing how other classes are using this class.

Is this okay to do, or am I misunderstanding something?


Solution

  • What you want doesn't work. static methods do not 'do' inheritance. You're better off thinking of static methods as existing fully outside classes entirely.

    In java, classes (well, types) have 2 roles. They define types, sure, but, they have a second role as namespaces. It's types that have a fully qualified namespace (they exist in a package, hence, you can have 2 classes both named Foo and java allows you to differentiate via packages). Methods then exist inside one, and thus, the fact that you can fully qualify classes means methods, too, are namespaced. This is how java solves the cameragun problem (both cameras and guns have a shoot(Person p) method, but, you wouldn't want to get confused!).

    Hence, static methods have to be in a class. But they don't do inheritance, unlike instance methods – You can't "override" static methods:

    class Parent {
      Parent() {
        System.out.println("This is the result of foo: " + foo());
      }
    
      static String foo() {
        return "in parent";
      }
    }
    
    class Child extends Parent {
      static String foo() {
        return "in child";
      }
    }
    
    new Child();
    

    will print in parent, showing how you cannot 'override' static methods.

    Because of this, the solution of immutable fields is simpler and more clear, so, you should probably stick with what you already have. However, note that your static constants aren't marked final which means you now have global mutable state. If you like maintainable code, you don't want that. Mark them final. And, make them ALL_CAPS (java convention for 'constant' - avoids having to document 'these should be immutable types', as the all-caps implies it).

    If you want extension-style things as part of the job of construction, this is what factories are for. Factories are the mechanism by which you give static aspects of classes (and constructors are static, in the sense that you do not need to have an instance of the class to call one, and they don't 'inherit' / cannot be overridden):

    class ParentFactory {
      public double defaultFooVal() {
        return 2.0;
      }
    
      public Parent create() {
        return new Parent(defaultFooVal());
    }
    
    class ChildFactory extends Parent {
      @Override public double defaultFooVal() {
        return 3.0;
      }
    }
    
    ParentFactory f = new ChildFactory();
    Parent p = f.create();
    System.out.println(p.getFooVal());
    

    would print 3.0 as you want it to.