Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Reflection] Öffentlichen Methoden von privaten inneren Klassen?

0 views
Skip to first unread message

Martin Carpella

unread,
May 12, 2005, 8:34:42 AM5/12/05
to
Hallo!

Im Zuge von Arbeiten an einem Projekt bin ich auf ein interessantes
Phänomen unter 1.4.2_05 gestoßen.

Man nehme folgende Sequenz, die meiner Meinung nach völlig legitim wäre
(das Beispiel ist etwas an den Haaren herbeigezogen, aber es
veranschaulicht das Problem):

try {
List l = new LinkedList();
Iterator it = l.iterator();
System.out.println(it.getClass().toString());
System.out.println();

Method m = it.getClass().getMethod("hasNext", new Class[] {});
m.invoke(it, new Object[] {}); // fail
} catch (Exception e) {
e.printStackTrace();
}

Dies liefert folgende Ausgabe:
| class java.util.LinkedList$ListItr
|
| java.lang.IllegalAccessException: Class Test can not access a member of
| class java.util.LinkedList$ListItr with modifiers "public"
| at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
| at java.lang.reflect.Method.invoke(Method.java:317)
| at Test.main(Test.java:14)

LinkedList$ListItr ist private, das ist mir klar, aber es implementiert
eine öffentliche Methode eines Interfaces, somit sollte meines Erachtens
auch ein Aufruf mittels Reflection möglich sein, oder nicht?

Beste Grüße,
Martin

Martin Carpella

unread,
May 12, 2005, 8:40:51 AM5/12/05
to
Ok, self-reply ist angebracht, aber ich seh's immer noch nicht ganz ein:

Wenn man die Methode vom Interface holt, dann funktioniert es, also wenn
man

List l = new LinkedList();
Iterator it = l.iterator();
System.out.println(it.getClass().toString());

// Method m = it.getClass().getMethod("hasNext", new Class[] {});
Method m = Iterator.class.getMethod("hasNext", new Class[] {});

m.invoke(it, new Object[] {}); // fail

schreibt. Macht durchaus Sinn, aber es hilft mir in meinem Problem nicht
weiter, weil ich das Interface nicht kenne, nur weiß, dass die Methode
da ist... Wohl zurück zum Designbrett, um das sauber hinzubekommen :)

Beste Grüße,
Martin

Frank Dreyer

unread,
May 12, 2005, 10:03:06 AM5/12/05
to
Martin Carpella schrieb:

> Man nehme folgende Sequenz, die meiner Meinung nach völlig legitim wäre
> (das Beispiel ist etwas an den Haaren herbeigezogen, aber es
> veranschaulicht das Problem):
>
> try {
> List l = new LinkedList();
> Iterator it = l.iterator();
> System.out.println(it.getClass().toString());
> System.out.println();
>
> Method m = it.getClass().getMethod("hasNext", new Class[] {});
> m.invoke(it, new Object[] {}); // fail
> } catch (Exception e) {
> e.printStackTrace();
> }
>
> Dies liefert folgende Ausgabe:
> | class java.util.LinkedList$ListItr
> |
> | java.lang.IllegalAccessException: Class Test can not access a member of
> | class java.util.LinkedList$ListItr with modifiers "public"
> | at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
> | at java.lang.reflect.Method.invoke(Method.java:317)
> | at Test.main(Test.java:14)
>
> LinkedList$ListItr ist private, das ist mir klar, aber es implementiert
> eine öffentliche Methode eines Interfaces, somit sollte meines Erachtens
> auch ein Aufruf mittels Reflection möglich sein, oder nicht?

Ist zwar keine wirklich saubere Lösung, aber wenn du nicht in einer
Sandbox bist (Applet o.ä.), kannst du Method.setAccessible(true) verwenden.

Jochen Theodorou

unread,
May 12, 2005, 10:14:33 AM5/12/05
to
Martin Carpella schrieb:
[...]

> Dies liefert folgende Ausgabe:
> | class java.util.LinkedList$ListItr
> |
> | java.lang.IllegalAccessException: Class Test can not access a member of
> | class java.util.LinkedList$ListItr with modifiers "public"
> | at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
> | at java.lang.reflect.Method.invoke(Method.java:317)
> | at Test.main(Test.java:14)
>
> LinkedList$ListItr ist private, das ist mir klar, aber es implementiert
> eine öffentliche Methode eines Interfaces, somit sollte meines Erachtens
> auch ein Aufruf mittels Reflection möglich sein, oder nicht?

Eine Methode kann keine höhere Sichtbarkeit haben als die Klasse mit der
ich darauf zugreife.

JLS 6.6.1: A member (field or method) of a reference (class, interface,
or array) type or a constructor of a class type is accessible only if
the type is accessible and the member or constructor is declared to
permit access

Der "type" ist im Falle der Klasse die Klasse und im Falle des
Interfaces das Interface. Wenn der "type" also hier die Klasse nicht
zugreifbar ist, dann kann es auch die Methode nicht sein. Dabei zählt
nicht der "runtime type" sondern der "compile time type". Oder besser
gesagt die Art und Weise wie du die auf die Methode zugreifst (über
Klasse oder Interface).

Daher bleibt dir nur übrig den Zugriff "richtig" über das Interface zu
machen, oder "falsch" und dann mit setAccessible

Gruss theo

Martin Carpella

unread,
May 12, 2005, 10:53:30 AM5/12/05
to
Frank Dreyer <FDr...@t-online.de> writes:

> Ist zwar keine wirklich saubere Lösung, aber wenn du nicht in einer
> Sandbox bist (Applet o.ä.), kannst du Method.setAccessible(true)
> verwenden.

Ah, interessant, auf die Idee, dass accessible ein veränderbares
Property sein könnte, bin ich gar nicht gekommen :)

Ist nicht so wichtig, es geht hier ohnehin nicht um eine dauerhafte
Lösung sondern eher darum, dass mir der o.a. Umstand aufgefallen ist und
ich eigentlich der Meinung bin, dass das nicht ganz korrekt ist.

Grüße,
Capi

Martin Carpella

unread,
May 12, 2005, 11:01:40 AM5/12/05
to
Jochen Theodorou <blac...@uni.de> writes:

> Eine Methode kann keine höhere Sichtbarkeit haben als die Klasse mit der
> ich darauf zugreife.

Hmm, macht Sinn.

> JLS 6.6.1: A member (field or method) of a reference (class, interface,
> or array) type or a constructor of a class type is accessible only if
> the type is accessible and the member or constructor is declared to
> permit access

Vielen Dank für das Zitat.

> Daher bleibt dir nur übrig den Zugriff "richtig" über das Interface zu
> machen, oder "falsch" und dann mit setAccessible

Auch kein Problem :) Reflection sollte ja ohnehin nur in wenigen
Ausnahmefällen notwendig sein. Es war nur eine Spielerei :)

Beste Grüße,
Martin

Frank Dreyer

unread,
May 12, 2005, 1:53:58 PM5/12/05
to
Martin Carpella schrieb:

> try {
> List l = new LinkedList();
> Iterator it = l.iterator();
> System.out.println(it.getClass().toString());
> System.out.println();
>
> Method m = it.getClass().getMethod("hasNext", new Class[] {});
> m.invoke(it, new Object[] {}); // fail
> } catch (Exception e) {
> e.printStackTrace();
> }
>
> Dies liefert folgende Ausgabe:
> | class java.util.LinkedList$ListItr
> |
> | java.lang.IllegalAccessException: Class Test can not access a member of
> | class java.util.LinkedList$ListItr with modifiers "public"
> | at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
> | at java.lang.reflect.Method.invoke(Method.java:317)
> | at Test.main(Test.java:14)
>
> LinkedList$ListItr ist private, das ist mir klar, aber es implementiert
> eine öffentliche Methode eines Interfaces, somit sollte meines Erachtens
> auch ein Aufruf mittels Reflection möglich sein, oder nicht?

Da fällt mir gerade ein, daß ich vor kurzem mal auf eine interessante
Variante dieses Problems gestossen bin:

> public class Outer {
> public static Inner getInner() {
> return new Inner();
> }
>
> private static class Inner { }
> }

> public class Accesser {
> public static void main(String[] args) {
> String str = Outer.getInner().toString();
> System.out.println(str);
> }
> }

Bringt beim Kompilieren folgenden Fehler:
> Accesser.java:3: toString() in java.lang.Object is not defined in a
> public class or interface; cannot be accessed from outside package

...offensichtlich ist java.lang.Object also keine public class ;)

Das ist übrigens unabhängig davon, ob toString() in Inner überschrieben
wird (war bei mir der Fall) oder nicht (wie im Beispiel oben). Durch
explizites casten auf Object ließ sich das ganze dann Kompilieren und
warf auch zur Laufzeit keine Exception:

> String str = ((Object)Outer.getInner()).toString();

Martin Carpella

unread,
May 12, 2005, 3:24:08 PM5/12/05
to
Frank Dreyer <FDr...@t-online.de> writes:

> > public class Outer {
> > public static Inner getInner() {
> > return new Inner();
> > }
> >
> > private static class Inner { }
> > }
>
> > public class Accesser {
> > public static void main(String[] args) {
> > String str = Outer.getInner().toString();
> > System.out.println(str);
> > }
> > }

> Das ist übrigens unabhängig davon, ob toString() in Inner überschrieben


> wird (war bei mir der Fall) oder nicht (wie im Beispiel oben). Durch
> explizites casten auf Object ließ sich das ganze dann Kompilieren und
> warf auch zur Laufzeit keine Exception:

Meines Erachtens liegt es vor allem daran, dass du hier als Rückgabewert
etwas zurückgibst, was "draußen" ohnehin niemand auflösen kann. Mit
einem

public static Object getInner()

könnte ich mich ja noch anfreunden, aber oben geschriebener Code kann
IMO gar nicht funktionieren.

Grüße,
Capi

Ingo R. Homann

unread,
May 13, 2005, 2:49:54 AM5/13/05
to
Hi,

Martin Carpella wrote:
> Frank Dreyer <FDr...@t-online.de> writes:
>
>> > public class Outer {
>> > public static Inner getInner() {
>> > return new Inner();
>> > }
>> >
>> > private static class Inner { }
>> > }
>>
>> > public class Accesser {
>> > public static void main(String[] args) {
>> > String str = Outer.getInner().toString();
>> > System.out.println(str);
>> > }
>> > }
>

> Meines Erachtens liegt es vor allem daran, dass du hier als Rückgabewert
> etwas zurückgibst, was "draußen" ohnehin niemand auflösen kann.

In der Tat! Ich bekomme (IMHO sinnvollerweise) auch einen "The type
Outer.Inner is not visible"-Fehler. (Eclipse 3.1M5)

> Mit
> einem
>
> public static Object getInner()
>
> könnte ich mich ja noch anfreunden,

Klar (nur ist das natürlich nichts "besonderes" mehr...)

> aber oben geschriebener Code kann
> IMO gar nicht funktionieren.

ACK,

Ciao,
Ingo

Frank Dreyer

unread,
May 14, 2005, 6:43:16 PM5/14/05
to
Ingo R. Homann schrieb:

> Martin Carpella wrote:
>> aber oben geschriebener Code kann
>> IMO gar nicht funktionieren.
>
> ACK,

Da muß ich euch leider enttäuschen, der Code funktioniert nämlich. Man
kann zwar wie erwartet nicht auf spezielle Methoden zugreifen, aber an
die "string representation" (die in dem Fall auch genau das ist, was ich
haben möchte) komme ich über den Umweg des auf-Object-casten ran. Und
eine getInnerToString() Methode zu schreiben scheint mir auch unsinnig.

Timo Stamm

unread,
May 15, 2005, 5:18:44 PM5/15/05
to
Frank Dreyer wrote:
> Ingo R. Homann schrieb:
>
>> Martin Carpella wrote:
>>
>>> aber oben geschriebener Code kann
>>> IMO gar nicht funktionieren.
>>
>>
>> ACK,
>
>
> Da muß ich euch leider enttäuschen, der Code funktioniert nämlich.

Jain.

Da das Inner-Objekt sich von der Klasse Object ableitet, kannst du es
auch auf Object casten und wie ein Object ansprechen. Du umgehst damit
aber die Typsicherheit.


Besser wäre folgendes:

public class Outer {
public static Object getInner() {
return new Inner();
}
private static class Inner {...}
}

Outer.getInner().toString(); // compiles and works


Du kannst an dieser Stelle natürlich auch interfaces benutzen.


Gruss,
Timo

Frank Dreyer

unread,
May 15, 2005, 6:15:39 PM5/15/05
to
Timo Stamm schrieb:

> Da das Inner-Objekt sich von der Klasse Object ableitet, kannst du es
> auch auf Object casten und wie ein Object ansprechen. Du umgehst damit
> aber die Typsicherheit.
>
>
> Besser wäre folgendes:
>
> public class Outer {
> public static Object getInner() {
> return new Inner();
> }
> private static class Inner {...}
> }
>
> Outer.getInner().toString(); // compiles and works

Gerade dadurch umgehe ich doch die Typsicherheit. Wenn ich jetzt aus
einer anderen Methode in Outer getInner() aufrufe, muss ich das Ergebnis
erst wieder auf Inner casten.

Timo Stamm

unread,
May 15, 2005, 9:05:42 PM5/15/05
to


In Outer kannst du doch new Inner() benutzen.


Timo

Bernd Eckenfels

unread,
May 15, 2005, 9:26:14 PM5/15/05
to
Timo Stamm <t.s...@macnews.de> wrote:
>>> Outer.getInner().toString(); // compiles and works
> In Outer kannst du doch new Inner() benutzen.

Üblicherweise gibt man die inner classes ja auch nur als spezielle
Implemnetierung eines (public) Interfaces zurueck, dann braucht man nicht
das untypisierte Object.

Oder halt die statischen Inner Classes wie z.b. bei Map.Entry.

Gruss
Bernd

Martin Carpella

unread,
May 16, 2005, 5:10:31 AM5/16/05
to
Bernd Eckenfels <ec...@lina.inka.de> writes:

> Üblicherweise gibt man die inner classes ja auch nur als spezielle
> Implemnetierung eines (public) Interfaces zurueck, dann braucht man nicht
> das untypisierte Object.

So seh ich das auch.

> Oder halt die statischen Inner Classes wie z.b. bei Map.Entry.

Naja, diese Klasse ist ohnehin öffentlich, womit sich das oben
diskutierte Problem nicht mehr stellt, da auch ein "externer" Verwender
auf diesen Typ Zugriff hat.

Grüße,
Martin

Timo Stamm

unread,
May 16, 2005, 6:43:25 AM5/16/05
to
Bernd Eckenfels wrote:
> Timo Stamm <t.s...@macnews.de> wrote:
>
>>>>Outer.getInner().toString(); // compiles and works
>>
>>In Outer kannst du doch new Inner() benutzen.
>
>
> Üblicherweise gibt man die inner classes ja auch nur als spezielle
> Implemnetierung eines (public) Interfaces zurueck, dann braucht man nicht
> das untypisierte Object.


Natürlich. In _diesem speziellen_ Fall, wo nur toString gefragt ist,
würde ich aber jederzeit einfach ein simples Object zurückgeben.


Gruss,
Timo

Ingo R. Homann

unread,
May 17, 2005, 8:04:54 AM5/17/05
to
Hi Frank,

Frank Dreyer wrote:
>>> aber oben geschriebener Code kann
>>> IMO gar nicht funktionieren.
>>
>> ACK,
>

> Da muß ich euch leider enttäuschen, der Code funktioniert nämlich...

Wie cih schon sagte: bei mir funktioniert er nicht nur nicht, er
compiliert nicht einmal.

Aber vielleicht ist das auch nur ein Bug im Eclipse-Compiler.

IMHO wäre das aber ein sehr nützlicher Bug! ;-)

Ciao,
Ingo

0 new messages