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?
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);
I propose this:
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 } }
I’ll leave it to you whether this looks cleaner or uglier.
There is a lightweight library that parses strings to java types which does what you want. It’s called type-parser and you can find it on github here.
Your above code could then look something like this:
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()); }
in jdk8, you could now do something like so O(1) lookup time with no if statements…
A better version now that handles nulls correct is here
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); }
such that you can then call
primitiveTranslator.getConverter(Integer.TYPE).apply(stringToConvert);