webentwicklung-frage-antwort-db.com.de

Verwenden Sie Reflection in Java, um eine neue Instanz zu erstellen, deren Referenzvariablentyp auf den neuen Instanzklassennamen gesetzt ist.

Alle Beispiele, die ich mir für Reflections anschaue, zeigen das Erstellen einer neuen Instanz einer unbekannten Implementierung und das Umwandeln dieser Implementierung in ihre Schnittstelle. Das Problem dabei ist, dass Sie jetzt keine neuen Methoden (nur Überschreibungen) für die implementierende Klasse aufrufen können, da Ihre Objektreferenzvariable den Schnittstellentyp hat. Folgendes habe ich:

Class c = null;
try {
    c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
InterfaceType interfaceType = null;
try {
    interfaceType = (InterfaceType)c.newInstance();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

Wenn ich nur einen Verweis auf "com.path.to.ImplementationType" habe und nicht weiß, was dieser Typ sein könnte (er stammt aus einer Konfigurationsdatei), wie kann ich dann den Klassennamen verwenden, um ihn in Cast umzuwandeln Implementierungstyp? Ist das überhaupt möglich?

19
user4903

Diese Zeile scheint den Kern Ihres Problems zusammenzufassen:

Das Problem dabei ist, dass Sie jetzt keine neuen Methoden (nur Überschreibungen) für die implementierende Klasse aufrufen können, da Ihre Objektreferenzvariable den Schnittstellentyp hat.

Sie sind in Ihrer aktuellen Implementierung ziemlich festgefahren, da Sie nicht nur einen Cast versuchen müssen, sondern auch die Definition der Methoden, die Sie für diese Unterklasse aufrufen möchten. Ich sehe zwei Möglichkeiten:

1. Wie bereits erwähnt, können Sie die String-Darstellung des Klassennamens nicht verwenden, um Ihre reflektierte Instanz in einen bekannten Typ umzuwandeln. Sie können jedoch einen Stringequals()-Test verwenden, um festzustellen, ob Ihre Klasse den gewünschten Typ hat, und dann eine hartcodierte Umwandlung durchführen:

try {
   String className = "com.path.to.ImplementationType";// really passed in from config
   Class c = Class.forName(className);
   InterfaceType interfaceType = (InterfaceType)c.newInstance();
   if (className.equals("com.path.to.ImplementationType") {
      ((ImplementationType)interfaceType).doSomethingOnlyICanDo();
   } 
} catch (Exception e) {
   e.printStackTrace();
}

Das sieht ziemlich hässlich aus und ruiniert den von Nizza konfigurierten Prozess. Ich schlage nicht vor, dass Sie das tun, es ist nur ein Beispiel. 

2. Eine weitere Möglichkeit besteht darin, Ihre Reflektion durch die Erstellung von Class/Object zu erweitern, um Method-Reflektion einzuschließen. Wenn Sie die Class aus einem übergebenen String aus einer Konfigurationsdatei erstellen können, können Sie auch einen Methodennamen aus dieser Konfigurationsdatei übergeben und über Reflektion eine Instanz der Method selbst von Ihrem Class-Objekt abrufen. Sie können dann invoke ( http://Java.Sun.com/javase/6/docs/api/Java/lang/reflect/Method.html#invoke(Java.lang.Object , Java.lang) aufrufen. Object ...)) auf der Method, die in der von Ihnen erstellten Instanz Ihrer Klasse übergeben wird. Ich denke, das wird dir helfen, das zu bekommen, was du willst.

Hier ist ein Code, der als Beispiel dienen soll. Beachten Sie, dass ich mir die Freiheit genommen habe, die Parameter für die Methoden hart zu codieren. Sie könnten sie auch in einer Konfiguration angeben und müssten ihre Klassennamen reflektieren, um ihre Class-Objekte und -Instanzen zu definieren.

public class Foo {

    public void printAMessage() {
    System.out.println(toString()+":a message");
    }
    public void printAnotherMessage(String theString) {
        System.out.println(toString()+":another message:" + theString);
    }

    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("Foo");
            Method method1 = c.getDeclaredMethod("printAMessage", new Class[]{});
            Method method2 = c.getDeclaredMethod("printAnotherMessage", new Class[]{String.class});
            Object o = c.newInstance();
            System.out.println("this is my instance:" + o.toString());
            method1.invoke(o);
            method2.invoke(o, "this is my message, from a config file, of course");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException nsme){
            nsme.printStackTrace();
        } catch (IllegalAccessException iae) {
            iae.printStackTrace();
        } catch (InstantiationException ie) {
            ie.printStackTrace();
        } catch (InvocationTargetException ite) {
            ite.printStackTrace();
        }
    }
}

und meine Ausgabe:

this is my instance:[email protected]
[email protected]:a message
[email protected]:another message:this is my message, from a config file, of course
29
akf
//====Single Class Reference used to retrieve object for fields and initial values. Performance enhancing only====          
Class<?>    reference           =   vector.get(0).getClass();
Object      obj                 =   reference.newInstance();
Field[]     objFields           =   obj.getClass().getFields(); 
3
GoToJack

Als Nachtrag zur Antwort von akf könnten Sie anstelle von String equals () -Aufrufen Instanzen von Checks verwenden:

String cname="com.some.vendor.Impl";
try {
  Class c=this.getClass().getClassLoader().loadClass(cname);
  Object o= c.newInstance();
  if(o instanceof Spam) {
    Spam spam=(Spam) o;
    process(spam);
  }
  else if(o instanceof Ham) {
    Ham ham = (Ham) o;
    process(ham);
  }
  /* etcetera */
}
catch(SecurityException se) {
  System.err.printf("Someone trying to game the system?%nOr a rename is in order because this JVM doesn't feel comfortable with: “%s”", cname);
  se.printStackTrace();
}
catch(LinkageError le) {
  System.err.printf("Seems like a bad class to this JVM: “%s”.", cname);
  le.printStackTrace();
}
catch(RuntimeException re) { 
  // runtime exceptions I might have forgotten. Classloaders are wont to produce those.
  re.printStackTrace();
}
catch(Exception e) { 
  e.printStackTrace();
}

Beachten Sie die liberale Hardcoding einiger Werte. Trotzdem sind die Hauptpunkte:

  1. Verwenden Sie instanzof statt equals (). Wenn überhaupt, wird es beim Refactoring besser zusammenarbeiten.
  2. Stellen Sie sicher, dass Sie auch diese Laufzeitfehler und Sicherheitsfehler abfangen.
2
user268396

Sie möchten in der Lage sein, eine Klasse zu übergeben und eine typsichere Instanz dieser Klasse zu erhalten? Versuche Folgendes:

public static void main(String [] args) throws Exception {
    String s = instanceOf(String.class);
}

public static <T> T instanceOf (Class<T> clazz) throws Exception {
    return clazz.newInstance();
}
1
Kevin

Ich bin mir nicht absolut sicher, ob ich deine Frage richtig verstanden habe, aber es scheint, dass du so etwas willst:

    Class c = null;
    try {
        c = Class.forName("com.path.to.ImplementationType");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    T interfaceType = null;
    try {
        interfaceType = (T) c.newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

Wobei T auf Methodenebene oder auf Klassenebene definiert werden kann, d. H. <T extends InterfaceType>

1
nanda

Wenn Sie die Class von ImplementationType kennen, können Sie eine Instanz davon erstellen. Was Sie also zu tun versuchen, ist nicht möglich.

0
fastcodejava