如何从 String 转换为原始类型或标准 java Wrapper 类型

Posted

技术标签:

【中文标题】如何从 String 转换为原始类型或标准 java Wrapper 类型【英文标题】:How to convert from String to a primitive type or standard java Wrapper types 【发布时间】:2012-12-06 06:48:33 【问题描述】:

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

根据我的阐述,我有一个 java.lang.String 类型的值,我需要将此值转换为方法所期望的适当 returnType(它可以是像 int、boolean、double 或像 Boolean、Integer 这样的包装类的原始类型,双,浮动等)。

例子:

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. 为什么您的第一个示例不起作用,以及 2. “似乎不合适”是什么意思。 1.第一个示例不起作用,因为在 convert 方法中它缺少实现(这是这个问题的主题) 2. 似乎不合适意味着在我看来,由于许多 ifs,这个实现非常难看,我想有更好的方法来完成这项工作。 由于第二个示例 有效(但它很丑),codereview.stackexchange.com 可能是更好的选择。 谢谢@luiscubal,这是真的,它有效,但我不想在我的代码库中出现丑陋的东西;我看不下去了:) 我希望本机方法 isAssignableFrom() 将 == 检查作为第一个操作,因此您提出的改进应该不会有太大的不同。谢谢你的提示。我宁愿故意让丑陋的代码看起来更糟,作为以后替换它的纪念品(再次开玩笑)。 【参考方案1】:

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

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


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 对象的相等性是对象身份。

可以说,我们应该使用直接返回包装对象的&lt;wrapper&gt;.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 行代码比多个 if 更好,并且我避免添加外部库依赖项,因为 java.beans 包位于 Java 标准库中(javadocs:PropertyEditorManager)。

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

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

我认为它比 ifs 列表更好,在美观方面,但在质量方面。

【讨论】:

我认为它是唯一一个使用原始类型的 ALSO。但它不适用于android:***.com/questions/22988376/…【参考方案3】:

大概org.apache.commons.beanutils.ConvertUtils可以帮忙?

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

【讨论】:

我测试过,当它无法将字符串test解析为Long时返回0,这不好,因为我需要区分成功和失败的转换 这真的很糟糕,因为它不会在垃圾数据的情况下抛出异常。 Integer.parseInt("12") 好很多,因为它会抛出 RunTimeException,你就会知道问题出在哪里。【参考方案4】:

我建议这样做:

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
    

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

【讨论】:

我能想到的另一个避免反射的选择是创建一个 Map,Converter> 来避免反射。转换器接口可以有一个方法Object convert(String),然后将Boolean.class映射到BooleanConverter; Integer.class 到 IntegerConverter 等等……至少,这不会像多个 if 那样难看。 这不适用于原始类型:int.class.getMethod("valueOf", new Class[]String.class) throws NoSuchMethodException @LuigiR.Viggiano - 应该指出,您尝试替换的代码也不适用于原语。 这个不错。但是为什么要迭代硬编码类型呢?这正是它要解决的问题。为什么不更简单return returnType.getMethod("valueOf",String.class).invoke(null, stringValue); 它实际上适用于您列举的所有基本类型。不适用于 Atomic*.class 类,还有一些例外(如果您通过反射更改构造函数(String)的 valueO f ,例如 BigInteger,则支持某些例外);【参考方案5】:

有一个轻量级的库,可以将字符串解析为你想要的 java 类型。它被称为类型解析器,你可以在 github here 上找到它。

您上面的代码可能看起来像这样:

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

【讨论】:

【参考方案6】:

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

【讨论】:

以上是关于如何从 String 转换为原始类型或标准 java Wrapper 类型的主要内容,如果未能解决你的问题,请参考以下文章

引用类型转换为原始值(基本类型)

如何使用 Lambda 或 Linq 将匿名类型转换为原始类型成员

如何将原始电子邮件 (MIME) 从 AWS SES 转换为 Gmail?

从MySQL数据库获取后如何将python字符串转换为原始类型

vs2013 提示无法从void转换为char

JavaScript--标准函数