Почему бы не абстрактные поля?
Почему классы Java не могут иметь абстрактные поля, например, они могут иметь абстрактные методы?
Например: у меня есть два класса, которые расширяют один и тот же абстрактный базовый класс. У этих двух классов есть метод, который идентичен, за исключением константы String, которая, как представляется, является сообщением об ошибке внутри них. Если поля могут быть абстрактными, я мог бы сделать эту константу абстрактной и вывести метод в базовый класс. Вместо этого я должен создать абстрактный метод, называемый getErrMsg()
в этом случае, который возвращает String, переопределяет этот метод в двух производных классах, а затем я могу вытащить метод (который теперь вызывает абстрактный метод).
Почему я не могу просто создать полевую абстракцию? Может ли Java быть разработан таким образом?
Ответы
Ответ 1
Вы можете сделать то, что вы описали, имея конечное поле в своем абстрактном классе, которое инициализируется в его конструкторе (непроверенный код):
abstract class Base {
final String errMsg;
Base(String msg) {
errMsg = msg;
}
abstract String doSomething();
}
class Sub extends Base {
Sub() {
super("Sub message");
}
String doSomething() {
return errMsg + " from something";
}
}
Если ваш дочерний класс "забывает" инициализировать окончательный через супер-конструктор, компилятор даст предупреждение ошибку, точно так же, как если абстрактный метод не реализован.
Ответ 2
Я не вижу в этом никакого смысла. Вы можете переместить функцию в абстрактный класс и просто переопределить какое-либо защищенное поле. Я не знаю, работает ли это с константами, но эффект тот же:
public abstract class Abstract {
protected String errorMsg = "";
public String getErrMsg() {
return this.errorMsg;
}
}
public class Foo extends Abstract {
public Foo() {
this.errorMsg = "Foo";
}
}
public class Bar extends Abstract {
public Bar() {
this.errorMsg = "Bar";
}
}
Итак, вы хотите, чтобы вы выполняли реализацию/переопределение/независимо от errorMsg
в подклассах? Я думал, что вы просто хотели иметь метод в базовом классе и не знали, как работать с этим полем.
Ответ 3
Очевидно, это могло быть спроектировано таким образом, но под крышками все равно придется выполнять динамическую отправку и, следовательно, вызов метода. Дизайн Java (по крайней мере, в первые дни) был, в некоторой степени, попыткой быть минималистским. То есть дизайнеры пытались избежать добавления новых функций, если их можно было легко имитировать с помощью других функций, уже существующих на этом языке.
Ответ 4
Читая свой титул, я думал, что вы имеете в виду абстрактные члены экземпляра; и я не мог много использовать для них. Но абстрактные статические члены - это совсем другое дело.
Я часто хотел, чтобы я мог объявить в Java способ, подобный приведенному ниже:
public abstract class MyClass {
public static abstract MyClass createInstance();
// more stuff...
}
В принципе, я хотел бы настаивать на том, что конкретные реализации моего родительского класса предоставляют статический метод factory с определенной сигнатурой. Это позволило бы мне получить ссылку на конкретный класс с Class.forName()
и быть уверенным, что я мог бы построить его в согласии по своему выбору.
Ответ 5
Другой вариант - определить поле как общедоступное (окончательное, если хотите) в базовом классе, а затем инициализировать это поле в конструкторе базового класса, в зависимости от того, какой подкласс используется в данный момент. Это немного теневое, поскольку оно вводит круговую зависимость. Но, по крайней мере, это не зависимость, которая может когда-либо измениться, т.е. Подкласс будет либо существовать, либо не существовать, но методы или поля подкласса не могут влиять на значение field
.
public abstract class Base {
public final int field;
public Base() {
if (this instanceof SubClassOne) {
field = 1;
} else if (this instanceof SubClassTwo) {
field = 2;
} else {
// assertion, thrown exception, set to -1, whatever you want to do
// to trigger an error
field = -1;
}
}
}
Ответ 6
Вы можете определить параметризованный конструктор в абстрактном классе.
Таким образом, подклассы вынуждены определять свои собственные конструкторы и вызывать супер-конструктор, что-то вроде следующего:
public abstract class TransactionManager {
private String separator;
public TransactionManager(String separator) {
this.separator = separator;
}
}
Тогда ваш конкретный класс будет выглядеть следующим образом:
public class TransactionManagerFS extends TransactionManager{
// The IDE forces you to implement constructor.
public TransactionManagerFS(String separator) {
super(separator);
}
}
Посмотрите полный пример из здесь.