2016-03-30 10 views
3

Ich habe eine einfache interpretierte Sprache mit dynamischer Typisierung in Java implementiert. Leider stieß ich auf folgendes Problem. Wenn Sie den folgenden Code zu testen:Interpretierte Sprache in Java und Aufrufe von Java-Methoden

def main() { 
    def ks = Map[[1, 2]].keySet(); 
    return ks.size(); 
} 

stieß ich auf die folgende Ausnahme:

java.lang.IllegalAccessException: class is not public: java.util.HashMap$KeySet.size()int/invokeSpecial 

Natürlich durch die Tatsache Dies gilt und verursacht, dass HashMap $ KeySet Klasse „Paket“ Sichtbarkeit. Das heißt, wenn ich die Methode "size()" aufrufe, rufe ich die Methode aus der Klasse auf, die für meinen Code nicht sichtbar ist. Java vermeidet dieses Problem ganz einfach - die Methode keySet() gibt den Wert des Typs Set zurück, also wird die verwendete Methode size() in der öffentlichen und der abstrakten Klasse "Set" deklariert.

Meine Frage ist: Hat jemand eine Idee, wie dies in generischer Weise behandelt werden soll? Unter „Allgemein“ Fall meine ich nicht nur diese einfachen Fall, wo ich durch ganze Vererbungskette gehen und „erste Erklärung“ dieser Methode finden, sondern auch pathologische Fälle wie folgt aus:

interface I1 { 
    public void foo(); 
} 
interface I2 { 
    public void foo(); 
} 
interface I3 { 
    public void foo(); 
} 
class C implements I1, I2, I3 { 
    public void foo() { .... } 
} 

Mein aktueller Eindruck ist, dass ich diese pathologischen Fälle ignorieren und jede passende Methode wählen könnte, wenn ein solches Objekt existiert, dann ist die Erstellung erfolgreich, also ist die Kompilierung erfolgreich, also haben alle diese Methoden identische Signaturen und da in Java gibt es keine Möglichkeit zu spezifizieren Je nachdem wie das Objekt betrachtet wird (wie I1, I2 oder I3), wird das Ergebnis immer dasselbe sein.

Jede Hilfe wird geschätzt.

+0

Sie sollten wahrscheinlich versuchen, die Auflösungsregeln der Java-Methode zu emulieren, außer dass Sie es zur Laufzeit anstatt zur Kompilierzeit tun würden. Im Grunde genommen müssten Sie die erste Methode in der Vererbungsstruktur (die am nächsten zur Wurzel) betrachten, und wenn es mehrere gibt, wählen Sie diejenige, die von einer Oberklasse kommt. Wenn sie alle von Schnittstellen kommen, können Sie beliebig wählen. – biziclop

+0

Eine verwandte Frage ist jedoch, wie all dies mit Standardmethoden funktionieren würde. – biziclop

+0

Vielleicht ist es nicht klar aus meinem Beitrag, aber das ist genau das, was ich gerade implementiere, ich bin mir einfach nicht sicher, ob das die Lösung ist. Vielleicht gibt es einen besseren Weg, weshalb ich gefragt habe. Natürlich könnte ich es einfach so lassen, denn dynamisches Tippen ist nur eine Phase für dieses Projekt, da ich statische Typisierung mit Typendifferenzierung anstrebe, aber es mit einem solchen klaffenden Loch zu verlassen, kann ich nicht mit gutem Gewissen tun. –

Antwort

0

Ok, also hier ist meine Lösung. Es ist nicht sehr schön, aber hey, was auch immer funktioniert:

public static Method findMethod(Class<?> cls, String name, Class<?>[] fa) { 
    System.out.println("Checking class " + cls + " for method " + name); 
    // since it is called recursively, we want to stop some day, and when we are 
    // passed null (so most getSuperclass was called on Object.class or something similar) 
    if (cls == null) { 
     return null; 
    } 
    Method m = null; 
    if ((m = findMethod(cls.getSuperclass(), name, fa)) != null) { 
     return m; 
    } 
    // ok, if we're here, then m is null. so check if cls is public. it must be public, because 
    // otherwise we won't be able to call it - we are definitely in different package. if class 
    // isn't public, then check interfaces. 
    if (!Modifier.isPublic(cls.getModifiers())) { 
     System.out.println("Class is not public, and superclasses do not contain method " + name); 
     System.out.println("Checking all interfaces"); 
     for (Class<?> iface: cls.getInterfaces()) { 
      if ((m = findMethod(iface, name, fa)) != null) { 
       return m; 
      } 
     } 
    } 
    return findMethodInClass(cls, name, fa); 
} 
private static Method findMethodInClass(Class<?> cls, String name, Class<?>[] fa) { 
    Method m = null; 
    // scan all methods and move plausible candidates to the start of an array 
    Method[] mm = cls.getMethods(); 
    int n = 0; 
    for (int i = 0 ; i < mm.length ; ++i) { 
     if (checkMethod(mm[i], name, fa)) { 
      mm[n++] = mm[i]; 
     } 
    } 
    if (n > 1) { 
     System.out.println("Caveat: we have to perform more specific test. n == " + n); 
     System.out.println("class: " + cls + "\nname: " + name); 
     for (int i = 0 ; i < n ; ++i) { 
      System.out.println(mm[i]); 
     } 
    } 
    if (n > 0) { 
     m = mm[0]; 
    } 
    return m; 
} 

Methode checkMethod() aufgerufen in findMethodInClass einfach überprüft, ob Name korrekt ist und wenn Argumente, mit denen Verfahren mehr oder weniger Spiel formaler Argumentliste aufgerufen werden. Die Implementierung bleibt dem Leser als einfache Übung überlassen. Irgendwelche Kommentare?