2011-07-20 12 views
6

Zasadniczo, co chcę zrobić, to wymuszenie podklasy w celu wywołania abstrakcyjnej metody nadklasy (zaimplementowanej w podklasie), więc nie muszę jej jawnie zapisywać czas tworzę nową podklasę.Jak zmusić podklasę do wywołania abstrakcyjnie zaimplementowanej metody

Napisałem go raz w konstruktorze nadklasy, ponieważ chcę go wymusić na każdą implementację.

public abstract class SupahClass { 
    public SupahClass() { 
     doStuff(); // It IS executed when the subclass constructor is called 
     init(); // NOT executed, even though it's implemented 
    } 

    private void doStuff() { ... }   

    protected abstract void init(); 
} 

public class SomeSubClass extends SupahClass { 

    // The problem lies HERE: this is executed AFTER init() ... so it gets NULL again 
    private TextBox myTextBox = null; 

    public SomeSubClass() { 
     super(); // invokes the super constructor, so init() should be called 
     // I could call init(); here EACH time i create a new subclass... but no :) 
    } 

    @Override 
    public void init() { 
     this.myTextBox = new TextBox(); // Executed BEFORE its declared as null above 
    } 
} 

Oczywiście, nadklasą nie może nazwać go od sposobu jego abstrakcyjnej (tak nieokreślonej), ale jego klasa abstrakcyjna, więc nie może być instanciated, musi powierzyć zadanie jej podklas, więc dlaczego Czy nie nazywają abstrakcyjną, ale teraz wdrożoną metodą?

EDIT Zobacz właściwość podklasy myTextBox i realizację

Które podejście sądzisz zrobić init()? Wyjąć = null w deklaracji majątkowych (duhhh)

lub usunąć init() w nadrzędnej i wyraźnie nazywając go w konstruktorze podklasy (to właśnie chciałem uniknąć, ponieważ będę musiał napisać to 100% czasu ..)

+1

To zadziała. To zdecydowanie nie jest twój rzeczywisty kod - twój rzeczywisty kod musi się różnić od tego w jakiś ważny sposób. Dlaczego nie zamieścić swojego prawdziwego kodu i daj nam rzucić okiem? –

+0

Rzeczywiście!zobacz mój komentarz na temat odpowiedzi Jona Skeeta i moje zaktualizowane pytanie :) – dominicbri7

Odpowiedz

2

Można to naprawić zmieniając deklarację:

private TextBox myTextBox; 

Przypisanie NULL niczemu nie służy. Gdyby nie było nadklasy, nie zrobiłoby to nic, ponieważ pola zostały zresetowane do wartości null. Ponieważ istnieje superklasa, działa ona jak strzała na stopę. Tak więc, pozbądź się tego.

+0

Tak, właśnie to zrobiłem, aby to naprawić, ale mnie zmusiłeś zdaje sobie sprawę, że to w ogóle nie jest użyteczne i jest naprawdę złą praktyką. Nie mogę nawet wymyślić przypadku, w którym byłoby to przydatne ... Sądzę, że usunę te zadania wszędzie, gdzie ich użyłem. Ale poza tym, co myślisz o mojej obecnej implementacji (wywoływania takich metod w konstruktorach)? – dominicbri7

+0

Jest to, jak wszyscy ci powiedzą, niebezpieczna praktyka. Otwiera drzwi do całkiem sprytnych błędów (metody działające przeciwko częściowo skonstruowanym obiektom, niebezpiecznej publikacji obiektów itp.). Jednak nie powoduje błędów w sposób nieunikniony - do ich wprowadzenia wciąż należy programista. Jeśli nie było korzyści z wywoływania metody podklasy od konstruktora, radziłbym, abyś nigdy tego nie robił. Często jest to prosty i jasny sposób na uzyskanie pożądanego zachowania bez prostej alternatywy. Pojawia się zatem opinia, czy wartość połączenia przewyższa ryzyko błędów. –

+0

To jest wyrok, który zależy od kontekstu - od tego, jak złożone są niezmienniki w klasie, od tego, jak zaawansowani są programiści pracujący nad bazą kodową, od tego, czy klasa zostanie szeroko rozszerzona, czy też ograniczona do niewielkiej części systemu, itd. To konkretne pytanie, na które nie mogę udzielić ogólnej odpowiedzi. Oczywiście Jon Skeet może. –

8

Nie mogę tego odtworzyć. init()będzie będzie wywoływane, zakładając, że nie są zgłaszane żadne wyjątki. Krótki, ale pełny przykład:

abstract class Superclass { 
    public Superclass() { 
     init(); 
    } 

    protected abstract void init(); 
} 

class Subclass extends Superclass { 
    public Subclass() { 
     super(); 
    } 

    @Override 
    public void init() { 
     System.out.println("Subclass.init called"); 
    } 
} 

public class Test {  
    public static void main(String[] args) throws Exception { 
     new Subclass(); 
    } 
} 

Który drukuje "Subclass.init nazwie" zgodnie z oczekiwaniami. Podejrzewam, że coś innego jest nie tak w kodzie, którego nam nie pokazałeś.

Należy pamiętać, że wywoływanie metod wirtualnych w konstruktorze jest ryzykowne - podklasa nie została jeszcze zainicjalizowana - wszystkie zmienne będą miały na przykład wartości domyślne. Zasadniczo należy unikać takiego wzoru.

+1

chciałem to zasugerować, ale nie wstydź się, że zostałem pokonany przez Jona Skeeta. – Leon

+0

Masz absolutną rację. Pomyślałem, że Init nigdy nie został wywołany, ponieważ w pewnym momencie właściwości podklasy miały wartość zerową, gdy miała zostać zainicjowana. Masz dwa razy więcej prawdy, gdy powiedziałeś, że jest to ryzykowne przedsięwzięcie, ponieważ podklasa nie została zainicjalizowana: to był dokładnie mój problem. Zobacz moje zaktualizowane pytanie na końcu, aby uzyskać więcej informacji, i bardzo chciałbym poznać twoją opinię na temat podejścia, które według ciebie powinienem użyć .. :) – dominicbri7

2

Klasy abstrakcyjne można wywoływać abstrakcyjne metody.

0

Czy użyłeś debuggera, aby zrozumieć, co robi kod?

Kolejność konstrukcji to 1. Konstruktor klasy bazowej 2. Inicjalizacja wszystkich elementów klasy pochodnej 3. Konstruktor klasy pochodnej.

W twoim przypadku krok 1 inicjuje element (w init klasy pochodnej - wywoływanej przez wywołanie polimorficzne), ale krok 2 ustawia go na wartość null.

Powiązane problemy