Ich habe mehrere Tage auf diesem niggly Problem jetzt verbracht und kann einfach keine Lösung finden.Wie eine Callback-Methode zwischen verschiedenen Klassen zur Laufzeit übergeben
Hier ist, was ich versuche zu tun: Lesen Sie eine Zeileneingabe und ändern Sie die Interpretation der Daten, basierend auf bestimmten Schlüsselwörter. Diese Schlüsselwörter können von Clientobjekten dynamisch registriert werden. Sie registrieren ein Stichwort und "Rückruf" Funktion (für ein besseres Wort). Diese "Callback" -Funktion wird aufgerufen, wenn das Schlüsselwort angetroffen wird, um mit der Eingabezeichenfolge umzugehen und ein standardisiertes Objekt zurückzugeben.
Dafür habe ich eine HashMap mit dem Schlüsselwort als Schlüssel und einer Methodenreferenz als Wert erstellt. Während der Initialisierung des Programms registrieren sich alle Objekte, die den Dienst nutzen wollen.
Nach der Initialisierung wird die Eingabe gelesen und auf Schlüsselwörter gegen meine Hashmaps überprüft. Wenn der Typ dort vorhanden ist, wird die Wertzeichenfolge zur Verarbeitung übergeben und eine standardisierte Objektdarstellung aus den Zeichenfolgendaten zurückgegeben.
Ich schaffte es, eine Methodenreferenz zu entlocken, indem ich ihren Namen nachschaute und die Methode abrief. Der folgende Code kann die Methode übergeben, speichern und abrufen, aber beim Versuch, sie aufzurufen, wird eine IllegalArgument-Ausnahme ausgelöst.
Während meiner Recherchen fand ich mehrere Berichte von ähnlichen Problemen, unter anderem auch auf dieser Website, aber die meisten haben sich mit meinem genauen Problem nicht ganz beschäftigt.
In one case gab es den Vorschlag, dass der Methodenaufruf nicht funktionieren kann, weil keine Instanz der Methode instanziiert wurde. Es könnte behoben werden, indem die Methode newInstance aufgerufen wird, um die erforderliche Instanziierung der Methode bereitzustellen. Es hat nicht für mich funktioniert, weil ich aus dieser Methodenreferenz keine neue Instanz bekommen kann.
Verschiedene Ansätze von Interfaces über Java8 Methodenreferenzen und Listener Patterns wurden getestet. Sie alle erlaubten die Weitergabe von Methodenreferenzen, aber ich kam immer zu einem totalen Stillstand, weil ich in XSettings eine Instanz des Clients deklarieren müsste. Und das ist genau was ich nicht tun kann, weil ich nicht was zu XSettings die ganze Zeit ändern kann.
Unten ist mein Code:
Locus.java
---------------------------------
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.function.Function;
...
public class Locus implements StringConverter{
private long x;
private long y;
public Locus(int x, int y) {
super();
this.x = x;
this.y = y;
}
public Locus(long x, long y) {
super();
this.x = x;
this.y = y;
}
public Locus(String s) {
super();
Locus lc = (Locus) convert(s);
this.x = lc.x;
this.y = lc.y;
}
private long getLongFromString(String s1){
long res=0L;
if(s1.matches("[0-9]*$")) // Integer detected
res = (long)(Integer.parseInt(s1));
// Long detected
else if(s1.matches("[0-9]*[lL]$")){
s1=s1.split("[lL]")[0];
if(!s1.isempty()) // don't parse empty strings
res = Long.parseLong(s1);
} // Double detected
else if(s1.matches("[0-9][0-9]*\\.[0-9]*$"))
res = (long) Double.parseDouble(s1);
return res;
}
public Object convert (String s) {
long x=0, y=0;
if(s.contains(",")){
String[] parts = s.split("\\,");
parts[0] = parts[0].trim();
parts[1] = parts[1].trim();
x = getLongFromString(parts[0]);
y = getLongFromString(parts[1]);
}
return createLocus(x, y);
}
public static void init() {
Method method = null;
try {
method = Locus.class.getDeclaredMethod("convert", String.class);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(method != null)
XSettings.setTypeHandler("Locus", method);
}
...
}
---------------------------------
XSettings.java
---------------------------------
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Iterator;
public class XSettings {
// *************************************************
// Singleton Pattern
private XSettings(){}
private static class InstanceHolder {
public static final XSettings instance = new XSettings();
}
public XSettings getInstance() {
return InstanceHolder.instance;
}
// *************************************************
static final String cfgFileName="C:\\workspace\\Router\\route.cfg";
// Map with the payload data to be queried
public static HashMap<String, Object> settings = new HashMap<String, Object>();
// Map with Key/method-references
static HashMap<String, Method> typeHandler = new HashMap<String, Method>();
// caller method for the object converters
public static Object convertToType(String type, String value){
Object result = null;
Method method = typeHandler.get(type);
if(method!=null){ // method exists
try {
result = method.invoke(method, value); // <------ causes IllegalArgumentException
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
result=value; // No? Default pass back the input string
}
return result;
}
// store the method refernce in map
public static void setTypeHandler(String key, final Method method) {
typeHandler.put(key, method);
}
// Store a payload key value pair
public static void set(String key, Object value) {
settings.put(key, value);
}
// Retrieve a value by key
public static Object get(String key) {
return settings.get(key);
}
public static void init(){ // read input
String line;
try (
InputStream fis = new FileInputStream(cfgFileName);
InputStreamReader isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
BufferedReader br = new BufferedReader(isr);
)
{
while ((line = br.readLine()) != null) { Parse the lines
String key="";
String type="";
String val="";
line=line.trim(); // trim back empty space
System.out.println(line);
if(line.isEmpty()) // skip empty lines
continue;
if(line.charAt(0)=='#') // skip comment lines
continue;
String[] subs = line.split("=",2); // split at assignment
continue;
subs[0]=subs[0].trim(); // key Part
subs[1]=subs[1].trim(); // value Part
String[] subValues = subs[0].split("::", 2); //Split at type, key section
if(subValues.length==1){ // empty value
key = subValues[0];
} else if(subValues.length>1){ // normal key, value case
type = subValues[0];
key = subValues[1];
} else { // no assignment operator, empty value
key = subs[0];
}
subValues = subs[1].split("\""); // remove double quotes from value part
if(subValues.length==0){ // no quotes and no value
val = "";
} else if(subValues.length==1){ // no quotes
val = subValues [0];
} else if(subValues.length==2){ // quotes present, stripped
val = subValues [1];
} else // more than two quotes treat as a string only
val = subs[1];
if(!typeExists(type)){ // check if type is in methodHandler
key = subs[0]; // No: set key back to full L-Value
type = "";
}
settings.put(key, convertToType(type, val)); // Call converter
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static boolean typeExists(String type) {
boolean res=false;
if(type!=null)
if(!(type.isEmpty()))
Object m = typeHandler.get(type);
res=(m!=null);
return res;
}
...
}
---------------------------------
Die Servicefunktion ist XSettings und der Kunde ist Locus. XSettings liest eine Eingabedatei Zeile für Zeile und scannt sie nach Schlüsselwertpaaren. Der Schlüssel darf den Namen der Zielklasse enthalten. Die Zeile wird also in Typ, Schlüssel und Wert aufgeteilt.
Die Interpretation des Werts unterscheidet sich je nach Typ. Standardmäßig sind nur Strings entfernt gespeichert. Wenn andere Objekte ein Schlüsselwort registrieren und die Konvertierungsmethode liefern, werden sie abgeholt und in das gewünschte Objekt konvertiert.Wenn also der Schlüssel abgefragt wird, ist das Ergebnis sofort das richtige Objekt.
Ich habe es geschafft, die Methodenreferenz den gesamten Weg zum Aufrufpunkt zu bekommen. Das Objekt sieht in Locus identisch aus, wenn es an die Methode setTypeHandler von XSettings übergeben wird, sowie nach dem Aufruf aus der HashMap und vor dem Aufruf von . Dann IllegalArgumentException ...
Dies könnte eine sehr "C" -artige Art, dies zu tun, aber ich dachte immer, es war relativ effizient und elegant. Wenn es jedoch einen viel besseren Java-ähnlichen Weg gibt, wäre ich begeistert davon zu hören.
Vielen Dank für Ihr Interesse. Ich freue mich wirklich darauf, von Ihnen zu hören.
Danke Martin für den Tipp. Allerdings habe ich im Moment keine Ahnung, wie ich das machen soll. Wird dies untersuchen und zurückmelden. – Helmut
hier ist das, was ich die invoke Linie ... geändert \t \t \t \t result = method.getClass() newInstance() aufrufen (Methode, Wert)..; ... nicht mehr IllegalArgumentException, jetzt ist es InstantiationException. Ich denke, das könnte sein, weil es keinen leeren Konstruktor in dieser Klasse gibt. Müssen das überprüfen. – Helmut
Die Lösung ist, dass die Methode in der Tat uninstantiated ist. GetClass gibt jedoch nicht die Locus-Klasse, sondern die Reflection-Klasse zurück. – Helmut