java: comment puis-je faire la coulée dynamique d'une variable d'un type à l'autre?

StackOverflow https://stackoverflow.com/questions/2127318

  •  22-09-2019
  •  | 
  •  

Question

Je voudrais faire la coulée dynamique pour une variable java, le type de coulée est stocké dans une autre variable.

est coulée régulière:

 String a = (String) 5;

est ce que je veux:

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

est-il possible? et si oui, comment? merci!

mise à jour

Je suis en train de remplir une classe avec un hashmap que je recevais.

est le constructeur:

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

}

le problème ici est que certaines des variables de classes sont de type double, et si le numéro 3 est reçu comme il voit entier et j'ai problème de type.

Était-ce utile?

La solution

  

En ce qui concerne votre mise à jour, la seule façon de résoudre ce en Java est d'écrire du code qui couvre tous les cas avec beaucoup de if et else et expressions instanceof. Qu'est-ce que vous essayez de le faire semble que sont utilisés pour programmer avec des langages dynamiques. Dans les langues statiques, ce que vous essayez de faire est presque impossible et ne choisirait probablement ce que vous essayez de faire une approche totalement différente. langues statiques sont tout simplement pas aussi flexible que les dynamiques:)

     

De bons exemples de meilleures pratiques Java sont les réponse par BalusC (c.-à-ObjectConverter) et réponse par Andreas_D (c.-à-Adapter) ci-dessous.


Cela n'a pas de sens, dans

String a = (theType) 5;

le type de a est statiquement lié à String il ne fait pas de sens d'avoir un casting dynamique à ce type statique.

PS:. La première ligne de votre exemple pourrait être écrit comme Class<String> stringClass = String.class; mais encore, vous ne pouvez pas utiliser stringClass pour lancer des variables

Autres conseils

Oui, il est possible en utilisant Réflexion

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

mais cela ne fait pas beaucoup de sens puisque l'objet résultant doit être enregistré dans une variable de type d'objet. Si vous avez besoin de la variable est d'une classe donnée, vous pouvez simplement jeter à cette classe.

Si vous souhaitez obtenir une classe donnée, numéro par exemple:

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

mais il n'y a toujours pas de point de le faire, vous pouvez simplement jeter en nombre.

Castings d'un objet ne change rien; il est juste la façon le compilateur le traite.
La seule raison pour faire quelque chose comme ça, est de vérifier si l'objet est une instance de la classe donnée ou d'une sous-classe de celui-ci, mais ce serait mieux fait en utilisant instanceof ou Class.isInstance().

Mise à jour

selon votre dernier mise à jour le vrai problème est que vous avez un nombre entier dans votre HashMap qui devrait être affecté à un double. Ce que vous pouvez faire dans ce cas, est de vérifier le type de terrain et utiliser les méthodes de xxxValue() de Nombre

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

(pas sûr si j'aime l'idée d'avoir le mauvais type dans la carte)

Vous aurez besoin d'écrire pour ce genre de ObjectConverter. Ce qui est faisable si vous avez à la fois l'objet que vous voulez convertir et vous connaissez la classe cible à laquelle vous souhaitez convertir. Dans ce cas particulier, vous pouvez obtenir la classe cible par Field#getDeclaringClass() .

Vous pouvez trouver ici un exemple d'un tel ObjectConverter . Il devrait vous donner l'idée de base. Si vous voulez plus de possibilités de conversion, il suffit d'ajouter plus de méthodes pour avec le type d'argument et le rendement souhaité.

Vous pouvez le faire en utilisant le méthode Class.cast() qui jette dynamiquement le paramètre fourni au type de l'instance de classe que vous avez. Pour obtenir l'instance de classe d'un domaine particulier, vous utilisez le getType() méthode sur le terrain en question. J'ai donné un exemple ci-dessous, mais notez qu'il omet toutes les manipulations d'erreur et ne doit pas être utilisé sans modification.

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

Vous pouvez écrire simple castMethod comme celui ci-dessous.

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

Dans votre méthode, vous devez utiliser comme

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

}

Il fonctionne et il y a même un modèle commun pour votre approche: modèle Adapter . Mais bien sûr, (1) il ne fonctionne pas pour la coulée des primitives Java aux objets et (2) la classe doit être adaptable (généralement en mettant en œuvre une interface personnalisée).

Avec ce modèle, vous pouvez faire quelque chose comme:

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

et la méthode getAdapter en classe Wolf:

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
}

Pour votre idée spéciale - qui est impossible. Vous ne pouvez pas utiliser une valeur de chaîne pour la coulée.

Votre problème n'est pas le manque de « casting dynamique ». Integer coulée à Double n'est pas possible du tout. Vous semblez vouloir donner un objet Java d'un type, un champ d'un type peut-être incompatible, et avoir en quelque sorte figure automatiquement comment convertir les types.

Ce genre de chose est anathème à un langage fortement typé comme Java, et l'OMI pour de très bonnes raisons.

Qu'est-ce que vous essayez de faire réellement? Tout ce que l'utilisation de la réflexion semble assez louche.

Ne pas faire cela. Il suffit de disposer d'un constructeur correctement paramétrés à la place. L'ensemble et les types des paramètres de connexion sont fixés de toute façon, donc il n'y a aucun intérêt à faire tout cela dynamiquement.

Pour ce qu'elle vaut, la plupart des langages de script (comme Perl) et les langues de compilation non statiques (comme grattées) prennent en charge dynamique chaîne d'exécution automatique de conversions d'objets (relativement arbitraires). Ceci peut être accompli en Java et sans perdre de type sécurité et les bonnes choses typées langues statiquement fournissent sans les désagréables effets secondaires de certaines des autres langues qui font le mal avec la coulée dynamique. Un exemple Perl qui fait un peu de mathématiques discutables:

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

En Java, ce qui est mieux accompli (à mon humble avis) en utilisant une méthode que j'appelle « cross-casting ». Avec contre-moulage, la réflexion est utilisé dans un cache paresseux chargé des constructeurs et des méthodes qui sont dynamiquement découvert par la méthode statique suivante:

Object fromString (String value, Class targetClass)

Malheureusement, aucune des méthodes intégrées Java telles que Class.cast () le fera pour chaîne à BigDecimal ou à chaîne entière ou tout autre conversion où il n'y a pas de hiérarchie de classe de soutien. Pour ma part, le point est de fournir un moyen entièrement dynamique pour y parvenir - pour que je ne pense pas que la référence préalable est la bonne approche - avoir à coder chaque conversion. Autrement dit, la mise en œuvre est juste de jeter-de-chaîne si elle est légale / possible.

La solution est donc simple réflexion à la recherche pour les membres du public soit:

STRING_CLASS_ARRAY = (nouvelle classe [] {String.class});

a) élément de membre = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) élément de membre = targetClass.getConstructor (STRING_CLASS_ARRAY);

Vous constaterez que toutes les primitives (Integer, Long, etc.) et toutes les bases (BigInteger, BigDecimal, etc.) et même java.regex.Pattern sont tous couverts par cette approche. Je l'ai utilisé avec succès cette importante sur les projets de production où il y a une énorme quantité d'entrées arbitraires de chaîne dont la valeur des vérifications plus strictes était nécessaire. Dans cette approche, s'il n'y a pas de méthode ou lorsque la méthode est appelée une exception est levée (parce qu'il est une valeur illégale comme une entrée non numérique à un RegEx BigDecimal ou illégal pour un motif), qui fournit la vérification spécifique la classe cible logique inhérente.

Il y a quelques inconvénients à ceci:

1) Vous devez comprendre une réflexion bien (ce qui est un peu compliqué et non pour les novices). 2) Certaines des classes Java et même les bibliothèques 3ème partie sont (surprise) pas codés correctement. Autrement dit, il existe des méthodes qui prennent un seul argument de chaîne en entrée et renvoient une instance de la classe cible, mais ce n'est pas ce que vous pensez ... Considérez la classe entière:

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

La méthode ci-dessus n'a vraiment rien à voir avec Entiers comme des objets d'emballage ints primitives. La réflexion trouvera cela comme un candidat possible pour la création d'un nombre entier d'une chaîne mal par rapport aux decode, ValueOf et constructeur membres - qui sont tous adaptés pour les conversions de chaînes les plus arbitraires où vous n'avez pas vraiment le contrôle sur vos données d'entrée, mais je veux juste savoir s'il est possible un nombre entier.

Pour remédier à ce qui précède, la recherche de méthodes qui jettent des exceptions est un bon début parce que les valeurs d'entrée non valides qui créent des instances de ces objets devraient jeter une exception. Malheureusement, les implémentations varient quant à savoir si les exceptions sont déclarées ou non vérifiées. Integer.valueOf (String) jette un NumberFormatException vérifié par exemple, mais des exceptions Pattern.compile () ne sont pas trouvés au cours des recherches de réflexion. Encore une fois, pas un échec de cette approche « cross-casting » dynamique je pense que tant en tant que mise en œuvre très non standard pour les déclarations d'exception dans les méthodes de création d'objets.

Si quelqu'un souhaite plus de détails sur la façon dont ce qui précède a été mis en œuvre, laissez-moi savoir, mais je pense que cette solution est beaucoup plus souple / extensible et avec moins de code sans perdre les bonnes parties de type sécurité. Bien sûr, il est toujours préférable de « connaître tes données », mais comme beaucoup d'entre nous trouver, noussont parfois seuls bénéficiaires de contenu non géré et doivent faire le mieux que nous pouvons l'utiliser correctement.

Vive.

Alors, c'est un ancien poste, mais je pense que je peux apporter quelque chose.

Vous pouvez toujours faire quelque chose comme ceci:

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

Bien sûr, ce n'est pas coulée vraiment dynamique, comme dans d'autres langues (Python par exemple), parce que Java est un lang statiquement typé. Cependant, cela peut résoudre certains cas marginaux où vous avez réellement besoin de charger des données de différentes manières, selon certains identifiant. En outre, la partie où vous obtenez un constructeur avec un paramètre de chaîne pourrait être probablement plus flexible, en ayant passé ce paramètre à partir de la même source de données. C'est à dire. à partir d'un fichier, vous obtenez la signature du constructeur que vous souhaitez utiliser, et la liste des valeurs à utiliser, de cette façon vous jumeler, par exemple, le premier paramètre est une chaîne, avec le premier objet, coulée comme une chaîne, suivant objet est un entier, etc, mais somehwere le long de l'exécution de votre programme, vous obtenez maintenant un objet de fichier, puis un double, etc.

De cette façon, vous pouvez expliquer ces cas, et faire un peu « dynamique » sur la coulée à la volée.

Espérons que cela aide quelqu'un que cela continue à tourner dans les recherches Google.

J'ai récemment senti que je devais le faire aussi, mais trouvé une autre façon qui fait peut-être mon regard code plus propre, et utilise mieux POO.

J'ai beaucoup de classes de frères et soeurs que chaque implantent une certaine méthode doSomething(). Pour accéder à cette méthode, je dois avoir une instance de cette classe de première, mais je créé une superclasse pour toutes mes classes de frères et soeurs et maintenant je peux accéder à la méthode de la superclasse.

Ci-dessous je montre deux façons d'autres façons de « casting dynamique ».

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

et ma méthode actuellement utilisée,

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

Je pensais juste que je poste quelque chose que je trouve très utile et pourrait être possible pour une personne qui éprouve des besoins similaires.

La méthode suivante est une méthode que j'ai écrit pour mon application JavaFX pour éviter d'avoir à lancer et aussi éviter d'écrire si l'objet x instance d'objet relevés b chaque fois que le contrôleur a été renvoyé.

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

La déclaration de méthode d'obtention de la commande était

public <T> T getController()

En utilisant le type U passé dans ma méthode via l'objet de classe, il pourrait être transmis à la méthode de commande get pour lui dire quel type d'objet pour revenir. Un objet en option est retourné dans le cas où la classe est mal fournie et une exception se produit dans ce cas, une option vide sera retourné que nous pouvons vérifier.

est ce que le dernier appel à la méthode ressemblait à (le cas échéant de l'objet retourné en option prend un consommateur

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

Essayez ceci pour Castings dynamique. Ça va marcher!!!

    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));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top