0

I need a method in a class that is going to be used in subclasses, although this method uses a property that is changed in subclasses. Is there a way to access the property of the subclass without having to override the method?

I've tried using a getter for the property, but got the same result.

public class SuperClass {
    private static final String a = "Super";

    public void superMethod(){
        System.out.println("SuperMethod: " + a);
    }

}

public class ChildClass extends SuperClass {

    private static final String a = "Child";

}

public class Main {
    public static void main(String[] args) {
        SuperClass s = new SuperClass();
        ChildClass c = new ChildClass();
        s.superMethod();
        c.superMethod();
    }
}

The console shows:

SuperMethod: Super

SuperMethod: Super

The expected result is:

SuperMethod: Super

SuperMethod: Child

mdevino
  • 77
  • 7

3 Answers3

5

I've tried using a getter for the property, but got the same result.

Are you sure? The following should be exactly what you're after:

class SuperClass {

    private String a = "Super";

    public void superMethod() {
        System.out.println("SuperMethod: " + getA());
    }

    public String getA() {
        return this.a;
    }

}

class ChildClass extends SuperClass {

    private String a = "Child";

    @Override
    public String getA() {
        return this.a;
    }

}

public class Main {

    public static void main(String[] args) {
        SuperClass s = new SuperClass();
        ChildClass c = new ChildClass();
        s.superMethod();
        c.superMethod();
    }
}

Note that the getters can't be private (otherwise they can't be accessed from outside the class), and they can't be static (otherwise they're part of the class, not any instances of that class.)

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
  • When I tried, I used the getters to return `this.a` rather than the hardcoded value. – mdevino Jul 12 '19 at 12:02
  • @berry120 I just realized you are not using `static` and `final` modifiers for `a`. I'm trying to make this class thread-safe and all must access the same instance of `a`. Are these modifiers not needed for that? I have some experience with Java, but not much with thread-safety. – mdevino Jul 12 '19 at 13:51
  • @mdevino I'm afraid that doesn't make much sense - I think you need to read up on thread safety, as well as the `final` and `static` modifiers. You can make both `a` fields `private` and `static` if you want in this example and the code will function identically, but that doesn't mean your class is (or isn't) thread-safe. – Michael Berry Jul 12 '19 at 13:57
1

It isn't clear exactly what you are doing, but your String a members are private static members of the class, not of the individual objects.

If you made String a a member of the object, instead of the class, you could override the value during the creation of the child class:

U:\>jshell
|  Welcome to JShell -- Version 12
|  For an introduction type: /help intro

jshell> class SuperClass {
   ...>    protected final String a;
   ...>
   ...>    protected SuperClass(String _a) {
   ...>       a = _a;
   ...>    }
   ...>
   ...>    public SuperClass() {
   ...>       this("Super");
   ...>    }
   ...>
   ...>    public void superMethod() {
   ...>       System.out.println("SuperMethod: "+a);
   ...>    }
   ...> }
|  created class SuperClass

jshell> class ChildClass extends SuperClass {
   ...>    public ChildClass() {
   ...>      super("Child");
   ...>    }
   ...> }
|  created class ChildClass

jshell> var s = new SuperClass();
s ==> SuperClass@4566e5bd

jshell> var c = new ChildClass();
c ==> ChildClass@ff5b51f

jshell> s.superMethod();
SuperMethod: Super

jshell> c.superMethod();
SuperMethod: Child

Update

Now that we know your actual use-case (from a comment, below), what you want to implement is pretty simple:

class SuperClass {
    private final static Logger LOG = Logger.getLogger(SuperClass.class);

    protected Logger getLogger() { return LOG; }

    public void superMethod(){
        getLogger().info("superMethod() called.");
    }
}

class ChildClass extends SuperClass {
    private final static Logger LOG = Logger.getLogger(ChildClass.class);

    @Override
    protected Logger getLogger() { return LOG; }
}

public class Main {
    public static void main(String[] args) {
        SuperClass s = new SuperClass();
        ChildClass c = new ChildClass();
        s.superMethod();                 // Message logged to SuperClass.LOG
        c.superMethod();                 // Message logged to ChildClass.LOG
    }
}
AJNeufeld
  • 8,526
  • 1
  • 25
  • 44
  • In the original code I am actually using a [Log4j Logger](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html) class, rather than a String. I'd like to make this work like that because you have to pass the `.class` to the Logger class constructor, so if I used a method from the superclass to get the logger, I'd get the superclass logger, rather than the child one. The reason for final static is because I am trying to make it thread-safe, so I'd like all instances of the class using the same instance of the object. – mdevino Jul 12 '19 at 12:08
  • As I mentioned in my answer, "_It isn't clear exactly what you are doing_". **That** is exactly the sort of details you should have included in your question. If you had included a `private final static Logger log` member in both classes, instead of a `private static final String a`, it would have gone a long way towards us understanding what your goal was, and you could have gotten useful answers, including best practices, instead of a hacky reflection answer. – AJNeufeld Jul 19 '19 at 21:16
0

Short answer: Java cannot do it the way you want since the compiler will merge String literal with final values, so "SuperMethod: " + a will get translated to "SuperMethod: Super" in the resulting bytecode.

The only solution is using reflection (if you must):

import java.lang.reflect.Field;

public class Main {
  public static void main(String[] args) {
      SuperClass s = new SuperClass();
      ChildClass c = new ChildClass();
      s.superMethod();
      c.superMethod();
  }
}

class SuperClass {
    private static final String a = "Super";

    public void superMethod(){
      try{
        final Class<?> clazz = this.getClass();
        final Field fieldA = clazz.getDeclaredField("a");
        fieldA.setAccessible(true);

        final String value = (String)fieldA.get(null);

        System.out.println("SuperMethod: " + value);
      } catch (final NoSuchFieldException | IllegalAccessException ex){
        // Because reflection
        ex.printStackTrace();
      }
    }
}

class ChildClass extends SuperClass {
    private static final String a = "Child";
}

Output is:

SuperMethod: Super
SuperMethod: Child

But, honestly, I would still favor using classic override:

public class Main {
  public static void main(String[] args) {
      SuperClass s = new SuperClass();
      ChildClass c = new ChildClass();
      s.superMethod();
      c.superMethod();
  }
}

class SuperClass {
    private static final String a = "Super";

    public void superMethod(){
        System.out.println("SuperMethod: " + getA());
    }

    public String getA() {
      return a;
    }

}

class ChildClass extends SuperClass {

    private static final String a = "Child";

    @Override
    public String getA() {
      return a;
    }
}
DanielCuadra
  • 970
  • 9
  • 17
  • The reflection option seems to be the best as child classes wouldn't have to override `getA()` in order to use `superMethod()` at all. Why would you recommend the classic override rather than reflection? – mdevino Jul 12 '19 at 15:24
  • It really depends on what you need this for. For regular code, I prefer readability and predictability; for example, if someone removes `a` from ChildClass the compiler will make it evident. However, if you are building a framework or specialized-library that needs to work over private properties with "clever code", then reflection would be OK. – DanielCuadra Jul 12 '19 at 19:23