55

I just start out with an example, that explains it best:

public abstract class A{
    static String str;
}

public class B extends A{
    public B(){
        str = "123";
    }
}

public class C extends A{
    public C(){
        str = "abc";
    }
}

public class Main{

    public static void main(String[] args){
        A b = new B();
        A c = new C();
        System.out.println("b.str = " + b.str);
        System.out.println("c.str = " + c.str);
    }
}

This will print out:

b.str = abc

c.str = abc

But I would like a solution where each subclass that instantiate the super class, has their own class variable, at the same time I want to be able to reference that class variable through the identifier, or a method call, defined in the abstract super class.

So I would like the output to be:

b.str = 123

c.str = abc

Is that doable?

Tommy
  • 551
  • 1
  • 4
  • 4

8 Answers8

59

If you want classes B and C to have separate static variables, you'll need to declare the variables in those classes. Basically, static members and polymorphism don't go together.

Note that accessing static members through references is a really bad idea in terms of readability - it makes it look like it depends on the value of the reference, when it doesn't really. So your current code won't even compile when you've moved str down to B and C. Instead, you'll need

System.out.println("b.str = " + B.str);
System.out.println("c.str = " + C.str);

If you really need to access the value polymorphically (i.e. through an instance of A) then one option is to make a polymorphic getter:

public class A {
    public abstract String getStr();
}

public class B extends A {
    private static String str = "b";

    @Override public String getStr() {
        return str;
    }
}

(and the same for C).

That way you get the behaviour you want in terms of not having a separate variable per instance, but you can still use it polymorphically. It's a little odd for an instance member to return a static value like this, but you're using the value for polymorphism of type, basically...

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 5
    My experience is that it's even worth setting Eclipse to mark them as compilation errors. – biziclop Feb 04 '11 at 14:04
  • 10
    @biziclop: Me too. That's why I requested it in Eclipse in the first place: https://bugs.eclipse.org/bugs/show_bug.cgi?id=21109 :) – Jon Skeet Feb 04 '11 at 14:07
  • Why is this answer not marked as solving the issue? – belka Nov 10 '17 at 15:07
  • 1
    But if I want to refer to use `str` in a static method of the abstract class, how do I do that, now that the getter is an instance method? – Cameron Hudson Mar 08 '19 at 20:23
  • @CameronHudson: You can't, and you'd need to specify exactly which class you wanted the static variable from anyway. Fundamentally it's very hard to give advice without knowing considerably more context. I suggest you ask a new question with detailed context - the chances that your requirements are exactly the same as the ones from this question 8 years ago are pretty slim. – Jon Skeet Mar 08 '19 at 20:26
  • @Jon Skeet. Thanks. I created a new question here: https://stackoverflow.com/questions/55071196/java-how-to-refer-to-subclasss-static-variable-in-abstract-class – Cameron Hudson Mar 08 '19 at 21:24
7
public abstract class A {
    private String str;
    public String getStr() { return str;}
    protected void setStr(String str) { this.str = str; }
}

Then you'll be able to have

B b = new B();
b.getStr();

The setter and getter are my addition, you can go by simply making the variable non-static.

Update If you want to have the static-per-subclass, then you can have:

protected static Map<Class, String> values;
public abstract String getValue();

and then:

public String getValue() {
    values.get(getClass());
}
public void setValue(String value) {
    values.set(getClass(), value);
}

But this is generally a bad idea.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • This will work, even without the getter and setter. Then just make sure the str variable is public, which is not recommended. – Thijs Wouters Feb 04 '11 at 14:04
  • @Thijs: I'd always prefer a method (especially a read-only method like `getStr()` here) over direct access to a static field. – Goran Jovic Feb 04 '11 at 14:10
  • Thanks! Not sure why you think it is a bad idea. With this solution I don't even have to code anything in the subclasses, everything is implemented in the super class. Of course it looks kind of weird to call method on an object to set a class variable (or to fake it as your solution does, kind of). But seems there is no way to escape that anyway (Jon Skeet's and Ralph's solution). – Tommy Feb 04 '11 at 17:32
  • @Bozho: could you elucidate on why this is a bad idea? – serv-inc Apr 21 '15 at 14:06
  • 1
    it's just unnatural and violates the principle of least astonishment :) – Bozho Apr 25 '15 at 18:32
  • The updated part is pretty cool actually, only if it could work in static methods, even if I manage to get the class from MethodHandles - it points to the abstract class. So from what I have tried with static methods - it doesn't work. @Bozho any chances it can work? – Sam Thomas Mar 13 '20 at 04:26
3

Put the static varibale in each subclass and add a (not static) abstract method to the abstract superclass:

abstract String getStr();

Then implement the getStr() method in each subclass by returning the static field of this special subclass.

public class B extends A {
 private static String str;

  @Override
  public String getStr() {
    return B.str;
  }
}
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • I edited my post, was an error ..sorry. The thing is, I need each object to have an reference to a value that is shared among all objects of the same subclass, but not necessarily among the objects of different subclasses. – Tommy Feb 04 '11 at 14:00
2

It is what I did to avoid to have to implement the same method in every subclass. It is based on the answer of Bozho. Maybe it may help someone.

import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Uglylab
 */
public class SandBox {

    public static void main(String[] args) {
        A b = new B();
        A c = new C();
        System.out.println("b.str = " + B.getStr(b.getClass()));
        System.out.println("c.str = " + C.getStr(c.getClass()));
    }

}
 abstract class A{
    protected static Map<Class, String> values = new HashMap<>();


    public static String getStr(Class<? extends A> aClass) {
        return values.get(aClass);
    }

    public static void setStr(Class<? extends A> aClass, String s) {
        values.put(aClass, s);
    }

}

 class B extends A{
    public B(){
        setStr(this.getClass(),"123");
    }
}

 class C extends A{
    public C(){
        setStr(this.getClass(),"abc");
    }
}
Uglylab
  • 103
  • 1
  • 6
2

only one instance of static variable is present in the system.

static variable will load into the system in the start before class is loaded.

reason both time abc is printed is because you set the value of str as abc in the last.

Chirag Tayal
  • 459
  • 1
  • 6
  • 14
2

This will print the output you want:

public abstract class A{
}

public class B extends A{
    static String str;

    public B(){
        str = "123";
    }
}

public class C extends A{
    static String str;

    public C(){
        str = "abc";
    }
}

public class Main{

    public static void main(String[] args){
        A a = new B();
        A c = new C();
        System.out.println("B.str = " + B.str);
        System.out.println("C.str = " + C.str);
    }
}
Thijs Wouters
  • 840
  • 1
  • 8
  • 16
  • Thanks. Almost there :) See Ralph's answer. It may may been me not explaining very well. – Tommy Feb 04 '11 at 14:13
2

Since you hardcode the value or str in subclasses anyway, you can do something like this:

public abstract class A{
    public abstract String getStr();
}

public class B extends A{
   public String getStr(){
      return "123";
   }
}

public class C extends A{
   public String getStr(){
      return "abc";
   }
}

This would do the trick in your case.

Of course, then you should call it by method, like this:

public class Main{

    public static void main(String[] args){
        A b = new B();
        A c = new C();
        System.out.println("b.str = " + b.getStr());
        System.out.println("c.str = " + c.getStr());
    }
}
Goran Jovic
  • 9,418
  • 3
  • 43
  • 75
0

I think one way to approach this is to use a singleton for class B and C to mimic static methods and fields. The can both extend abstract class A, but will have their own values of str..

Martin Konecny
  • 57,827
  • 19
  • 139
  • 159