I have a java.lang.reflect.InvocationHandler
and I need to implement the method invoke()
I have a value of type java.lang.String
from my elaboration and I need to convert this value to the appropriate returnType expected by the method (it can be a primitive like int, boolean, double or wrapper classes like Boolean, Integer, Double, Float, etc).
Example:
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?
}
I am not expecting to simply implement an automatic conversion between complex objects, but I expect a simple way to convert from String to the standard java types.
I've seen (too) many times stuff like this, but it doesn't seem appropriate to me:
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;
}
and the above is not even the worse one I saw, so far :)
Does anybody have a secret trick here?
For converting a primitive type value to a string in Java, use the valueOf() method.
Converting an object of a wrapper type (Integer) to its corresponding primitive (int) value is called unboxing. The Java compiler applies unboxing when an object of a wrapper class is: Passed as a parameter to a method that expects a value of the corresponding primitive type.
We can use the Integer. parseInt() to get the corresponding primitive int value of a string or use Integer. valueOf() to get the corresponding value of Integer wrapper class. If the string is not an integer, NumberFormatException will be thrown.
1. valueOf() method: We can use the valueOf() method to create a Wrapper object for a given primitive or String.
As far as I'm aware, there is no real alternative to the version you presented. You can simplify it a bit (since the wrapper types are all final
), but you essentially need to use if
or switch
or hashing to switch on the class.
My advice is to code it like the above. Ugly code is only a problem per se if you have to look at it. So put it inside a utility method and don't look at it again.
FWIW - this is how I'd simplify the method:
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;
}
This is simpler and more efficient. And it is equivalent to the original version because the classes are all final
and because the specs state that equality for Class
objects is object identity.
Arguably, we should be using the <wrapper>.valueOf(String)
methods which return the wrapper objects directly.
I make no claim that this is less ugly ... but "beauty" is not a useful measure of code quality, because it is subjective and because it doesn't tell you whether the code is easy to understand and / or maintain.
UPDATE
To support primitive types as well, add the corresponding classes to the if
conditions; e.g.
if (Boolean.class == clazz || Boolean.TYPE == clazz) {
return Boolean.parseBoolean(value);
}
It may now be getting to the point where doing a String switch on the type's name is more efficient, though there are some slightly knotty issues of type identity that need to be thought through. (In theory, you can have multiple types with the same full name that have been loaded by different classloaders. I think you'd need to "play fast and loose" in a classloader to do that with the primitive wrapper classes ... but I think it might still be possible.)
I think I found something
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();
}
I think that those 3 lines of code are better than the multiple ifs, and I avoided to add external library dependencies, since java.beans
package is inside the Java standard libraries (javadocs: PropertyEditorManager
).
I find it quite acceptable; my only perplexity is that PropertyEditor
is contained in java.beans
package and I would have preferred something available in java.util
or java.lang.reflect
package, since this code has nothing to do with java.beans
actually.
The code above has also the advantage that you can register additional PropertyEditor
instances to translate complex objects, btw. That's not a bad thing to have though.
I think it's better than a list of ifs, in beauty, but also in quality.
Probably org.apache.commons.beanutils.ConvertUtils can help?
import org.apache.commons.beanutils.ConvertUtils;
// ...
final Object v = ConvertUtils.convert("42", Integer.class);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With