Почему бы не абстрактные поля?

Почему классы 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);
    }
}

Посмотрите полный пример из здесь.