Ich möchte dynamisches Casting für eine Java-Variable durchführen, der Casting-Typ wird in einer anderen Variablen gespeichert.
Dies ist das reguläre Casting:
String a = (String) 5;
Das ist was ich will:
String theType = 'String';
String a = (theType) 5;
Ist das möglich und wenn ja wie? Vielen Dank!
Aktualisieren
Ich versuche, eine Klasse mit einer zu füllen HashMap
, die ich erhalten habe.
Dies ist der Konstruktor:
public ConnectParams(HashMap<String,Object> obj) {
for (Map.Entry<String, Object> entry : obj.entrySet()) {
try {
Field f = this.getClass().getField(entry.getKey());
f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
} catch (NoSuchFieldException ex) {
log.error("did not find field '" + entry.getKey() + '"');
} catch (IllegalAccessException ex) {
log.error(ex.getMessage());
}
}
}
Das Problem hier ist, dass einige der Variablen der Klasse vom Typ sind Double
, und wenn die Nummer 3 empfangen wird, sieht es so aus Integer
und ich habe ein Typproblem.
Antworten:
Das macht keinen Sinn
String a = (theType) 5;
Der Typ von
a
ist statisch gebunden,String
daher macht es keinen Sinn, eine dynamische Umwandlung in diesen statischen Typ vorzunehmen.PS: Die erste Zeile Ihres Beispiels könnte so geschrieben werden
Class<String> stringClass = String.class;
, dass Sie sie dennoch nichtstringClass
zum Umwandeln von Variablen verwenden können.quelle
Ja, es ist möglich, Reflection zu verwenden
Object something = "something"; String theType = "java.lang.String"; Class<?> theClass = Class.forName(theType); Object obj = theClass.cast(something);
Das macht aber wenig Sinn, da das resultierende Objekt in einer Variablen vom
Object
Typ gespeichert werden muss . Wenn die Variable einer bestimmten Klasse angehören soll, können Sie sie einfach in diese Klasse umwandeln.Wenn Sie eine bestimmte Klasse erhalten möchten,
Number
zum Beispiel:Object something = new Integer(123); String theType = "java.lang.Number"; Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class); Number obj = theClass.cast(something);
aber es macht immer noch keinen Sinn, es zu tun, man könnte es einfach besetzen
Number
.Das Gießen eines Objekts ändert nichts; Es ist genau so, wie der Compiler es behandelt.
Der einzige Grund, so etwas zu tun, besteht darin, zu überprüfen, ob das Objekt eine Instanz der angegebenen Klasse oder einer Unterklasse davon ist. Dies sollte jedoch besser mit
instanceof
oder erfolgenClass.isInstance()
.Aktualisieren
Laut Ihrem letzten Update besteht das eigentliche Problem darin, dass Sie ein
Integer
in Ihrem habenHashMap
, das einem zugewiesen werden sollteDouble
. In diesem Fall können Sie den Feldtyp überprüfen und diexxxValue()
Methoden von verwendenNumber
... Field f = this.getClass().getField(entry.getKey()); Object value = entry.getValue(); if (Integer.class.isAssignableFrom(f.getType())) { value = Integer.valueOf(((Number) entry.getValue()).intValue()); } else if (Double.class.isAssignableFrom(f.getType())) { value = Double.valueOf(((Number) entry.getValue()).doubleValue()); } // other cases as needed (Long, Float, ...) f.set(this, value); ...
(Ich bin mir nicht sicher, ob mir die Idee gefällt, den falschen Typ in der zu haben.
Map
)quelle
Sie müssen eine Art dafür schreiben
ObjectConverter
. Dies ist möglich, wenn Sie sowohl das Objekt haben, das Sie konvertieren möchten, als auch die Zielklasse kennen, in die Sie konvertieren möchten. In diesem speziellen Fall können Sie die Zielklasse von erhaltenField#getDeclaringClass()
.Ein Beispiel dafür finden Sie hier
ObjectConverter
. Es sollte Ihnen die Grundidee geben. Wenn Sie mehr Konvertierungsmöglichkeiten wünschen, fügen Sie einfach weitere Methoden mit dem gewünschten Argument und Rückgabetyp hinzu.quelle
Sie können dies mit der
Class.cast()
Methode tun , die den angegebenen Parameter dynamisch in den Typ der vorhandenen Klasseninstanz umwandelt. Um die Klasseninstanz eines bestimmten Felds abzurufen, verwenden Sie diegetType()
Methode für das betreffende Feld. Ich habe unten ein Beispiel gegeben, aber beachte, dass es jede Fehlerbehandlung auslässt und nicht unverändert verwendet werden sollte.public class Test { public String var1; public Integer var2; } public class Main { public static void main(String[] args) throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("var1", "test"); map.put("var2", 1); Test t = new Test(); for (Map.Entry<String, Object> entry : map.entrySet()) { Field f = Test.class.getField(entry.getKey()); f.set(t, f.getType().cast(entry.getValue())); } System.out.println(t.var1); System.out.println(t.var2); } }
quelle
Sie können eine einfache castMethod wie die folgende schreiben.
private <T> T castObject(Class<T> clazz, Object object) { return (T) object; }
In Ihrer Methode sollten Sie es wie verwenden
public ConnectParams(HashMap<String,Object> object) { for (Map.Entry<String, Object> entry : object.entrySet()) { try { Field f = this.getClass().getField(entry.getKey()); f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */ } catch (NoSuchFieldException ex) { log.error("did not find field '" + entry.getKey() + '"'); } catch (IllegalAccessException ex) { log.error(ex.getMessage()); } } }
quelle
Es funktioniert und es gibt sogar ein allgemeines Muster für Ihren Ansatz: das Adaptermuster . Aber (1) es funktioniert natürlich nicht zum Umwandeln von Java-Grundelementen in Objekte und (2) die Klasse muss anpassbar sein (normalerweise durch Implementieren einer benutzerdefinierten Schnittstelle).
Mit diesem Muster könnten Sie so etwas tun:
Wolf bigBadWolf = new Wolf(); Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);
und die getAdapter-Methode in der Wolf-Klasse:
public Object getAdapter(Class clazz) { if (clazz.equals(Sheep.class)) { // return a Sheep implementation return getWolfDressedAsSheep(this); } if (clazz.equals(String.class)) { // return a String return this.getName(); } return null; // not adaptable }
Für Sie besondere Idee - das ist unmöglich. Sie können keinen String-Wert für das Casting verwenden.
quelle
Ihr Problem ist nicht das Fehlen von "dynamischem Casting". Casting
Integer
zuDouble
ist überhaupt nicht möglich. Sie möchten Java anscheinend ein Objekt eines Typs, ein Feld eines möglicherweise inkompatiblen Typs, geben und es irgendwie automatisch herausfinden lassen, wie zwischen den Typen konvertiert wird.Diese Art von Dingen ist für eine stark typisierte Sprache wie Java und IMO aus sehr guten Gründen ein Gräuel.
Was versuchst du eigentlich zu tun? All diese Verwendung von Reflexion sieht ziemlich faul aus.
quelle
Tu das nicht. Verwenden Sie stattdessen einfach einen ordnungsgemäß parametrisierten Konstruktor. Der Satz und die Typen der Verbindungsparameter sind ohnehin festgelegt, so dass es keinen Sinn macht, dies alles dynamisch zu tun.
quelle
Die meisten Skriptsprachen (wie Perl) und nicht statischen Sprachen zur Kompilierungszeit (wie Pick) unterstützen automatische dynamische Konvertierungen von Zeichenfolgen in (relativ willkürliche) Objekte zur Laufzeit. Dies kann auch in Java erreicht werden, ohne die Typensicherheit zu verlieren, und die guten statisch typisierten Sprachen bieten OHNE die bösen Nebenwirkungen einiger anderer Sprachen, die mit dynamischem Casting böse Dinge tun. Ein Perl-Beispiel, das fragwürdige Berechnungen anstellt:
print ++($foo = '99'); # prints '100' print ++($foo = 'a0'); # prints 'a1'
In Java wird dies besser erreicht (IMHO), indem eine Methode verwendet wird, die ich "Cross-Casting" nenne. Beim Cross-Casting wird die Reflexion in einem verzögert geladenen Cache von Konstruktoren und Methoden verwendet, die mithilfe der folgenden statischen Methode dynamisch erkannt werden:
Object fromString (String value, Class targetClass)
Leider tun dies keine integrierten Java-Methoden wie Class.cast () für String to BigDecimal oder String to Integer oder andere Konvertierungen, bei denen keine unterstützende Klassenhierarchie vorhanden ist. Für meinen Teil geht es darum, einen vollständig dynamischen Weg zu finden, um dies zu erreichen - für den ich nicht denke, dass die vorherige Referenz der richtige Ansatz ist - und jede Konvertierung codieren zu müssen. Einfach ausgedrückt, die Implementierung ist nur aus dem String zu werfen, wenn es legal / möglich ist.
Die Lösung ist also eine einfache Reflexion, bei der nach öffentlichen Mitgliedern gesucht wird:
STRING_CLASS_ARRAY = (neue Klasse [] {String.class});
a) Mitglied member = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) Mitglied member = targetClass.getConstructor (STRING_CLASS_ARRAY);
Sie werden feststellen, dass alle Grundelemente (Integer, Long usw.) und alle Grundlagen (BigInteger, BigDecimal usw.) und sogar java.regex.Pattern über diesen Ansatz abgedeckt werden. Ich habe dies mit großem Erfolg bei Produktionsprojekten verwendet, bei denen es eine große Anzahl beliebiger String-Werteingaben gibt, bei denen eine strengere Überprüfung erforderlich war. Wenn bei diesem Ansatz keine Methode vorhanden ist oder wenn die Methode aufgerufen wird, wird eine Ausnahme ausgelöst (da es sich um einen unzulässigen Wert handelt, z. B. eine nicht numerische Eingabe in ein BigDecimal oder eine unzulässige RegEx für ein Muster), die die Prüfung für bereitstellt die inhärente Logik der Zielklasse.
Dies hat einige Nachteile:
1) Sie müssen die Reflexion gut verstehen (dies ist etwas kompliziert und nicht für Anfänger). 2) Einige der Java-Klassen und Bibliotheken von Drittanbietern sind (überraschenderweise) nicht richtig codiert. Das heißt, es gibt Methoden, die ein einzelnes Zeichenfolgenargument als Eingabe verwenden und eine Instanz der Zielklasse zurückgeben, aber es ist nicht das, was Sie denken ... Betrachten Sie die Integer-Klasse:
static Integer getInteger(String nm) Determines the integer value of the system property with the specified name.
Die obige Methode hat wirklich nichts mit Ganzzahlen als Objekten zu tun, die primitive Ints umschließen. Reflection wird dies als einen möglichen Kandidaten für das falsche Erstellen einer Ganzzahl aus einem String im Vergleich zu den Dekodierungs-, Wert- und Konstruktorelementen finden. Diese sind alle für die meisten willkürlichen String-Konvertierungen geeignet, bei denen Sie wirklich keine Kontrolle über Ihre Eingabedaten haben, sondern nur möchten wissen, ob es möglich ist, eine Ganzzahl.
Um Abhilfe zu schaffen, ist die Suche nach Methoden, die Ausnahmen auslösen, ein guter Anfang, da ungültige Eingabewerte, die Instanzen solcher Objekte erstellen, dies tun sollten , eine Ausnahme . Leider variieren die Implementierungen dahingehend, ob die Ausnahmen als aktiviert deklariert sind oder nicht. Integer.valueOf (String) löst beispielsweise eine überprüfte NumberFormatException aus, aber Pattern.compile () -Ausnahmen werden bei Reflection-Lookups nicht gefunden. Auch hier ist ein Fehlschlagen dieses dynamischen "Cross-Casting" -Ansatzes meiner Meinung nach keine sehr standardmäßige Implementierung für Ausnahmedeklarationen in Objekterstellungsmethoden.
Wenn jemand mehr Details darüber möchte, wie das oben Genannte implementiert wurde, lassen Sie es mich wissen, aber ich denke, diese Lösung ist viel flexibler / erweiterbarer und mit weniger Code, ohne die guten Teile der Typensicherheit zu verlieren. Natürlich ist es immer am besten, "Ihre Daten zu kennen", aber wie viele von uns feststellen, sind wir manchmal nur Empfänger von nicht verwalteten Inhalten und müssen unser Bestes tun, um sie richtig zu verwenden.
Prost.
quelle
Dies ist also ein alter Beitrag, aber ich denke, ich kann etwas dazu beitragen.
Sie können immer so etwas tun:
package com.dyna.test; import java.io.File; import java.lang.reflect.Constructor; public class DynamicClass{ @SuppressWarnings("unchecked") public Object castDynamicClass(String className, String value){ Class<?> dynamicClass; try { //We get the actual .class object associated with the specified name dynamicClass = Class.forName(className); /* We get the constructor that received only a String as a parameter, since the value to be used is a String, but we could easily change this to be "dynamic" as well, getting the Constructor signature from the same datasource we get the values from */ Constructor<?> cons = (Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class}); /*We generate our object, without knowing until runtime what type it will be, and we place it in an Object as any Java object extends the Object class) */ Object object = (Object) cons.newInstance(new Object[]{value}); return object; } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { DynamicClass dynaClass = new DynamicClass(); /* We specify the type of class that should be used to represent the value "3.0", in this case a Double. Both these parameters you can get from a file, or a network stream for example. */ System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0")); /* We specify a different value and type, and it will work as expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and File.toString() would do. */ System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath")); }
Natürlich ist dies kein wirklich dynamisches Casting, wie in anderen Sprachen (z. B. Python), da Java eine statisch typisierte Sprache ist. Dies kann jedoch einige Randfälle lösen, in denen Sie einige Daten je nach Kennung auf unterschiedliche Weise laden müssen. Außerdem könnte der Teil, in dem Sie einen Konstruktor mit einem String-Parameter erhalten, möglicherweise flexibler gestaltet werden, indem dieser Parameter von derselben Datenquelle übergeben wird. Dh aus einer Datei erhalten Sie die Konstruktorsignatur, die Sie verwenden möchten, und die Liste der zu verwendenden Werte. Auf diese Weise verbinden Sie beispielsweise den ersten Parameter mit einem String, wobei das erste Objekt ihn als String umwandelt. Das nächste Objekt ist eine Ganzzahl usw., aber irgendwann während der Ausführung Ihres Programms erhalten Sie jetzt zuerst ein Dateiobjekt, dann ein Double usw.
Auf diese Weise können Sie diese Fälle berücksichtigen und ein etwas "dynamisches" Casting im laufenden Betrieb durchführen.
Ich hoffe, dies hilft jedem, da dies in der Google-Suche immer wieder auftaucht.
quelle
Ich hatte kürzlich das Gefühl, dass ich dies auch tun musste, fand dann aber einen anderen Weg, der meinen Code möglicherweise ordentlicher aussehen lässt und eine bessere OOP verwendet.
Ich habe viele Geschwisterklassen, die jeweils eine bestimmte Methode implementieren
doSomething()
. Um auf diese Methode zugreifen zu können, müsste ich zuerst eine Instanz dieser Klasse haben, aber ich habe eine Oberklasse für alle meine Geschwisterklassen erstellt und jetzt kann ich über die Oberklasse auf die Methode zugreifen.Im Folgenden zeige ich zwei Möglichkeiten alternativer Möglichkeiten zum "dynamischen Casting".
// Method 1. mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); switch (mUnitNum) { case 0: ((MyFragment0) mFragment).sortNames(sortOptionNum); break; case 1: ((MyFragment1) mFragment).sortNames(sortOptionNum); break; case 2: ((MyFragment2) mFragment).sortNames(sortOptionNum); break; }
und meine derzeit verwendete Methode,
// Method 2. mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum)); mSuperFragment.sortNames(sortOptionNum);
quelle
Ich dachte nur, ich würde etwas posten, das ich sehr nützlich fand und das für jemanden möglich sein könnte, der ähnliche Bedürfnisse hat.
Die folgende Methode wurde von mir für meine JavaFX-Anwendung geschrieben, um zu vermeiden, dass bei jeder Rückgabe des Controllers eine x-Instanz von Objekt-b-Anweisungen umgewandelt werden muss.
public <U> Optional<U> getController(Class<U> castKlazz){ try { return Optional.of(fxmlLoader.<U>getController()); }catch (Exception e){ e.printStackTrace(); } return Optional.empty(); }
Die Methodendeklaration zum Erhalt des Controllers war
public <T> T getController()
Durch die Verwendung des Typs U, der über das Klassenobjekt an meine Methode übergeben wird, kann er an die Methode get controller weitergeleitet werden, um ihm mitzuteilen, welcher Objekttyp zurückgegeben werden soll. Ein optionales Objekt wird zurückgegeben, falls die falsche Klasse angegeben wird und eine Ausnahme auftritt. In diesem Fall wird ein leeres optionales Objekt zurückgegeben, auf das wir prüfen können.
So sah der letzte Aufruf der Methode aus (wenn das zurückgegebene optionale Objekt vorhanden ist, wird ein Consumer benötigt
quelle
Versuchen Sie dies für Dynamic Casting. Es wird klappen!!!
String something = "1234"; String theType = "java.lang.Integer"; Class<?> theClass = Class.forName(theType); Constructor<?> cons = theClass.getConstructor(String.class); Object ob = cons.newInstance(something); System.out.println(ob.equals(1234));
quelle