29

Czy w Javie jest jakiś sposób na zainicjowanie pola przed uruchomieniem super-konstruktora?Inicjalizuj pole przed uruchomieniem super-konstruktora?

Nawet najbrzydszy hacki mogę wymyślić są odrzucane przez kompilator:

class Base 
{ 
    Base(String someParameter) 
    { 
     System.out.println(this); 
    } 
} 

class Derived extends Base 
{ 
    private final int a; 

    Derived(String someParameter) 
    { 
     super(hack(someParameter, a = getValueFromDataBase())); 
    } 

    private static String hack(String returnValue, int ignored) 
    { 
     return returnValue; 
    } 

    public String toString() 
    { 
     return "a has value " + a; 
    } 
} 

Uwaga: Problem zniknął, kiedy przełącza się z delegacją do dziedziczenia, ale nadal chciałbym wiedzieć.

+1

Czy próbujesz wstępnie zainicjalizować pole 'a'? – Woot4Moo

+1

Nie sądzę, że możesz to zrobić. Każda inicjalizacja wykonywana w klasie (nawet jeśli znajduje się poza konstruktorem) jest przenoszona do każdego konstruktora po wywołaniu 'super'. Tak więc super konstruktor jest zawsze uruchamiany przed inicjalizacją pola. –

+5

@FredOverflow, ponieważ 'a' jest dostępny tylko w' Derived', dlaczego ma znaczenie, że zostanie zainicjowany * przed wywołaniem * 'super()'? Inicjowanie go zaraz po wykonaniu nie ma wpływu na przykład dostarczany przez ciebie (chyba że wywołasz nadpisaną metodę z konstruktora Base, który zaczyna pachnieć dość podejrzanie). – assylias

Odpowiedz

20

Nie, nie ma sposobu, aby to zrobić.

Zgodnie z language specs, zmienne instancji nie są inicjowane, dopóki nie zostanie wykonane wywołanie super().

Są to kroki wykonywane podczas etapu konstruktora tworzenia instancji klasy, wykonane z linku:

  1. Przypisywanie argumenty dla konstruktora do nowo utworzonych zmiennych parametrów do tego konstruktora pw.
  2. Jeżeli konstruktor rozpoczyna się inwokacją wyraźną konstruktora (§8.8.7.1) innego konstruktora w tej samej klasy (przy tym), następnie ocenić argumenty i proces, który konstruktor inwokacja rekurencyjnie za pomocą tych samych pięciu kroków. Jeśli wywołanie konstruktora kończy się nagle, procedura ta kończy się nagle z tego samego powodu; w przeciwnym razie kontynuuj od kroku 5.
  3. Ten konstruktor nie rozpoczyna się od jawnego konstruktora wywołania innego konstruktora w tej samej klasie (używając tego). Jeśli ten konstruktor jest dla klasy innej niż Object, to ten konstruktor rozpocznie się od jawnego lub niejawnego wywołania konstruktora nadklasy (przy użyciu super). Oceń argumenty i przetwórz wywołanie konstruktora klasy superkomputerowej rekurencyjnie, używając tych samych pięciu kroków. Jeśli to wywołanie konstruktora zostanie nagle zakończone, procedura ta zakończy się nagle z tego samego powodu . W przeciwnym razie przejdź do kroku 4.
  4. Wykonaj inicjatorów instancji i zmiennej instancji inicjatorów dla tej klasy, przypisanie wartości zmiennej instancji inicjalizatory do odpowiednich zmiennych przykład w lewej do prawej Kolejność, w jakiej pojawiają się one tekstowo w źródle kodu dla klasy. Jeśli wykonanie któregokolwiek z tych inicjalizatorów spowoduje powstanie wyjątku, wówczas żadne dalsze inicjalizatory nie są przetwarzane i ta procedura kończy się nagle z tym samym wyjątkiem. W przeciwnym razie kontynuuj od kroku 5.
  5. Wykonaj resztę treści tego konstruktora. Jeśli wykonanie zakończy się nagle, to ta procedura kończy się nagle dla tego samego powodu z . W przeciwnym razie procedura ta kończy się normalnie.
+0

czy jesteś tego pewien? Natknąłem się na to: _...Może się zdarzyć, że B widzi te zdarzenia w następującej kolejności (a kompilator może również zmienić kolejność takich instrukcji): przydzielić pamięć, przypisać odwołanie do zasobu, wywołać konstruktor. Przypuśćmy, że wątek B pojawia się po przydzieleniu pamięci i ustawieniu pola zasobów, ale zanim konstruktor zostanie wywołany .._ z @BrianGoetz w artykule DCL, więc kompilator najwidoczniej może dokonać pewnych optymalizacji, które obejmują także przypisanie odwołania i wywołanie konstruktor po tym! – mkilic

1

To zakazane przez Java language specification (section 8.8.7):

Pierwsze oświadczenie z ciała konstruktora może być jawne wywołanie innego konstruktora tej samej klasy lub bezpośrednich nadrzędnej.

Ciało konstruktor powinien wyglądać następująco:

ConstructorBody:

{ ExplicitConstructorInvocationopt BlockStatementsopt } 
4

Jak mówili inni, nie można zainicjować pole instancji przed wywołaniem konstruktora nadklasy.

Ale są rozwiązania. Jedną z nich jest utworzenie klasy fabrycznej, która pobiera wartość i przekazuje ją do konstruktora klasy pochodnej.

class DerivedFactory { 
    Derived makeDerived(String someParameter) { 
     int a = getValueFromDataBase(); 
     return new Derived(someParameter, a); 
    } 
} 


class Derived extends Base 
{ 
    private final int a; 

    Derived(String someParameter, int a0) { 
     super(hack(someParameter, a0)); 
     a = a0; 
    } 
    ... 
} 
10

Super konstruktor będzie działać w każdym przypadku, ale skoro mówimy o „najbrzydszy hacków”, możemy skorzystać z tej

public class Base { 
    public Base() { 
     init(); 
    } 

    public Base(String s) { 
    } 

    public void init() { 
    //this is the ugly part that will be overriden 
    } 
} 

class Derived extends Base{ 

    @Override 
    public void init(){ 
     a = getValueFromDataBase(); 
    } 
} 

nigdy sugerować przy użyciu tego rodzaju hacki.

+0

Nie sądzę, że działa w Javie, i nie bez powodu. Szukam referencji ... –

+0

To działa, testowane ... –

+1

+1 - Masz rację. Ick. Myślałem o C++. Nadal - nie sądzę, że hack jest niezbędny - zobacz moją odpowiedź. –

4

Mam sposób to zrobić.

class Derived extends Base 
{ 
    private final int a; 

    // make this method private 
    private Derived(String someParameter, 
        int tmpVar /*add an addtional parameter*/) { 
     // use it as a temprorary variable 
     super(hack(someParameter, tmpVar = getValueFromDataBase())); 
     // assign it to field a 
     a = tmpVar; 
    } 

    // show user a clean constructor 
    Derived(String someParameter) 
    { 
     this(someParameter, 0) 
    } 

    ... 
} 
Powiązane problemy