23

我有一个java.lang.reflect.InvocationHandler并且我需要实现方法 invoke()

java.lang.String从我的阐述中得到了一个类型值,我需要将此值转换为方法期望的适当 returnType(它可以是像 int、boolean、double 或像 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's 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;
}

到目前为止,以上还不是我看到的最糟糕的:)

有人在这里有什么秘密吗?

4

6 回答 6

30

据我所知,您提供的版本没有真正的替代品。您可以稍微简化一下(因为包装器类型都是 all final),但您基本上需要使用iforswitch或散列来打开类。

我的建议是像上面那样编码。丑陋的代码本身只是一个问题,如果您必须查看它。所以把它放在一个实用方法中,不要再看它了。


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)直接返回包装对象的方法。

我没有声称这不那么难看……但是“美”并不是衡量代码质量的有用指标,因为它是主观的,而且它并不能告诉您代码是否易于理解和/或维护。

更新

为了也支持原始类型,将相应的类添加到if条件中;例如

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

现在可能已经到了在类型名称上进行 String 切换更有效的地步,尽管需要考虑一些稍微棘手的类型标识问题。(理论上,您可以拥有由不同类加载器加载的具有相同全名的多个类型。我认为您需要在类加载器中“快速而松散地”使用原始包装类来做到这一点......但是我认为这仍然是可能的。)

于 2012-12-19T00:15:34.677 回答
26

我想我找到了些东西

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 行代码比多个 if 更好,并且我避免添加外部库依赖项,因为java.beans包位于 Java 标准库 (javadocs: PropertyEditorManager) 中。

我觉得这是可以接受的;我唯一的困惑是它PropertyEditor包含在java.beans包中,我更喜欢包中可用的东西java.utiljava.lang.reflect因为这个代码实际上与它无关java.beans

上面的代码还有一个优点是您可以注册额外的PropertyEditor实例来翻译复杂的对象,顺便说一句。不过,这并不是一件坏事。

我认为它比如果列表更好,在美丽方面,而且在质量方面。

于 2012-12-19T09:33:25.940 回答
7

可能org.apache.commons.beanutils.ConvertUtils可以提供帮助?

import org.apache.commons.beanutils.ConvertUtils;
// ...
final Object v = ConvertUtils.convert("42", Integer.class);
于 2017-06-01T15:44:06.710 回答
5

有一个轻量级库可以将字符串解析为你想要的 java 类型。它被称为类型解析器,你可以在 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());
}
于 2014-05-04T12:31:43.440 回答
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
    }
}

这看起来更干净还是更丑陋,我会留给你。

于 2012-12-19T00:19:41.257 回答
4

在 jdk8 中,您现在可以在没有 if 语句的情况下执行类似 O(1) 的查找时间...

现在可以正确处理空值的更好版本在这里

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);
于 2016-06-14T02:26:09.077 回答