2013-03-04 13 views
16

Mam klasę Outer, która ma klasę private Inner.Java: prywatni konstruktorzy z syntezatorami wewnętrznymi

W moim Outer metody klasy, I instancji klasy Inner następująco:

Outer outer = new Outer(); 
Inner inner = outer.new Inner(); 

Kompilator konwertuje ten kod:

Outer outer = new Outer(); 
Inner inner = new Inner(outer, null); 

Korzystanie odbicie pokazuje, że klasa Inner ma następującą syntetyzowane konstruktorzy:

private Outer$Inner(Outer) 
Outer$Inner(Outer,Outer$Inner) 

Ponieważ klasa Inner to private, kompilator dodaje do niej konstruktora private, aby nikt nie mógł utworzyć wystąpienia tej klasy. Oczywiście klasa Outer powinna mieć możliwość utworzenia instancji, więc kompilator dodaje inny prywatny konstruktor pakietu, który z kolei wywołuje prywatny konstruktor. Ponadto, ponieważ konstruktor prywatny pakietu ma w swojej nazwie nazwę $, normalny kod Java nie może go wywołać.

Pytanie: dlaczego syntetyzować jeden prywatny i jeden pakiet-prywatny konstruktor? Dlaczego nie zsyntetyzować tylko prywatnego konstruktora pakietu i z tym skończyć?

+0

@Noofiz te konstruktory są tworzone przez kompilator, bez potrzeby jawnego kodowania ich; stąd nazwałem je syntetyzowanymi. – shrini1000

+0

@Noofiz Jeśli nie rozumiesz pytania, które sugeruję, zostaw to tym, którzy to robią. – EJP

+0

Czy 'Outer $ Inner (zewnętrzny, zewnętrzny Inner)' naprawdę działa poprawnie? Konstruktor otrzymuje instancję tej samej klasy co argument? Dlaczego kompilator powinien dodać taki parametr? –

Odpowiedz

13

Jeśli piszesz kod podobnego,

public class Outer { 
     private class Inner {} 
} 

można zauważyć, że istnieje tylko jeden konstruktor private Outer$Inner(Outer)

Ten konstruktor jest wymagane przez pkt 8.8.9 of the JLS, który mówi, że jeśli nie konstruktor definiuje domyślną należy wygenerować konstruktora iw tym przypadku domyślny konstruktor musi być prywatny,

W klasie, jeśli klasa jest zadeklarowany jako publiczny, domyślny konstruktor jest domyślnie podany jako publiczny modyfikator dostępu (§6.6); jeśli klasa jest deklarowana jako chroniona, wówczas domyślnym konstruktorem jest niejawnie chroniony modyfikator dostępu (§6.6); jeśli klasa jest zadeklarowana jako prywatna, wówczas domyślnemu konstruktorowi przypisuje się niejawnie modyfikator dostępu prywatny (§6.6); w przeciwnym razie domyślny konstruktor ma domyślny dostęp domyślny bez żadnego modyfikatora dostępu.

Jednak podczas tworzenia instancji wystąpienie wewnętrznej wewnątrz kosmicznej z kodem jak,

public class Outer { 
    private class Inner {} 
     public String foo() { 
      return new Inner().toString(); 
     } 
} 

Kompilator musi wygenerować konstruktora że zewnętrzna może legalnie zadzwonić (nie można legalnie nazwać prywatnym domyślny konstruktor, ponieważ jest prywatny). Tak więc nowy kompilator syntetyczny musi zostać wygenerowany przez kompilator.Nowe konstruktor musi być syntetyczne, według section 13.1 of the JLS

Wszelkie konstruktów wprowadzonych przez kompilator, które nie mają odpowiadającego mu konstrukt kodu źródłowego muszą być oznaczone jako syntetycznych, z wyjątkiem domyślnych konstruktorów i inicjalizacji klasy metoda.

Ten drugi konstruktor nie ma odpowiadającej konstrukcji w kodzie źródłowym, więc ten nowy konstruktor musi być syntetyczny. Pierwszy prywatny konstruktor musi zostać wygenerowany, ponieważ JLS wymaga prywatnego domyślnego konstruktora.

+0

Dlaczego tworzenie instancji Innera jest nielegalne? Zewnętrzne wciąż może to zrobić, prawda? Ponadto, ponieważ ten prywatny konstruktor jest generowany przez kompilator, dlaczego nie jest syntetycznym konstruktorem? – shrini1000

+0

Skorygowano moją odpowiedź. – sbridges

+0

To jest teraz jasne. Dzięki! – shrini1000

2

Najbardziej prawdopodobną odpowiedzią jest respektowanie tego, co zadeklarowałeś w swoim kodzie źródłowym. Wykonanie tego nadal pozwala na użycie prywatnego konstruktora przez odbicie, gdy je zadeklarowałeś.

Pozwala to również uniknąć sprawdzenia, czy prywatny konstruktor jest rzeczywiście wywoływany w klasie Inner.

3

To nie jest odpowiedź, która, jak sądzę, została dobrze omówiona przez sbridges. Jest to po prostu działający przykład, który opisuje zachowanie, które opisujesz:

public class Outer { 
    private class Inner { 
    } 

    public static void main(String[] args) { 
     printConstructors(); 

     //only one constructor is printed but two would appear if you 
     //uncommented the line below 

     //new Outer().new Inner(); 
    } 

    private static void printConstructors() { 
     Constructor[] constructors = Outer.Inner.class.getDeclaredConstructors(); 
     for (Constructor c : constructors) { 
      System.out.println(c.toGenericString()); 
     } 
    } 
} 
Powiązane problemy