Почему не абстрактные поля? - PullRequest
89 голосов
/ 06 февраля 2010

Почему классы Java не могут иметь абстрактные поля, как они могут иметь абстрактные методы?

Например: у меня есть два класса, которые расширяют один и тот же абстрактный базовый класс. Каждый из этих двух классов имеет метод, который идентичен, за исключением константы String, которая является сообщением об ошибке внутри них. Если бы поля могли быть абстрактными, я мог бы сделать эту константу абстрактной и перенести метод в базовый класс. Вместо этого мне нужно создать абстрактный метод, в данном случае называемый getErrMsg(), который возвращает строку, переопределить этот метод в двух производных классах, а затем я могу вызвать метод (который теперь вызывает абстрактный метод).

Почему я не мог просто сделать поле абстрактным для начала? Мог ли Java быть спроектирован так, чтобы это было возможно?

Ответы [ 5 ]

88 голосов
/ 06 февраля 2010

Вы можете сделать то, что вы описали, имея последнее поле в вашем абстрактном классе, которое инициализируется в его конструкторе (непроверенный код):

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

Если ваш дочерний класс "забывает" инициализировать финал с помощью супер-конструктора, компилятор выдаст предупреждение об ошибке, как если бы абстрактный метод не реализован.

6 голосов
/ 06 февраля 2010

Не вижу в этом смысла. Вы можете переместить функцию в абстрактный класс и просто переопределить какое-то защищенное поле. Я не знаю, работает ли это с константами, но эффект тот же:

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 голосов
/ 06 февраля 2010

Очевидно, что мог бы быть спроектирован для этого, но под прикрытием ему все равно пришлось бы выполнять динамическую диспетчеризацию и, следовательно, вызов метода. Дизайн Java (по крайней мере, в первые дни) был, в некоторой степени, попыткой быть минималистичным. То есть дизайнеры старались избегать добавления новых функций, если их можно легко смоделировать с помощью других функций, уже имеющихся в языке.

0 голосов
/ 16 июня 2017

Другой вариант - определить поле как общедоступное (если хотите, финальное) в базовом классе, а затем инициализировать это поле в конструкторе базового класса, в зависимости от того, какой подкласс используется в настоящее время. Это немного сомнительно, поскольку вводит круговую зависимость. Но, по крайней мере, это не зависимость, которая когда-либо может измениться, то есть подкласс будет либо существовать, либо не существовать, но методы или поля подкласса не могут влиять на значение 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;
    }
  }
}
0 голосов
/ 06 февраля 2010

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

Мне часто хотелось, чтобы я мог объявить метод, подобный следующему в Java:

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

По сути, я хотел бы настаивать на том, чтобы конкретные реализации моего родительского класса обеспечивали статический метод фабрики с определенной сигнатурой. Это позволило бы мне получить ссылку на конкретный класс с Class.forName() и быть уверенным, что я смогу создать его в соответствии с соглашением по своему выбору.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...