Java: статическое поле в абстрактном классе
Проблема:
Я только начал работать с Java и столкнулся с проблемой, связанной с статическими переменными в абстрактных классах. У меня есть следующий пример, который лучше всего иллюстрирует суть моей проблемы:
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
Как видно, переменная str
является статической и разделяется между всеми экземплярами классов B
и C
, поэтому и b.str
, и c.str
выводят одно и то же значение "abc".
Желаемое решение:
Я хотел бы, чтобы каждая подкласс, создающий экземпляр суперкласса, имел собственную переменную класса. При этом я хочу иметь возможность ссылаться на эту переменную через идентификатор или метод, определенный в абстрактном суперклассе.
В идеале, я хотел бы получить следующий вывод:
b.str = 123
c.str = abc
Можно ли это реализовать?
5 ответ(ов)
Если вы хотите, чтобы классы 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).
Таким образом, вы получите нужное поведение, не имея отдельной переменной для каждого экземпляра, и при этом сможете использовать его полиморфно. Это немного странно, когда экземплярный метод возвращает статическое значение, но вы используете это значение для полиморфизма по типу...
Ваша реализация выглядит хорошо, и если вы хотите, чтобы класс A
имел возможность устанавливать и получать значение переменной str
, то добавление методов доступа (геттеров и сеттеров) — это правильный подход.
Вот пример кода с вашими дополнениями:
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(); // Получаем значение str
В данном случае, чтобы B
мог получить значение str
, класс B
должен наследоваться от класса A
и может использовать метод getStr()
.
Обновление: Если вы хотите использовать статические переменные для подклассов, вы можете сделать что-то вроде этого:
protected static Map<Class<?>, String> values = new HashMap<>();
public abstract String getValue();
И затем реализовать методы getValue
и setValue
следующим образом:
public String getValue() {
return values.get(getClass());
}
public void setValue(String value) {
values.put(getClass(), value);
}
Однако стоит отметить, что использование статических переменных для каждого подкласса может привести к потенциальным проблемам с потокобезопасностью и управлением данными, поэтому такой подход обычно не рекомендуется. Лучше использовать экземпляры класса для хранения состояния.
Вам нужно создать абстрактный класс с абстрактным методом, а затем реализовать этот метод в каждом подклассе, при этом используя статическую переменную. Вот как это можно сделать:
- Создайте абстрактный класс с абстрактным методом:
abstract class A {
abstract String getStr();
}
- В каждом подклассе объявите статическую переменную и реализуйте метод
getStr()
, чтобы он возвращал значение этой статической переменной:
public class B extends A {
private static String str = "Hello from B";
@Override
public String getStr() {
return B.str;
}
}
public class C extends A {
private static String str = "Hello from C";
@Override
public String getStr() {
return C.str;
}
}
Таким образом, каждый подкласс имеет свою собственную статическую переменную str
, и метод getStr()
возвращает значение этой переменной. Вы можете добавлять столько подклассов, сколько вам нужно, и каждый из них будет иметь свою реализацию метода getStr()
, возвращающую соответствующее значение.
Вопрос: Почему статическая переменная загружается в систему только один раз, и почему оба раза при выводе печатается "abc"?
Ответ: Статическая переменная в Java (или в других языках программирования) действительно загружается в систему в самом начале, до загрузки класса. Это объясняет, почему существует только один экземпляр этой переменной для всего класса, и все экземпляры классов имеют доступ к одной и той же статической переменной.
Что касается вывода "abc" в обоих случаях, причина заключается в том, что вы устанавливаете значение переменной str
равным "abc" в последнем месте кода. Таким образом, когда вы обращаетесь к этой статической переменной, она уже была обновлена в процессе выполнения программы. Поэтому оба раза, когда вы выводите значение str
, результат будет "abc".
Если у вас есть дополнительные вопросы о работе со статическими переменными, не стесняйтесь задавать!
Этот код выведет на экран нужный вам результат:
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);
}
}
Когда вы выполните этот код, он создаст экземпляры классов B
и C
, которые инициализируют свои статические поля str
. Результатом выполнения программы будет:
B.str = 123
C.str = abc
Статические поля str
являются общей частью классов B
и C
, и их значения изменяются при создании новых объектов этих классов. Поэтому, при выводе значений B.str
и C.str
, вы получите соответственно "123" и "abc".
Можно ли использовать статический метод в абстрактном классе?
Можно ли создать экземпляр абстрактного класса?
Почему в Java нельзя переопределять статические методы?
Почему нельзя вызывать абстрактные функции из абстрактных классов в PHP?
Java: Статические transient-поля