2009-07-06 19 views
10

Rozważmy następujący przypadek:Java wewnętrzna klasa widoczność puzzle

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { } 
} 

Od ostrzeżenie w Eclipse cytuję, że: kompilator Javy emuluje A.b konstruktora() przy syntetycznej metody akcesor. Przypuszczam, że kompilator teraz idzie do przodu i tworzy dodatkowy konstruktor "pod wodą" dla B.

Mam wrażenie, że jest to dość dziwne: dlaczego klasa B nie będzie widoczna jako a.k.o. pole w A? I: czy to oznacza, że ​​klasa B nie jest już prywatna w czasie wykonywania? Oraz: dlaczego zachowuje chronione słowo kluczowe dla klasy B różne?

public class A { 
    public A() { b = new B(); } 
    B b; 
    protected class B { } 
} 

Odpowiedz

24

klasy wewnętrzne są zasadniczo hack wprowadzony w Javie 1.1. JVM w rzeczywistości nie ma żadnej koncepcji klasy wewnętrznej, więc kompilator musi ją usunąć. Kompilator generuje klasę B "na zewnątrz" klasy A, ale w tym samym pakiecie, a następnie dodaje do niej syntetyczne akcesoria/konstruktory, aby umożliwić A dostęp do niej.

Po nadaniu B zabezpieczonego konstruktora A może uzyskać dostęp do tego konstruktora, ponieważ znajduje się w tym samym pakiecie, bez konieczności dodawania syntetycznego konstruktora.

+0

OK, widzę to. Dla mnie oznacza to, że uniknę użycia klas wewnętrznych, jak w przykładzie, może tylko doprowadzić do zamieszania. – Gerard

+2

Nie zawracam ci głowy. To ostrzeżenie dla konkretnego kompilatora nie jest specjalnie przydatne dla nikogo, metody syntetyczne są używane przez cały czas w klasach wewnętrznych i nie mają znaczącego wpływu. – skaffman

+0

IMHO metody syntetyczne były niepotrzebnym dodatkiem do języka. Samo użycie zakresu pakietu dla "prywatnych" członków (które i tak robi kompilator pod maską) było zadowalającym rozwiązaniem. – finnw

-1

Trzeba użyć

this.new B(); 
+0

Przepraszam, ale to.new B(); daje takie samo ostrzeżenie i zachowanie. – Gerard

+2

@Rats: to nie ma znaczenia dla problemu. "Ta" kwalifikacja jest ukryta. – skaffman

2

Dostęp do class B i jego konstruktora nie musi być taki sam. Możesz mieć prywatną klasę wewnętrzną z konstruktorem o zakresie pakietów i to właśnie robię zazwyczaj.

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { 
    B() { } 
    } 
} 
+0

Ale w jaki sposób możesz mieć pakiet-prywatną instancję właściwie prywatnej klasy? – einpoklum

4

Wiem, że to pytanie jest teraz prawie trzy lata, ale uważam, że część pytania wciąż nie odpowiedział:

A: to znaczy, że klasa B nie jest już prywatnym W czasie wykonywania?

Carlos Heubergers komentarz na skaffmans odpowiedź sugeruje, klasa B wciąż private do innych klas w pakiecie.

Prawdopodobnie jest odpowiedni dla języka programowania Java, tj. Nie można odwoływać się do klasy B z innej klasy. Przynajmniej nie bez użycia refleksji (z pomocą której również prywatni członkowie klasy mogą być dostęp z zewnątrz), ale jest to kolejna kwestia.

Ale ponieważ JVM nie ma żadnej koncepcji klasy wewnętrznej (jak stany skaffman), zadałem sobie pytanie, w jaki sposób widoczność "dostępna przez jedną klasę" jest realizowana na poziomie bajtodu. Odpowiedź: Nie jest w ogóle realizowana, ponieważ JVM wewnętrzna klasa wygląda jak normalna prywatna klasa pakietowa. To znaczy, jeśli sam napiszesz kod bajtowy (lub modyfikujesz go wygenerowany przez kompilator), możesz bez problemu uzyskać dostęp do klasy B.

Dostęp do wszystkich metod syntezatora syntetycznego można uzyskać ze wszystkich klas w tym samym pakiecie. Jeśli więc przypiszesz wartość do prywatnego pola klasy A w metodzie klasy B, zostanie wygenerowana syntetyczna metoda dostępu z domyślną (tj. Niepodzielną pakietowo) widocznością w klasie A (o nazwie podobnej do access$000), która ustawi dla Ciebie wartość. Ta metoda ma być wywołana tylko z klasy B (i rzeczywiście może być wywołana tylko z tego przy użyciu języka Java).Ale z punktu widzenia JVM jest to tylko metoda jak każda inna i może być wywołana przez dowolną klasę.

Tak więc, aby odpowiedzieć na pytanie:

  • Od języków Java punktu widzenia, klasa B jest i pozostaje prywatne.
  • Z perspektywy JVM klasa B (lub lepiej: klasa A$B) nie jest prywatna.
+0

poprawne, ale to nie jest to, co sugerowałem. Napisałem, że "członkowie ** nadal są prywatni" - chodzi mi o pola klasy, a nie o ** klasę ** samą w sobie interpretowaną przez ciebie! Również to pytanie dotyczy [tag: java], a nie [tag: bytecode] (generacja, modyfikacja, _hacking_ ...). –

+0

@CarlosHeuberger oczywiście nie było to dla ciebie obrazą! Z pewnością członkowie klasy B są nadal prywatni, ale klasa B (jako rodzaj członka klasy A) nie jest (z punktu widzenia JVM) i było to oryginalne pytanie Gerarda. I tak, pytanie dotyczy języka Java, ale jak podano w tagu wiki: "Java jest językiem programowania i ** środowiskiem uruchomieniowym **". Środowisko wykonawcze to JVM, a JVM per se nie ma nic wspólnego z językiem programowania Java, ale interpretuje tylko kod bajtowy. – siegi