it-swarm.com.de

java: Wie kann ich eine Variable dynamisch von einem Typ zum anderen gießen?

ich möchte dynamisches Casting für eine Java-Variable durchführen, der Casting-Typ wird in einer anderen Variablen gespeichert.

dies ist ein regelmäßiges Casting:

 String a = (String) 5;

das ist was ich will:

 String theType = 'String';
 String a = (theType) 5;

ist es möglich? und wenn ja wie? Vielen Dank!

aktualisieren

Ich versuche, eine Klasse mit einer HashMap zu füllen, die ich erhalten habe.

das 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 Klassenvariablen vom Typ Double sind und wenn die Nummer 3 empfangen wird, sie als Integer betrachtet wird und ich ein Typproblem habe. 

67
ufk

Die einzige Möglichkeit, dieses Problem in Java zu lösen, besteht darin, Code zu schreiben, der alle Fälle mit vielen if- und else- und instanceof-Ausdrücken abdeckt. Was Sie zu tun versuchen, sieht so aus, als würden Sie mit dynamischen Sprachen programmieren. In statischen Sprachen ist das, was Sie versuchen, fast unmöglich, und Sie würden wahrscheinlich einen völlig anderen Ansatz für das, was Sie versuchen, wählen. Statische Sprachen sind einfach nicht so flexibel wie dynamische :)

Gute Beispiele für Java Best Practices sind die Antwort von BalusC (dh ObjectConverter) und die Antwort von Andreas_D (dh Adapter).


Das ergibt keinen Sinn 

String a = (theType) 5;

der Typ von a ist statisch an String gebunden, so dass es keinen Sinn macht, eine dynamische Umwandlung in diesen statischen Typ durchzuführen. 

PS: _ ​​Die erste Zeile Ihres Beispiels könnte als Class<String> stringClass = String.class; geschrieben werden, aber Sie können mit stringClass keine Variablen umwandeln. 

13
akuhn

Ja, es ist möglich mit Reflection

Object something = "something";
String theType = "Java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);

das macht jedoch keinen Sinn, da das resultierende Objekt in einer Variablen vom Objekttyp gespeichert werden muss. Wenn Sie benötigen, dass die Variable einer bestimmten Klasse angehört, können Sie sie einfach in diese Klasse umwandeln.

Wenn Sie eine bestimmte Klasse erhalten möchten, geben Sie beispielsweise Number ein:

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 hat immer noch keinen Sinn, Sie könnten einfach auf Number setzen.

Das Casting eines Objekts ändert nichts. es ist nur der Weg der Compiler behandelt es.
Der einzige Grund, etwas zu tun, besteht darin, zu prüfen, ob es sich bei dem Objekt um eine Instanz der angegebenen Klasse oder einer Unterklasse handelt, aber dies wäre besser mit instanceof oder Class.isInstance() möglich.

Aktualisieren

entsprechend Ihrem letzten update ist das eigentliche Problem, dass Sie eine Ganzzahl in Ihrer HashMap haben, die einem Double zugewiesen werden sollte. In diesem Fall können Sie den Typ des Felds überprüfen und die xxxValue()-Methoden von Number verwenden

...
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);
...

(nicht sicher, ob ich die Vorstellung eines falschen Typs in der Map mag)}

94

Sie müssen dazu eine Art ObjectConverter schreiben. Dies ist machbar, wenn Sie sowohl über das Objekt verfügen, das Sie konvertieren möchten, als auch die Zielklasse kennen, in die Sie konvertieren möchten. In diesem Fall erhalten Sie die Zielklasse über Field#getDeclaringClass() .

hier Ein Beispiel für eine solche 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 dem gewünschten Rückgabetyp hinzu.

20
BalusC

Sie können dies mit der Methode Class.cast() tun, die den angegebenen Parameter dynamisch in den Typ der Klasseninstanz konvertiert, die Sie besitzen. Um die Klasseninstanz eines bestimmten Felds abzurufen, verwenden Sie die Methode getType() für das betreffende Feld. Ich habe unten ein Beispiel gegeben. Beachten Sie jedoch, dass hier keine Fehlerbehandlung vorgesehen ist und diese 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);
    }
}
11
Jared Russell

Sie können eine einfache castMethod wie die unten stehende schreiben.

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

In Ihrer Methode sollten Sie es gerne 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());          
    }    
}

}
6
Hoji

Es funktioniert und es gibt sogar ein gemeinsames Muster für Ihren Ansatz: das Adapter-Muster . Natürlich funktioniert es (1) nicht, um Java-Primitive in Objekte umzuwandeln, und (2) die Klasse muss anpassbar sein (normalerweise durch Implementierung einer benutzerdefinierten Schnittstelle).

Mit diesem Muster können Sie Folgendes 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 eine besondere Idee - das ist unmöglich. Sie können keinen String-Wert für das Casting verwenden.

4
Andreas_D

Ihr Problem ist nicht das Fehlen von "dynamischem Casting". Das Umwandeln von Integer in Double ist überhaupt nicht möglich. Anscheinend möchten Sie Java ein Objekt eines Typs geben, ein Feld eines möglicherweise inkompatiblen Typs, und es irgendwie automatisch herausfinden, wie zwischen den Typen konvertiert wird.

Dies ist aus sehr guten Gründen ein Widerspruch zu einer stark typisierten Sprache wie Java und IMO.

Was versuchst du eigentlich zu tun? All diese Reflexion wirkt ziemlich fischig.

2

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 tatsächlich einige Daten auf verschiedene Arten laden müssen, abhängig von einem bestimmten Bezeichner. Der Teil, in dem Sie einen Konstruktor mit einem String-Parameter erhalten, könnte möglicherweise flexibler gestaltet werden, indem dieser Parameter von derselben Datenquelle übergeben wird. Das heißt Von einer Datei erhalten Sie die Konstruktorsignatur, die Sie verwenden möchten, und die Liste der Werte, die verwendet werden sollen. Auf diese Weise koppeln Sie den ersten Parameter, sagen wir, der erste Parameter ist ein String. Das erste Objekt wandelt es als String um object ist ein Integer usw., aber irgendwann während der Ausführung Ihres Programms erhalten Sie jetzt zuerst ein File-Objekt, dann ein Double usw.

Auf diese Weise können Sie diese Fälle berücksichtigen und ein "dynamisches" Casting im laufenden Betrieb durchführen.

Ich hoffe, das hilft jedem, da dies bei Google-Suchen immer wieder auftaucht.

1
Acapulco

Die meisten Skriptsprachen (wie Perl) und nicht statische Sprachen für die Kompilierzeit (wie Pick) unterstützen automatische dynamische Konvertierung von String in (relativ beliebige) Objekte. Dies kann auch in Java durchgeführt werden, ohne die Typsicherheit zu verlieren. Die statisch typisierten Sprachen bieten OHNE die unangenehmen Nebenwirkungen einiger anderer Sprachen, die mit dynamischem Casting böse Dinge bewirken. Ein Perl-Beispiel, das einige fragwürdige Berechnungen vornimmt:

print ++($foo = '99');  # prints '100'
print ++($foo = 'a0');  # prints 'a1'

In Java wird dies (IMHO) besser durch Verwendung einer Methode erreicht, die ich "Cross-Casting" nenne. Bei Cross-Casting wird Reflektion in einem Lazy-Load-Cache von Konstruktoren und Methoden verwendet, die dynamisch über die folgenden Elemente entdeckt werden statische Methode: 

Object fromString (String value, Class targetClass)

Leider führen integrierte Java-Methoden wie Class.cast () dies nicht für String to BigDecimal oder String to Integer oder andere Konvertierungen durch, bei denen keine Klassenhierarchie unterstützt wird. Meines Erachtens geht es darum, einen vollständig dynamischen Weg zu bieten, um dies zu erreichen. Ich denke nicht, dass die vorherige Referenz der richtige Ansatz ist - jede Konvertierung muss codiert werden. Einfach ausgedrückt, ist die Implementierung nur ein Cast-from-String, wenn dies legal/möglich ist.

Die Lösung ist also eine einfache Reflexion, die nach öffentlichen Mitgliedern sucht:

STRING_CLASS_ARRAY = (neue Klasse [] {String.class});

a) Member member = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) Member 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 alle durch diesen Ansatz abgedeckt werden. Ich habe dies mit großem Erfolg bei Produktionsprojekten verwendet, bei denen eine große Anzahl von beliebigen String-Eingabewerten vorhanden ist, 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 dies ein unzulässiger Wert ist, beispielsweise eine nicht-numerische Eingabe in ein BigDecimal-Format oder ein ungültiges RegEx für ein Muster), wird die Überprüfung spezifisch durchgeführt die inhärente Logik der Zielklasse.

Es gibt einige Nachteile:

1) Sie müssen die Reflexion gut verstehen (dies ist ein wenig kompliziert und nicht für Anfänger geeignet) . 2) Einige Java-Klassen und Drittanbieter-Bibliotheken sind (Überraschung) nicht ordnungsgemäß codiert. Das heißt, es gibt Methoden, die ein einzelnes String-Argument 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 Integer-Werten zu tun, wenn Objekte die Primitiven ints umwickeln. Dies wird von Reflection als möglicher Kandidat für das Erstellen einer Integer-Zahl aus einer Zeichenfolge im Vergleich zu Decodier-, Valueof- und Konstruktor-Members angesehen, die alle für die meisten geeignet sind beliebige String-Konvertierungen, bei denen Sie wirklich keine Kontrolle über Ihre Eingabedaten haben, sondern nur wissen möchten, ob eine Ganzzahl möglich ist.

Um Abhilfe zu schaffen, ist das Suchen nach Methoden, die Exceptions auslösen, ein guter Anfang, da ungültige Eingabewerte, die Instanzen solcher Objekte erstellen, sollte eine Exception auslösen. Leider variieren die Implementierungen, ob die Exceptions als geprüft deklariert sind oder nicht. Integer.valueOf (String) löst beispielsweise eine geprüfte NumberFormatException aus, aber Pattern.compile () -Ausnahmen werden während Reflection-Lookups nicht gefunden. Wiederum kein Misserfolg dieses dynamischen "Cross-Casting" -Ansatzes halte ich für eine sehr nicht standardisierte Implementierung von Ausnahmedeklarationen in Objekterstellungsmethoden.

Wenn jemand mehr Informationen darüber erhalten möchte, wie das Obige 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 finden, sind wir manchmal nur Empfänger von nicht verwaltetem Inhalt und müssen das Beste tun, um sie richtig zu nutzen.

Prost.

1
Darrell Teague

Tu das nicht. Verwenden Sie stattdessen einen ordnungsgemäß parametrisierten Konstruktor. Die Einstellungen und Typen der Verbindungsparameter sind ohnehin festgelegt, sodass es keinen Sinn macht, dies alles dynamisch zu tun.

1
Bandi-T

Ich hatte kürzlich das Gefühl, ich müsste das auch tun, fand dann aber einen anderen Weg, der meinen Code möglicherweise netter aussehen lässt und besseres OOP verwendet.

Ich habe viele Geschwisterklassen, die jeweils eine bestimmte Methode doSomething() implementieren. Um auf diese Methode zuzugreifen, müsste ich zuerst eine Instanz dieser Klasse haben, aber ich habe eine Superklasse für alle meine Geschwisterklassen erstellt und kann nun auf die Methode von der Superklasse aus zugreifen.

Im Folgenden zeige ich zwei Möglichkeiten, alternative Wege zum "dynamischen Casting" zu finden.

// 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);
0
Anonsage

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));
0
Sunil M M

Ich dachte nur, ich würde etwas posten, das ich für nützlich halte und für jemanden mit ähnlichen Bedürfnissen möglich wäre.

Die folgende Methode war eine Methode, die ich für meine JavaFX-Anwendung geschrieben habe, um das Casting zu vermeiden und zu vermeiden, dass bei jeder Rückgabe des Controllers Objekt-x-Instanz von Object-b-Anweisungen geschrieben wird.

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 Erhalten des Controllers war

public <T> T getController()

Wenn Sie den Typ U verwenden, der über das Klassenobjekt an meine Methode übergeben wird, könnte er an die Methode get controller weitergeleitet werden, um anzugeben, welche Art von Objekt zurückgegeben werden soll. Ein optionales Objekt wird zurückgegeben, wenn die falsche Klasse geliefert 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 endgültige Aufruf der Methode aus (wenn ein optionales Objekt zurückgegeben wird, nimmt ein Consumer an

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());
0
Eladian