Подтвердить что ты не робот

Как преобразовать из String в примитивный тип или стандартный тип java Wrapper

У меня есть java.lang.reflect.InvocationHandler, и мне нужно реализовать метод invoke()

У меня есть значение типа java.lang.String из моей разработки, и мне нужно преобразовать это значение в соответствующий метод returnType, ожидаемый методом (он может быть примитивным, например, int, boolean, double или wrapper classes типа Boolean, Integer, Double, Float и т.д.).

Пример:

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 the simplest way?
}

Я не ожидаю просто реализовать автоматическое преобразование между сложными объектами, но я ожидаю простой способ преобразования из String в стандартные типы java.

Я тоже много раз видел такие вещи, но мне это не кажется нужным:

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

и выше не хуже того, что я видел до сих пор:)

Есть ли у кого-нибудь секретный трюк?

4b9b3361

Ответ 1

Насколько мне известно, нет реальной альтернативы версии, которую вы представили. Вы можете немного упростить его (так как все типы обертки final), но вам по существу нужно использовать if или switch или хеширование для включения класса.

Мой совет - закодировать его, как указано выше. Уродливый код - это только проблема как таковая, если вам нужно посмотреть на нее. Поэтому поставьте его внутри метода утилиты и не смотрите на него снова.


FWIW - вот как я упростил метод:

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

Это проще и эффективнее. И это эквивалентно исходной версии, потому что все классы final и потому, что спецификации утверждают, что равенство для объектов Class является идентификатором объекта.

Возможно, мы должны использовать методы <wrapper>.valueOf(String), которые возвращают объекты оболочки напрямую.

Я не утверждаю, что это менее уродливо... но "красота" не является полезной мерой качества кода, потому что она субъективна и потому, что она не говорит вам, легко ли понять код и/или обслуживание.

UPDATE

Чтобы поддерживать примитивные типы, добавьте соответствующие классы в условия if; например.

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

Теперь он может дойти до того, что использование строкового переключателя в имени типа более эффективно, хотя есть некоторые немного зауженные проблемы типа, которые необходимо продумать. (Теоретически вы можете иметь несколько типов с тем же полным именем, которые были загружены разными загрузчиками классов. Думаю, вам нужно будет "быстро и свободно играть" в загрузчике классов, чтобы сделать это с помощью примитивных классов-оболочек... но Я думаю, что это все еще возможно.)

Ответ 2

Я думаю, что нашел что-то

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

Я думаю, что эти 3 строки кода лучше, чем множественные ifs, и я избегал добавлять внешние зависимости библиотек, так как пакет java.beans находится внутри стандартных библиотек Java (javadocs: PropertyEditorManager).

Я считаю это вполне приемлемым; моя единственная недоумение заключается в том, что PropertyEditor содержится в пакете java.beans, и я предпочел бы что-то доступное в пакете java.util или java.lang.reflect, так как этот код не имеет ничего общего с java.beans.

В приведенном выше коде также есть преимущество, что вы можете зарегистрировать дополнительные экземпляры PropertyEditor для перевода сложных объектов, кстати. Это не плохо, если есть.

Я думаю, что это лучше, чем список ifs, по красоте, но также и по качеству.

Ответ 3

Существует небольшая библиотека, которая анализирует строки на типы java, которые делают то, что вы хотите. Он называется type-parser, и вы можете найти его на github здесь.

Ваш вышеприведенный код мог бы выглядеть примерно так:

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

в jdk8, теперь вы можете сделать что-то вроде O (1) времени поиска без инструкций if...

Теперь лучшая версия, которая обрабатывает нулевые значения, находится здесь

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

чтобы вы могли вызывать

primitiveTranslator.getConverter(Integer.TYPE).apply(stringToConvert);

Ответ 5

Я предлагаю следующее:

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

Я оставлю это вам, выглядит ли это более чистым или уродливым.

Ответ 6

Возможно org.apache.commons.beanutils.ConvertUtils может помочь?

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