web-dev-qa-db-fra.com

Comment convertir de String en un type primitif ou standard Java Wrapper types

J'ai un Java.lang.reflect.InvocationHandler et je dois implémenter la méthode invoke ()

J'ai une valeur de type Java.lang.String de mon élaboration et j'ai besoin de convertir cette valeur en returnType approprié attendu par la méthode (il peut s'agir d'une primitive comme int, booléenne, double ou wrapper comme Boolean, Integer, Double, Float, etc.).

Exemple:

public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {
    String computedValue = compute(...);
    return convert(method.getReturnType(), computedValue);
}

private Object convert(Class<?> returnType, String stringValue) {
    return ...; // what's the simplest way?
}

Je ne m'attends pas à simplement implémenter une conversion automatique entre des objets complexes, mais je m'attends à un moyen simple de convertir de String aux types standard Java.

J'ai vu (trop) de fois des trucs comme ça, mais ça ne me semble pas approprié:

public static Object toObject( Class clazz, String value ) {
    if( Boolean.class.isAssignableFrom( clazz ) ) return Boolean.parseBoolean( value );
    if( Byte.class.isAssignableFrom( clazz ) ) return Byte.parseByte( value );
    if( Short.class.isAssignableFrom( clazz ) ) return Short.parseShort( value );
    if( Integer.class.isAssignableFrom( clazz ) ) return Integer.parseInteger( value );
    if( Long.class.isAssignableFrom( clazz ) ) return Long.parseLong( value );
    if( Float.class.isAssignableFrom( clazz ) ) return Float.parseFloat( value );
    if( Double.class.isAssignableFrom( clazz ) ) return Double.parseDouble( value );
    return value;
}

et ce qui précède n'est même pas le pire que j'ai vu jusqu'à présent :)

Quelqu'un a-t-il un truc secret ici?

21
Luigi R. Viggiano

Pour autant que je sache, il n'y a pas de véritable alternative à la version que vous avez présentée. Vous pouvez le simplifier un peu (puisque les types d'encapsuleurs sont tous final), mais vous devez essentiellement utiliser if ou switch ou un hachage pour activer la classe.

Mon conseil est de le coder comme ci-dessus. Le code laid n'est qu'un problème en soi si vous devez le regarder. Mettez-le donc dans une méthode utilitaire et ne le regardez plus.


FWIW - voici comment je simplifierais la méthode:

public static Object toObject( Class clazz, String value ) {
    if( Boolean.class == clazz ) return Boolean.parseBoolean( value );
    if( Byte.class == clazz ) return Byte.parseByte( value );
    if( Short.class == clazz ) return Short.parseShort( value );
    if( Integer.class == clazz ) return Integer.parseInt( value );
    if( Long.class == clazz ) return Long.parseLong( value );
    if( Float.class == clazz ) return Float.parseFloat( value );
    if( Double.class == clazz ) return Double.parseDouble( value );
    return value;
}

C'est plus simple et plus efficace. Et elle est équivalente à la version d'origine car les classes sont toutes final et parce que les spécifications indiquent que l'égalité pour les objets Class est l'identité de l'objet.

On devrait peut-être utiliser les méthodes <wrapper>.valueOf(String) qui retournent directement les objets wrapper.

Je ne prétends pas que c'est moins moche ... mais la "beauté" n'est pas une mesure utile de la qualité du code, car elle est subjective et parce qu'elle ne vous dit pas si le code est facile à comprendre et/ou à maintenir.

MISE À JOUR

Pour prendre également en charge les types primitifs, ajoutez les classes correspondantes aux conditions if; par exemple.

    if (Boolean.class == clazz || Boolean.TYPE == clazz) {
        return Boolean.parseBoolean(value);
    }

Il peut maintenant arriver au point où faire un changement de chaîne sur le nom du type est plus efficace, bien qu'il y ait quelques problèmes légèrement noueux d'identité de type qui doivent être réfléchis. (En théorie, vous pouvez avoir plusieurs types avec le même nom complet qui ont été chargés par différents chargeurs de classe. Je pense que vous auriez besoin de "jouer rapidement et librement" dans un chargeur de classe pour le faire avec les classes de wrapper primitif ... mais Je pense que cela pourrait encore être possible.)

27
Stephen C

Je pense avoir trouvé quelque chose

import Java.beans.PropertyEditor;
import Java.beans.PropertyEditorManager;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String returnValue = ...
    return convert(method.getReturnType(), returnValue); 
}

private Object convert(Class<?> targetType, String text) {
    PropertyEditor editor = PropertyEditorManager.findEditor(targetType);
    editor.setAsText(text);
    return editor.getValue();
}

Je pense que ces 3 lignes de code sont meilleures que les if multiples, et j'ai évité d'ajouter des dépendances de bibliothèque externes, puisque Java.beans le package est à l'intérieur des bibliothèques standard Java Java (javadocs: PropertyEditorManager ).

Je trouve cela tout à fait acceptable; ma seule perplexité est que PropertyEditor est contenu dans Java.beans package et j'aurais préféré quelque chose de disponible dans Java.util ou Java.lang.reflect package, car ce code n'a rien à voir avec Java.beans réellement.

Le code ci-dessus présente également l'avantage de pouvoir enregistrer des instances PropertyEditor supplémentaires pour traduire des objets complexes, btw. Ce n'est pas une mauvaise chose à avoir cependant.

Je pense que c'est mieux qu'une liste d'if, en beauté, mais aussi en qualité.

22
Luigi R. Viggiano

Probablement org.Apache.commons.beanutils.ConvertUtils peut aider?

import org.Apache.commons.beanutils.ConvertUtils;
// ...
final Object v = ConvertUtils.convert("42", Integer.class);
7
Borv

dans jdk8, vous pouvez maintenant faire quelque chose comme ça O(1) temps de recherche sans instructions if ...

Une meilleure version maintenant qui gère correctement les valeurs NULL est ici

https://github.com/deanhiller/webpieces/blob/master/webserver/http-router/src/main/Java/org/webpieces/router/impl/params/ObjectTranslator.Java

private Map<Class<?>, Function<String, Object>> classToUnmarshaller = new HashMap<>();
private Map<Class<?>, Function<Object, String>> classToMarshaller = new HashMap<>();

public ObjectTranslator() {
    classToUnmarshaller.put(Boolean.class, s -> s == null ? null : Boolean.parseBoolean(s));
    classToUnmarshaller.put(Boolean.TYPE, s -> Boolean.parseBoolean(s));
    classToUnmarshaller.put(Byte.class, s -> s == null ? null : Byte.parseByte(s));
    classToUnmarshaller.put(Byte.TYPE, s -> Byte.parseByte(s));
    classToUnmarshaller.put(Short.class, s -> s == null ? null : Short.parseShort(s));
    classToUnmarshaller.put(Short.TYPE, s -> Short.parseShort(s));
    classToUnmarshaller.put(Integer.class, s -> s == null ? null : Integer.parseInt(s));
    classToUnmarshaller.put(Integer.TYPE, s -> Integer.parseInt(s));
    classToUnmarshaller.put(Long.class, s -> s == null ? null : Long.parseLong(s));
    classToUnmarshaller.put(Long.TYPE, s -> Long.parseLong(s));
    classToUnmarshaller.put(Float.class, s -> s == null ? null : Float.parseFloat(s));
    classToUnmarshaller.put(Float.TYPE, s -> Float.parseFloat(s));
    classToUnmarshaller.put(Double.class, s -> s == null ? null : Double.parseDouble(s));
    classToUnmarshaller.put(Double.TYPE, s -> Double.parseDouble(s));
    classToUnmarshaller.put(String.class, s -> s);

    classToMarshaller.put(Boolean.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Boolean.TYPE, s -> s.toString());
    classToMarshaller.put(Byte.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Byte.TYPE, s -> s.toString());
    classToMarshaller.put(Short.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Short.TYPE, s -> s.toString());
    classToMarshaller.put(Integer.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Integer.TYPE, s -> s.toString());
    classToMarshaller.put(Long.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Long.TYPE, s -> s.toString());
    classToMarshaller.put(Float.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Float.TYPE, s -> s.toString());
    classToMarshaller.put(Double.class, s -> s == null ? null : s.toString());
    classToMarshaller.put(Double.TYPE, s -> s.toString());
    classToMarshaller.put(String.class, s -> s == null ? null : s.toString());
}

public Function<String, Object> getUnmarshaller(Class<?> paramTypeToCreate) {
    return classToUnmarshaller.get(paramTypeToCreate);
}

public Function<Object, String> getMarshaller(Class<?> type) {
    return classToMarshaller.get(type);
}

de telle sorte que vous puissiez ensuite appeler

primitiveTranslator.getConverter(Integer.TYPE).apply(stringToConvert);
4
Dean Hiller

Il existe une bibliothèque légère qui analyse les chaînes en types Java qui fait ce que vous voulez. Elle s'appelle analyseur de type et vous pouvez la trouver sur github ici .

Votre code ci-dessus pourrait alors ressembler à ceci:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    TypeParser parser = TypeParser.newBuilder().build();
    String computedValue = compute(...);
    return parser.parseType(computedValue,  method.getGenericReturnType());
}
4
etxalpo

Je propose ceci:

List<Class<?>> clsList = new ArrayList<Class<?>>();
clsList.add(Boolean.class);
clsList.add(Integer.class);
//etc.

for (Class<?> cls : clsList) {
    if (cls.isAssignableFrom(clazz)) {
        return cls.getMethod("valueOf", new Class[] { String.class }).invoke(null, new Object[] { value });
        //Missing in this example: Handle a few exceptions
    }
}

Je vous laisse le soin de savoir si cela semble plus propre ou plus laid.

3
luiscubal