Java: статическое поле в абстрактном классе - PullRequest
47 голосов
/ 04 февраля 2011

Я только начинаю с примера, который объясняет это лучше всего:

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);
    }
}

Это распечатает:

b.str = abc

c.str = abc

Но я хотел бы получить решение, в котором каждый подкласс, который создает экземпляр суперкласса, имеет свою собственную переменную класса, в то же время я хочу иметь возможность ссылаться на эту переменную класса через идентификатор или вызов метода, определенного в абстрактном суперклассе.

Итак, я бы хотел, чтобы результат был:

b.str = 123

c.str = abc

Это выполнимо?

Ответы [ 8 ]

49 голосов
/ 04 февраля 2011

Если вы хотите, чтобы классы B и C имели отдельные статические переменные, вам нужно объявить переменные в этих классах. По сути, статические члены и полиморфизм не сочетаются друг с другом.

Обратите внимание, что доступ к статическим элементам через ссылки - это действительно плохая идея с точки зрения читабельности - это заставляет его выглядеть как будто это зависит от значения ссылки, когда она не правда. Таким образом, ваш текущий код даже не скомпилируется, когда вы переместили str вниз на B и C. Вместо этого вам понадобится

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

Если вам действительно нужен полиморфный доступ к значению (т. Е. Через экземпляр A), тогда одним из вариантов является создание полиморфного геттера:

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

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

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

(и то же самое для C).

Таким образом, вы получаете желаемое поведение с точки зрения отсутствия отдельной переменной на экземпляр, но вы все равно можете использовать ее полиморфно. Для члена экземпляра немного странно возвращать статическое значение, подобное этому, но вы используете значение для полиморфизма типа, в основном ...

6 голосов
/ 04 февраля 2011
public abstract class A {
    private String str;
    public String getStr() { return str;}
    protected void setStr(String str) { this.str = str; }
}

Тогда вы сможете иметь

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

Сеттер и геттер - это мое дополнение, вы можете просто сделать переменную нестатической.

Обновление Если вы хотите иметь статический для подкласса, то вы можете иметь:

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

и затем:

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

Но обычно это плохая идея.

3 голосов
/ 04 февраля 2011

Поместите статическую переменную в каждый подкласс и добавьте (не статический) абстрактный метод к абстрактному суперклассу:

abstract String getStr();

Затем реализуйте метод getStr () в каждом подклассе, возвращая статическое поле этогоспециальный подкласс.

public class B extends A {
 private static String str;

  @Override
  public String getStr() {
    return B.str;
  }
}
2 голосов
/ 04 февраля 2011

Поскольку вы все равно жестко задаете значение или str в подклассах, вы можете сделать что-то вроде этого:

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";
   }
}

Это бы сработало в вашем случае.

Конечно, тогда вы должны вызывать его методом, например так:

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());
    }
}
2 голосов
/ 04 февраля 2011

Будет напечатан вывод, который вы хотите:

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);
    }
}
2 голосов
/ 04 февраля 2011

в системе присутствует только один экземпляр статической переменной.

статическая переменная будет загружаться в систему при запуске до загрузки класса.

причина, по которой время abc печатается, потому чтоВы устанавливаете значение str как abc в последнем.

1 голос
/ 14 февраля 2016

Это то, что я сделал, чтобы избежать реализации одного и того же метода в каждом подклассе.Он основан на ответе Божо.Может быть, это может кому-то помочь.

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");
    }
}
0 голосов
/ 05 ноября 2013

Я думаю, что один из способов сделать это - использовать синглтон для класса B и C для имитации статических методов и полей. Оба могут расширять абстрактный класс A, но будут иметь свои собственные значения str ..

...