2010-02-05 3 views
79

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

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

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

+3

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

+4

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

+0

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

ответ

80

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

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

Если ваш ребенок класс «забывает» инициализирует окончательный через super constructor компилятор даст предупреждение ошибку, точно так же, как абстрактный метод не реализован.

+0

У меня нет редактора, чтобы попробовать его здесь, но это было то, что я собирался публиковать. – Tim

+4

«компилятор даст предупреждение». На самом деле, конструктор Child будет пытаться использовать несуществующий конструктор noargs, и это ошибка компиляции (а не предупреждение). –

+0

И добавление к комментарию @Stephen C, «например, когда абстрактный метод не реализован» также является ошибкой, а не предупреждением. –

3

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

+0

@Laurence - мне не ясно, что абстрактные поля могут быть включены. Что-то вроде этого, вероятно, окажет глубокое и фундаментальное влияние на систему типов и на удобство использования языка. (Точно так же, как множественное наследование приносит глубокие проблемы ..., которые могут укусить неосторожного программиста на C++.) –

5

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

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 в подклассы? Я думал, что вы просто хотели иметь метод в базовом классе и не знали, как справляться с этим полем.

+0

Это именно то, что я был в середине набора. Здесь есть только быстрые и мертвые ... За исключением ошибок с ошибкой «Foo» на errorMsg. :) –

+0

Спасибо за указание :) –

+3

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

0

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

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

public abstract class MyClass { 

    public static abstract MyClass createInstance(); 

    // more stuff... 

} 

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

+1

Да, хорошо ... это, вероятно, означает, что вы используете отражение далеко! –

+0

Звучит как странная привычка программирования для меня. Класс.forName (..) пахнет как дефект дизайна. – whiskeysierra

+0

В наши дни мы обычно используем Spring DI для достижения такого рода вещей; но до того, как эта структура стала известной и популярной, мы будем указывать классы реализации в свойствах или XML-файлах, а затем использовать Class.forName() для их загрузки. –

0

Другой вариант - определить поле как общедоступное (окончательное, если хотите) в базовом классе, а затем инициализировать это поле в конструкторе базового класса, в зависимости от того, какой подкласс используется в данный момент. Он немного теневой, поскольку он вводит круговую зависимость. Но, по крайней мере, это не зависимость, которая может когда-либо измениться, т. Е. Подкласс будет либо существовать, либо не существовать, но методы или поля подкласса не могут влиять на значение 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

Вы можете определить параметризованный конструктор в абстрактном классе.

Таким образом, подклассы вынуждены определять свои собственные конструктор и называют супер конструктор, что-то вроде следующего:

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

Проверьте полной пример от here.

Смежные вопросы