2010-02-05 18 views
79

Dlaczego klasy Java nie mogą zawierać abstrakcyjnych pól, ponieważ mogą zawierać abstrakcyjne metody?Dlaczego nie abstrakcyjne pola?

Na przykład: Mam dwie klasy, które rozszerzają tę samą abstrakcyjną klasę podstawową. Każda z tych dwóch klas ma metodę, która jest identyczna, z wyjątkiem stałej String, która w tym przypadku jest komunikatem o błędzie. Jeśli pola mogłyby być abstrakcyjne, mógłbym uczynić to stałym abstraktem i przenieść metodę do klasy bazowej. Zamiast tego, muszę utworzyć abstrakcyjną metodę o nazwie getErrMsg() w tym przypadku, która zwraca String, nadpisuje tę metodę w dwóch klasach pochodnych, a następnie mogę wywołać metodę (która teraz nazywa metodę abstrakcyjną).

Dlaczego nie mogę po prostu zrobić tego streszczenia na początek? Czy Java mogła zostać zaprojektowana, aby to umożliwić?

+3

Wygląda na to, że mógłbyś pominąć cały ten problem, czyniąc pole niestałym i po prostu dostarczając wartość przez konstruktor, kończąc na 2 wystąpieniach jednej klasy zamiast 2 klas. – Nate

+4

Tworząc pola abstrakcyjne w super klasie, masz określone, że każda podklasa musi mieć to pole, więc nie różni się to od pola nie abstrakcyjnego. –

+0

@peter, nie jestem pewien, czy podążam za twoim punktem. jeśli w klasie abstrakcyjnej określono stałą nie abstrakcyjną, to jej wartość jest stała również dla wszystkich podklas. jeśli byłaby abstrakcyjna, to jego wartość musiałaby zostać wdrożona/dostarczona przez każdą podklasę. więc nie byłoby to w ogóle takie samo. – liltitus27

Odpowiedz

80

Można zrobić to, co opisane poprzez końcową boiska w swojej abstrakcyjnej klasy, która zostaje zainicjowany w jego konstruktora (kod niesprawdzone):

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

Jeśli klasa dziecko „zapomina”, aby zainicjować ostateczna poprzez super konstruktor kompilator da ostrzeżenie błąd, tak jak wtedy, gdy metoda abstrakcyjna nie jest zaimplementowana.

+0

Nie mam edytora, aby wypróbować go tutaj, ale to właśnie miałem zamiar opublikować. – Tim

+4

"kompilator da ostrzeżenie". W rzeczywistości konstruktor Child będzie próbował użyć nieistniejącego konstruktora noargs i jest to błąd kompilacji (nie ostrzeżenie). –

+0

Dodanie do komentarza @Stephen C, "jak wtedy, gdy abstrakcyjna metoda nie jest zaimplementowana" jest również błędem, a nie ostrzeżeniem. –

3

Oczywiście można było może na to pozwolić, ale pod pokrywami nadal musiałaby odbywać się dynamiczna wysyłka, a więc wywołanie metody. Konstrukcja Javy (przynajmniej na początku) była w pewnym stopniu próbą minimalistyczną. Oznacza to, że projektanci starali się unikać dodawania nowych funkcji, jeśli mogliby być łatwo symulowani przez inne funkcje już w tym języku.

+0

@Laurence - nie jest * oczywiste dla mnie, że abstrakcyjne pola mogły zostać uwzględnione. Coś takiego może mieć głęboki i fundamentalny wpływ na system typów i na użyteczność języka. (W ten sam sposób, że wielokrotne dziedziczenie przynosi poważne problemy ... które mogą zagrozić nieostrożnemu programistom C++.) –

5

Nie widzę sensu w tym. Możesz przenieść funkcję do klasy abstrakcyjnej i po prostu zastąpić pewne chronione pole. Nie wiem, czy to działa ze stałymi ale efekt jest ten sam:

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

Więc chodzi o to, że chcesz, aby wymusić wdrożenia/nadpisywanie/cokolwiek z errorMsg w podklasach? Myślałem, że po prostu chcesz mieć tę metodę w klasie bazowej i nie wiesz, jak sobie poradzić z tym polem.

+0

To właśnie byłem w trakcie pisania. Tu jest tylko ten szybki i martwy ... Z wyjątkiem tego, że napisałeś błędnie "Foo" na errorMsg. :) –

+0

Dzięki za wskazanie :) –

+3

Cóż, oczywiście mógłbym to zrobić. I myślałem o tym. Ale dlaczego muszę ustawić wartość w klasie bazowej, gdy wiem, że zamierzam zastąpić tę wartość w każdej wyprowadzonej klasie? Twój argument może być również użyty do stwierdzenia, że ​​nie ma żadnej wartości w dopuszczaniu metod abstrakcyjnych. –

0

Czytając tytuł, myślałem, że odnosisz się do abstrakcyjnych członków instancji; i nie widziałem dla nich większego pożytku. Ale abstrakcyjni statyczni członkowie to zupełnie inna sprawa.

mam często żałował, że mogę zadeklarować metodę jak poniżej w Javie:

public abstract class MyClass { 

    public static abstract MyClass createInstance(); 

    // more stuff... 

} 

Zasadniczo chciałbym twierdzą, że konkretne implementacje mojej klasy dominującej zapewniają metody statycznej fabryki z konkretnego podpisu . To pozwoliłoby mi uzyskać odniesienie do konkretnej klasy z Class.forName() i być pewnym, że mógłbym ją utworzyć w konwencji według mojego wyboru.

+1

No cóż ... to prawdopodobnie oznacza, że ​​używasz refleksji za dużo !! –

+0

To dla mnie dziwny nawyk programowania. Class.forName (..) pachnie jak wada projektowa. – whiskeysierra

+0

Obecnie w zasadzie zawsze używamy Spring DI do realizacji tego typu rzeczy; ale zanim ten framework stał się znany i popularny, określilibyśmy klasy implementacji we właściwościach lub plikach XML, a następnie użył Class.forName(), aby je załadować. –

0

Inną opcją jest zdefiniowanie pola jako publicznego (końcowego, jeśli chcesz) w klasie bazowej, a następnie zainicjowanie tego pola w konstruktorze klasy bazowej, w zależności od tego, która podklasa jest aktualnie używana. Jest to trochę podejrzane, ponieważ wprowadza zależność cykliczną. Ale przynajmniej nie jest to zależność, która kiedykolwiek może się zmienić - tj. Podklasa będzie istnieć lub nie istnieje, ale metody lub pola podklasy nie mogą wpływać na wartość 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

Można zdefiniować sparametryzowany konstruktor w klasie abstrakcji.

ten sposób podklasy są zmuszeni do zdefiniowania własnych konstruktorów i nazwać super konstruktora, coś jak następuje:

public abstract class TransactionManager { 
    private String separator; 

    public TransactionManager(String separator) { 
     this.separator = separator; 
    } 
} 

Wtedy twój beton klasy wyglądałby następująco:

public class TransactionManagerFS extends TransactionManager{ 

    // The IDE forces you to implement constructor. 
    public TransactionManagerFS(String separator) { 
     super(separator); 
    } 
} 

sprawdzenia pełnej przykład z here.