Domanda

mi piacerebbe fare fusione dinamica di una variabile Java, il tipo di fusione è memorizzato in una variabile diversa.

questo è colata regolare:

 String a = (String) 5;

Questo è quello che voglio:

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

è possibile? e se sì, come? grazie!

update

Sto cercando di compilare una classe con un HashMap che ho ricevuto.

Questo è il costruttore:

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());         
        }
    }

}

il problema qui è che alcune delle variabili classi sono tipo Double, e se viene ricevuto il numero 3 si vede come Integer e ho tipo di problema.

È stato utile?

Soluzione

  

Per quanto riguarda l'aggiornamento, l'unico modo per risolvere questo in Java è quello di scrivere codice che copre tutti i casi con un sacco di if e else e le espressioni instanceof. Quello che si cerca di fare sembra come se vengono utilizzati per programmare con linguaggi dinamici. In linguaggi statici, ciò che si tenta di fare è quasi impossibile, e si sarebbe probabilmente scegliere un approccio totalmente diverso per quello che si tenta di fare. linguaggi statici sono semplicemente non flessibile come quelli dinamici:)

     

Buoni esempi di Java migliori pratiche sono il risposta da BalusC (cioè ObjectConverter) e il (cioè Adapter) qui di seguito.


Questo non ha senso, in

String a = (theType) 5;

il tipo di a è destinato ad essere staticamente String in modo che non ha alcun senso di avere un cast dinamico di questo tipo statico.

PS:. La prima riga del vostro esempio potrebbe essere scritto come Class<String> stringClass = String.class;, ma ancora, non è possibile utilizzare stringClass per lanciare le variabili

Altri suggerimenti

Sì, è possibile utilizzando Riflessione

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

ma che non ha molto senso in quanto l'oggetto risultante deve essere salvato in una variabile di tipo oggetto. Se avete bisogno di essere la variabile di una data classe, si può solo lanciare a tale classe.

Se si vuole ottenere una data classe, numero ad esempio:

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

, ma non esiste ancora un punto di farlo così, si può solo lanciare al numero.

Casting di un oggetto non cambia nulla; si tratta solo di il modo il compilatore lo tratta.
L'unica ragione per fare una cosa del genere, è quello di verificare se l'oggetto è un'istanza della classe dato o di qualsiasi sottoclasse di esso, ma che sarebbe meglio fatto utilizzando instanceof o Class.isInstance().

Aggiornamento

secondo la vostra ultima Aggiorna il vero problema è che avete un intero tua HashMap che dovrebbe essere assegnato ad un doppio. Che cosa si può fare in questo caso, è controllare il tipo di campo e utilizzare i metodi di xxxValue() Numero

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

(non so se mi piace l'idea di avere il tipo sbagliato nella mappa)

Avrete bisogno di scrivere una sorta di ObjectConverter per questo. Questo è fattibile se si ha sia l'oggetto che si desidera convertire e si conosce la classe di destinazione a cui si desidera convertire. In questo caso particolare è possibile ottenere la classe di destinazione da Field#getDeclaringClass() .

Si possono trovare qui un esempio di una tale ObjectConverter . Esso dovrebbe darvi l'idea di base. Se si desidera più possibilità di conversione, basta aggiungere più metodi ad essa con il tipo di argomento e di ritorno desiderata.

È possibile farlo utilizzando il metodo Class.cast() , che getta dinamicamente il parametro fornito al tipo di istanza di classe che avete. Per ottenere l'istanza della classe di un campo particolare, si utilizza il metodo getType() sul campo in questione. Ho dato un esempio al di sotto, ma nota che omette tutta la gestione degli errori e non deve essere utilizzato senza modifiche.

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

È possibile scrivere un semplice castMethod come quella qui sotto.

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

Nel vostro metodo che dovrebbe essere utilizzando come

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());          
    }    
}

}

Si lavora e c'è anche un modello comune per il vostro approccio: il Adapter modello . Ma naturalmente, (1) non funziona per la colata primitive java agli oggetti e (2) la classe deve essere adattabile (solitamente implementando un'interfaccia personalizzata).

Con questo modello si potrebbe fare qualcosa di simile:

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

e il metodo getAdapter in classe Lupo:

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
}

Per te speciale idea - che è impossibile. Non è possibile utilizzare un valore stringa per la fusione.

Il problema non è la mancanza di "casting dinamica". Casting Integer a Double non è possibile a tutti. Lei sembra voler dare Java un oggetto di un tipo, un campo di tipo eventualmente incompatibili, e lo hanno in qualche modo capire automaticamente come convertire tra i tipi.

Questo genere di cose è un anatema per un linguaggio fortemente tipizzato come Java, e IMO per ottime ragioni.

Che cosa stai in realtà cercando di fare? Tutto ciò che l'uso di riflessione sembra piuttosto pescoso.

Non fare questo. Basta avere invece un costruttore correttamente con parametri. Il set e tipi dei parametri di connessione sono fissi in ogni caso, quindi non c'è nessun punto nel fare tutto questo in modo dinamico.

Per quello che vale, molti linguaggi di script (come Perl) e linguaggi di compilazione non statici (come nel disegno) supportano runtime automatico String dinamico (relativamente arbitrarie) conversioni oggetto. Questo può essere realizzato in Java e senza perdere tipo di sicurezza e la roba buona linguaggi staticamente tipizzati forniscono senza i fastidiosi effetti collaterali di alcune delle altre lingue che fanno cose cattive con fusione dinamica. Un esempio Perl che fa un po 'di matematica discutibile:

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

In Java, questo è meglio compiuta (IMHO) utilizzando un metodo che io chiamo "cross-casting". Con croce-casting, riflessione viene utilizzato in una cache minimale carico di costruttori e metodi dinamicamente scoperto tramite il seguente metodo statico:

Object fromString (String value, Class targetClass)

Purtroppo, non built-in metodi Java come ad esempio Class.cast () farà questo per String a BigDecimal o stringa a intero o di qualsiasi altra conversione in cui non esiste una gerarchia di classe di supporto. Per quanto mi riguarda, il punto è quello di fornire un modo completamente dinamico per raggiungere questo obiettivo - per cui non credo che il riferimento precedente è l'approccio giusto - dover codificare ogni conversione. In poche parole, l'implementazione è solo quello di cast-da-string se è legale / possibile.

Quindi, la soluzione è semplice riflessione alla ricerca di soci pubblici di entrambi:

STRING_CLASS_ARRAY = (nuova Classe [] {String.class});

a) gli Stati = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) gli Stati = targetClass.getConstructor (STRING_CLASS_ARRAY);

Troverete che tutti i primitivi (Integer, Long, ecc) e tutte le basi (BigInteger, BigDecimal, ecc) e anche java.regex.Pattern sono tutti coperti tramite questo approccio. Ho usato questo con notevole successo su progetti di produzione in cui ci sono una quantità enorme di ingressi valore stringa arbitraria in cui era necessario un po 'di controllo più severe. In questo approccio, se non esiste un metodo o quando viene richiamato il metodo viene generata un'eccezione (perché è un valore non valido come un ingresso non numerico in un BigDecimal o RegEx illegale per un motivo), che fornisce il controllo specifico la classe di destinazione logica intrinseca.

Ci sono alcuni aspetti negativi a questo:

1) È necessario capire bene di riflessione (questo è un po 'complicato e non per i principianti). 2) Alcune delle classi Java e in effetti le librerie terze parti sono (sorpresa) non codificati correttamente. Cioè, ci sono metodi che accettano un singolo argomento stringa come input e restituiscono un'istanza della classe di destinazione, ma non è quello che pensi ... Considerate la classe Integer:

static Integer getInteger(String nm)
      Determines the integer value of the system property with the specified name.

Il metodo di cui sopra non ha davvero nulla a che fare con numeri interi come oggetti confezionamento primitivi int. Riflessione troveranno questo come un possibile candidato per la creazione di un numero intero da una stringa in modo non corretto rispetto alle decodifica, valueOf e costruttore Membri - che sono tutti adatti per le conversioni di stringa più arbitrari in cui davvero non si ha il controllo su dati di input, ma voglio solo sapere se è possibile un intero.

Per porre rimedio a quanto sopra, alla ricerca di metodi che generare eccezioni è un buon inizio, perché i valori di input validi che creano le istanze di tali oggetti dovrebbe lancerà un'eccezione. Purtroppo, le implementazioni variano se le eccezioni sono dichiarate come controllato o no. Integer.valueOf (String) getta una NumberFormatException controllato per esempio, ma Pattern.compile) eccezioni (non vengono rilevate durante le ricerche di riflessione. Ancora una volta, non un difetto di questo approccio dinamico "cross-casting" Credo così tanto come un'implementazione molto non standard per le dichiarazioni eccezione in metodi di creazione degli oggetti.

Se qualcuno volesse maggiori dettagli su come è stato attuato quanto sopra, me lo faccia sapere, ma penso che questa soluzione è molto più flessibile / estensibile e con meno codice, senza perdere le buone parti del tipo di sicurezza. Naturalmente è sempre meglio di "conoscere i tuoi dati", ma come molti di noi trovare, abbiamoa volte sono solo i destinatari di contenuto gestito e hanno a che fare il meglio che possiamo usare in modo corretto.

Saluti.

Quindi, questo è un vecchio post, ma credo di poter contribuire con qualcosa ad esso.

Si può sempre fare qualcosa di simile:

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"));  
  }  

Naturalmente, questo non è realmente fusione dinamica, come in altre lingue (Python per esempio), perché Java è una lingua staticamente tipizzato. Tuttavia, questo può risolvere alcuni casi marginali in cui hai veramente bisogno di caricare alcuni dati in modi diversi, a seconda di alcuni identificatore. Inoltre, la parte in cui si ottiene un costruttore con un parametro String potrebbe essere probabilmente reso più flessibile, per avere quel parametro passato dalla stessa fonte di dati. Cioè da un file, si ottiene la firma del costruttore che si desidera utilizzare, e l'elenco dei valori da utilizzare, in questo modo si associa fino, diciamo, il primo parametro è una stringa, con il primo oggetto, gettando come una stringa, il prossimo oggetto è un numero intero, ecc, ma perché è un posto lungo l'esecuzione del programma, si ottiene ora un oggetto File, poi un doppio, ecc.

In questo modo, è possibile tenere conto di questi casi, e fare un po ' "dinamica" casting on-the-fly.

Spero che questo aiuti chiunque come questo continua a girare in ricerche di Google.

Di recente ho sentito come ho dovuto fare anche questo, ma poi trovato un altro modo che forse rende il mio codice aspetto più ordinato, e utilizza una migliore programmazione orientata agli oggetti.

Ho molte classi di pari livello che ogni eseguono un determinato metodo di doSomething(). Per accedere a tale metodo, avrei dovuto avere un'istanza di quella classe prima, ma ho creato una superclasse per tutte le mie classi di pari livello e ora posso accedere al metodo della superclasse.

Qui di seguito vi mostro due modi modi alternativi per "casting dinamica".

// 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;
}

e il mio metodo attualmente utilizzato,

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);

Proprio pensato che avrei postare qualcosa che ho trovato molto utile e potrebbe essere possibile per qualcuno che sperimenta esigenze simili.

Il seguente metodo era un metodo che ho scritto per la mia applicazione JavaFX per evitare di dover lanciare e anche evitare di scrivere se l'oggetto x istanza di oggetto B affermazioni ogni volta che il controller è stato restituito.

public <U> Optional<U> getController(Class<U> castKlazz){
    try {
        return Optional.of(fxmlLoader.<U>getController());
    }catch (Exception e){
        e.printStackTrace();
    }
    return Optional.empty();
}

La dichiarazione metodo per ottenere il controllo era

public <T> T getController()

Utilizzando tipo U passato nel mio metodo tramite l'oggetto di classe, potrebbe essere trasmessa al controller metodo get indicare cosa tipo di oggetto per tornare. Un oggetto opzionale viene restituito nel caso in cui la classe sbagliata viene fornito e si verifica un'eccezione in tal caso, un optional vuota verrà restituito il quale possiamo controllare.

Questo è ciò che l'ultima chiamata al metodo sembrava (se presente dell'oggetto opzionale restituito prende una consumatori

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

Prova questo per Casting dinamica. Funzionerà!!!

    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));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top