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
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
Ist zwar keine wirklich saubere Lösung, aber wenn du nicht in einer
Sandbox bist (Applet o.ä.), kannst du Method.setAccessible(true) verwenden.
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
> 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
> 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
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();
> > 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
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
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.
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
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.
In Outer kannst du doch new Inner() benutzen.
Timo
Ü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
> Ü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
Natürlich. In _diesem speziellen_ Fall, wo nur toString gefragt ist,
würde ich aber jederzeit einfach ein simples Object zurückgeben.
Gruss,
Timo
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