如何从 String 转换为基元类型或标准 java 包装器类型

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

我从我的阐述中有一个类型的值,我需要将此值转换为该方法期望的适当returnType(它可以是一个基元,如int,布尔值,双精度类或包装类,如布尔值,整数,双精度值,浮点数等)。java.lang.String

例:

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

以上甚至不是我看到的最糟糕的一个,到目前为止:)

有人在这里有秘密把戏吗?


答案 1

据我所知,除了您介绍的版本之外,没有真正的替代方案。你可以稍微简化一下(因为包装器类型都是),但你基本上需要使用or或或散列来打开类。finalifswitch

我的建议是像上面那样编写代码。丑陋的代码本身只是一个问题,如果你必须看它。因此,将其放在实用程序方法中,不要再看它了。


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

这更简单、更高效。它等同于原始版本,因为类都是,并且因为规范声明对象的相等性是对象标识。finalClass

可以说,我们应该使用直接返回包装器对象的方法。<wrapper>.valueOf(String)

我没有声称这不那么丑陋...但是“美”不是衡量代码质量的有用指标,因为它是主观的,因为它没有告诉你代码是否易于理解和/或维护。

更新

要支持基元类型,请将相应的类添加到条件中;例如:if

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

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


答案 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标准库(javadocs:PropertyEditorManager)内部。java.beans

我认为这完全可以接受。我唯一的困惑是它包含在包中,我更喜欢在包中可用的东西,因为这段代码实际上与实际无关。PropertyEditorjava.beansjava.utiljava.lang.reflectjava.beans

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

我认为这比一系列ifs更好,在美感上,而且在质量上。