Determine Correct Method Signature During Runtime - java

I am using the following the following class org.apache.poi.hssf.usermodel.HSSFCell, with a list of the following methods:
void setCellValue(boolean value)
void setCellValue(java.util.Calendar value)
void setCellValue(java.util.Date value)
void setCellValue(double value)
void setCellValue(HSSFRichTextString value)
void setCellValue(java.util.Calendar value)
void setCellValue(HSSFRichTextString value)
Take note that, there are no methods with Object as method parameter.
Now, I have no way to determine my value class type during compiled time. I can only determine my value class type during runtime. Hence, how I can determine the correct method to be called, if I do not know the method signature during compiled time?
My code is below:
final int rowCount = tableModel.getRowCount();
for (int i = 0; i < rowCount; i++) {
final HSSFRow row = sheet.createRow(i + 1);
for (int j = 0; j < columnCount; j++) {
final Object object = tableModel.getValueAt(i, j);
final Class myClass = tableModel.getColumnClass(j);
// How to perform casting during compiled time, and invoke
// the setCellValue with correct signature?
if (object != null) {
row.createCell(j).setCellValue(??); // Does not accept Object!
}
}
}
Perhaps ugly if...else with instanceof would resolve my problem. However, if I do not want the ugly if...else with instanceof, is there any better way to do so?

One way of handling this is to load that list of methods at runtime into a Map and then for each call, use the Map. That is, something like this (where this code is simplified and omits error checking):
Map<? extends Object, Method> map;
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
map.put(method.getParameterTypes()[0], method);
}
}
then when you want to call this, look up the Method in the map by argument type and use that instance.
To show this off, again with simplified but this time full code. Note that to be fully general, the code gets a little more complicated, as is shown below. If you don't have to worry about primitives (which depends on your usage) or if you don't have to worry about interfaces or superclasses, then you can simplify the example below.
Also, if you can guarantee that there will be no overlap in interfaces or superclasses in the arguments that you have to worry about, you can move all of the complicated logic into initialization (which doesn't matter if it takes 1 ms longer). In this case, all of the logic in findMethodToInvoke() would be moved into the constructor, where you would loop over all interfaces and superclasses of each method you find and add them to your parameterTypeMap. If you do this optimization, then findMethodToInvoke() becomes a single line:
return parameterTypeMap.get(test.getClass());
but without this optimization and with full generality, here's my example of how to do this:
import java.lang.reflect.*;
import java.util.*;
public class Test {
private final Map<Object, Method> parameterTypeMap = new HashMap<Object, Method>();
private final Object[] tests = {Double.valueOf(3.1415),
Boolean.TRUE,
new Date(),
new GregorianCalendar(),
new HashMap<Object, Object>()};
public Test() {
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
Class<?>[] clazzes = method.getParameterTypes();
if (clazzes.length != 1) {
continue;
}
if (clazzes[0].isPrimitive()) {
handlePrimitive(method, clazzes[0]);
}
parameterTypeMap.put(clazzes[0], method);
}
}
}
// See http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isPrimitive()
private void handlePrimitive(Method method, Class<?> clazz) {
if (clazz == Boolean.TYPE) {
parameterTypeMap.put(Boolean.class, method);
} else if (clazz == Double.TYPE) {
parameterTypeMap.put(Double.class, method);
} // ... and so on for the other six primitive types (void doesn't matter)
}
public void doTests(Setters setter) {
for (Object test : tests) {
Method method = findMethodToInvoke(test);
if (method == null) {
System.out.println("Nothing found for " + test.getClass());
continue;
}
try {
method.invoke(setter, test);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Method findMethodToInvoke(Object test) {
Method method = parameterTypeMap.get(test.getClass());
if (method != null) {
return method;
}
// Look for superclasses
Class<?> x = test.getClass().getSuperclass();
while (x != null && x != Object.class) {
method = parameterTypeMap.get(x);
if (method != null) {
return method;
}
x = x.getSuperclass();
}
// Look for interfaces
for (Class<?> i : test.getClass().getInterfaces()) {
method = parameterTypeMap.get(i);
if (method != null) {
return method;
}
}
return null;
}
public static void main(String[] args) {
Test test = new Test();
test.doTests(new Setters());
}
}
class Setters {
public void setCellValue(boolean value) {
System.out.println("boolean " + value);
}
public void setCellValue(double value) {
System.out.println("double " + value);
}
public void setCellValue(Calendar value) {
System.out.println("Calendar " + value);
}
public void setCellValue(Date value) {
System.out.println("Date " + value);
}
public void setCellValue(Map<?, ?> value) {
System.out.println("Map " + value);
}
}

If you don't have subclasses (you can still do it if you do, but it will be harder, let me know if you do) you could use reflection:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main
{
public static void main(final String[] argv)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
final Object o;
if(argv.length == 0)
{
o = "Hello";
}
else
{
o = Integer.valueOf(42);
}
callFoo(o);
}
private static void callFoo(final Object o)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method;
method = Main.class.getDeclaredMethod("foo", o.getClass());
method.invoke(null, o);
}
private static void foo(final String val)
{
System.out.println("foo(String) -> " + val);
}
private static void foo(final Integer val)
{
System.out.println("foo(Integer) -> " + val);
}
}
The downside is that you don't have the compiler tell you if you try to call a method that does not exist.
The exception handling in the code above is utter crap, but I wanted to focus on the reflection part.
Using instance of is better from the point of view that it has compile time type safety. Reflection will not have to be updated if new methods are added.

I think the instanceof is the way to go. If you think it makes your code ugly extract that instanceof expressions into an auxiliary method:
public void setCellValue(HSSFCell cell, Object value) {
if (null == cell)
throw new IllegalArgumentException("cell");
if (null == value)
throw new IllegalArgumentException("value");
if (value instanceof Double)
cell.setCellValue((Double)value); // auto-boxing will handle this
else if (value instanceof Boolean) {
cell.setCellValue((Boolean)value); // auto-boxing will handle this
} else if (value instanceof Calendar) {
cell.setCellValue((Calendar)value);
} else if ...
.....
} else {
throw new UnsupportedTypeException("Object of class " + Value.class.getName() + " not supported.");
}
}
Alternately you can use reflection. Even with reflection I think you still have to do some customization for the primitive types because the auto-boxing doesn't work for getMethod() ...
public void invokeSetCellValue(HSSFCell cell, Object obj) {
try {
Class<?> clazz = obj.getClass();
if (obj instanceof Double) {
clazz = double.class;
} else if (obj instanceof Boolean) {
clazz = boolean.class;
}
Method m = HSSFCell.class.getMethod("setCellValue", clazz);
m.invoke(cell, obj);
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}

Related

Java reflection with clone

Example I have data layer after
public class DemoData implements Cloneable {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
I want to assign data values (DemoData) to a duplicate data (DemoData clone) layer as follows
public static void main(String[] args) {
DemoData demoData = new DemoData();
demoData.setName("Class Sources");
testReflectionDemo(demoData);
}
private static DemoData testReflectionDemo(DemoData demoData) {
try {
DemoData clone = (DemoData) demoData.clone();
clone.setName(demoData.getName());
clone.setValue(demoData.getValue());
return clone;
} catch (CloneNotSupportedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
I want to convert the method testReflectionDemo(DemoData demoData) to method testReflectionDemo(T t) reflection as shown below.I do not know how to continue, please help me
public <T> T testReflectionDemo(T t){
Class<?> aClass = t.getClass();
for (Method method : aClass.getMethods()) {
}
return null;
}
Thank you all for the help for my question,I've removed the clone method, I just applied reflection.Hi #dabaicai.Your code helped me with the idea,I thought passing the value to the private field would be easier a little.
public static <T> T clazzClone(T t) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> clazzRoot = t.getClass();
Object newInstance = clazzRoot.newInstance();
Field[] fieldsClone = newInstance.getClass().getDeclaredFields();
for (Field fieldClone : fieldsClone) {
fieldClone.setAccessible(true);
fieldClone.set(newInstance, getContent(t, fieldClone.getName()));
}
return (T) newInstance;
}
private static String getContent(Object aClass, String name) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field declaredField = aClass.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
return (String) declaredField.get(aClass);
}
My program means when I need to edit user input data to output the results I want,with a common filter function
fieldClone.set(newInstance,methodYourEdit(getContent(t, fieldClone.getName())));
If the argument of testReflectionDemo is a javabean,it means that the class of argument have several a pair method of setXXX and 'getXXX,and thegetXXXdon't have argument,thesetXXX` just have one argument.If is this,the following code can copy the property from old object to new object.
Class<?> aClass = t.getClass();
Object result = aClass.newInstance();
Map<String,MethodHolder> map=new HashMap<>();
for (Method method : aClass.getMethods()) {
if(method.getName().startsWith("get") && method.getParameterTypes().length==0){
String property=method.getName().substring(3);
MethodHolder hodler = map.get(property);
if(hodler ==null){
map.put(property, new MethodHolder(property, method, null));
continue;
}
hodler.getMethod=method;
}else if (method.getName().startsWith("set") && method.getParameterTypes().length==1) {
String property=method.getName().substring(3);
MethodHolder holder = map.get(property);
if(holder ==null){
map.put(property, new MethodHolder(property, null, method));
continue;
}
holder.setMethod=method;
}
}
List<MethodHolder> collect = map.values().stream().filter(item -> item.setMethod != null && item.getMethod != null).collect(Collectors.toList());
for (MethodHolder holder : collect) {
Object property = holder.getMethod.invoke(t);
holder.setMethod.invoke(result,property);
}
return (T)result;
The MethodHolder just have some field:
public static class MethodHolder{
private String property;
private Method getMethod;
private Method setMethod;
public MethodHolder() {
}
public MethodHolder(String property, Method getMethod, Method setMethod) {
this.property = property;
this.getMethod = getMethod;
this.setMethod = setMethod;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MethodHolder)) return false;
MethodHolder that = (MethodHolder) o;
return Objects.equals(property, that.property);
}
#Override
public int hashCode() {
return Objects.hash(property);
}
}
Pay attention of that the following code just make shallow copy.

Overriding hashCode when using isAssignableFrom on equals method

I need to find a way to cache Methods(java.lang.reflect.Method) in such a way that whenever I call a function with the class (Class) methodName(String) and arguments(T[]) the function will return the cached method if exists or find the method, add it to the cache and return it.
I want to use HashMap for caching so I can find the method in O(1) but the problem is that I need to useisAssignableFrom when I override the equals method:
public class A1 extends AParent {}
public class A2 extends AParent {}
public class AParent {}
public class Temp{
public void testFunc(AParent a){}
}
This is the class I use for the keys in the HashMap :
import java.util.Arrays;
class MethodAbs{
Class c;
String methodName;
Class<?>[] argsTypes;
public MethodAbs(Class c, String methodName, Class<?>[] argsTypes){
this.c = c;
this.methodName = methodName;
this.argsTypes = argsTypes;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodAbs methodAbs = (MethodAbs) o;
if (c != null ? !c.equals(methodAbs.c) : methodAbs.c != null) return false;
if (methodName != null ? !methodName.equals(methodAbs.methodName) : methodAbs.methodName != null)
return false;
return isArgsTypesEq(argsTypes, methodAbs.argsTypes);
}
//a method is equals to the one cached if the arguments types
// can be cast to the ones that are saved on the map,
// i.e the ones on the method declaration
private boolean isArgsTypesEq(Class<?>[] at1, Class<?>[] at2){
boolean res = at1.length == at2.length;
for(int i = 0; i<at1.length && res; i++){
if(!at1[i].isAssignableFrom(at2[i])) res = false;
}
return res;
}
//default implementation (not working properly!)
#Override
public int hashCode() {
int result = c != null ? c.hashCode() : 0;
result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
result = 31 * result + Arrays.hashCode(argsTypes);
return result;
}
}
The class I use for caching
class Run{
public Map<MethodAbs, Method> map = new HashMap<>();
public<T> Method myFunc(Class c, String methodName, T[] args){
MethodAbs ma = new MethodAbs(c, methodName, getTypes(args));
if(map.containsKey(ma)){
return map.get(ma);
}
else{
for(Method method: c.getMethods()){
MethodAbs currMethodAbs = new MethodAbs(c, method.getName(), method.getParameterTypes());
if(!map.containsKey(currMethodAbs))
map.put(currMethodAbs, method);
if(currMethodAbs.equals(ma)) break;
}
}
return map.get(ma);
}
private<T> Class<?>[] getTypes(T[] args) {
Class<?>[] types = new Class<?>[args.length];
for(int i = 0; i< args.length; i++){
types[i] = args[i].getClass();
}
return types;
}
}
And Main:
public static void main(String[] args){
Run r = new Run();
Object [] arr = new Object[1];
arr[0] = new A1();
r.myFunc(Temp.class, "testFunc", arr);
arr[0] = new A2();
r.myFunc(Temp.class, "testFunc", arr);
}
In the scenario above after calling r.myFunc for the first time, the map looks like this:
MethodAbs(Temp.class, "testFunc", [AParent.class])
on the second time map.containsKey will return false(because AParent.hashCode != A2.hashCode) but they are equals.
The hierarchy shown in the example will not necessarily look like that(for example A2 can be a grandchild of AParent)
I know I can use the Class and method name as keys and the value will be a list of methods which I'll need to iterate and compare with equals but I'm trying to find a better way...
Unfortunately, you're equals method is fundamentally broken, for at least two reasons.
It is not symmetric, see the following code snippet:
public static void main(String... args) {
MethodAbs methodValueOfObject = new MethodAbs(String.class, "valueOf", new Class<?>[] { Object.class });
MethodAbs methodValueOfCharArrays = new MethodAbs(String.class, "valueOf", new Class<?>[] { char[].class });
System.out.println(methodValueOfObject.equals(methodValueOfCharArrays)); // prints "true"
System.out.println(methodValueOfCharArrays.equals(methodValueOfObject)); // prints "false"
}
It equates methods which you probably don't mean to be considered equal. To see that imagine that your Temp class has two testFunc methods, public void testFunc(A1 a) and public void testFunc(A2 a). The corresponding MethodAbs objects shouldn't be equal, but according to your implementation, they indeed are.
I think that the best solution for you is to just get rid of the cache altogether. Just use
public Method getMethod(Class<?> c, String methodName, Class<?>... paramClasses) {
try {
return c.getDeclaredMethod(methodName, paramClasses);
} catch (NoSuchMethodException | SecurityException e) {
// Your exception handling goes here
return null;
}
}
Class objects are already cached by the classloader, so the performance loss is negligible.

How to convert method.getGenericReturnType() to a jvm type signature

How to convert an instance of java.lang.reflect.Type into a jvm generic type signature?
Type type = method.getGenericReturnType();
String signature = toTypeSig(type);
For instance this Type:
Map<String, Map<?, ? super Integer>>
Must become this String:
"Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;"
How do people using orb.objectweb.asm.ClassWriter solve this?
I've done it iterating over the Type information.
But I'd prefer something more robust.
(I've posted my own solution as an answer because it's a bit long)
I guess you want to get the jvm signature from a class file (or did you mean from a source file?)
From a class following could be a solution
public class GetSignature {
// the method for which you want to retrieve the signature
Map<String, Map<?, ? super Integer>> someMethod() {
return null;
}
public static void main(String[] args) throws Exception {
// make the private method in class Method accessible
Method methodGenericSignature = Method.class.getDeclaredMethod(
"getGenericSignature",
(Class<?>[]) null
);
methodGenericSignature.setAccessible(true);
// get the signature from the method
Method method = GetSignature.class.getDeclaredMethod("someMethod",
(Class<?>[]) null
);
String returnValue = (String) methodGenericSignature.invoke(method,
(Object[]) null
);
System.out.println("signature: " + returnValue);
}
}
output
signature: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;
edit A small snipped to demonstrate how to get the signature using asm.
public class GetSignatureDemo {
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("/tmp/GetSignature.class");
ClassReader classReader = new ClassReader(is);
classReader.accept(getClassVisitor(), 0);
}
private static ClassVisitor getClassVisitor() {
return new ClassVisitor(Opcodes.ASM4) {
#Override
public MethodVisitor visitMethod(int access, String name,
String descriptor, String signature, String[] exceptions) {
System.out.printf(
"method: %s descriptor: %s signature: %s%n",
name,
descriptor,
signature
);
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
};
}
}
sample output
method: someMethod descriptor: ()Ljava/util/Map; signature: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;
My final solution. I'm posting for reference. Since it might be useful when method.setAccessible(true) is not available.
This is more or less what I ended up using.
Because I needed to transform the generic return type into a class signature. And I had no use for the type variables.
It works well for methods without type parameters.
For methods with generic type parameters it tries to replace the parameters.
Processing the generic return type of this method: <P> List<P> someMethod()
results in "Ljava/util/List<Ljava/lang/Object;>;"
static String toGenericSignature(final Type type)
{
StringBuilder sb = new StringBuilder();
toGenericSignature(sb, type);
return sb.toString();
}
static void toGenericSignature(StringBuilder sb, final Type type)
{
if (type instanceof GenericArrayType)
{
sb.append("[");
toGenericSignature(sb, ((GenericArrayType) type).getGenericComponentType());
}
else if (type instanceof ParameterizedType)
{
ParameterizedType pt = (ParameterizedType) type;
sb.append('L');
sb.append(((Class) pt.getRawType()).getName().replace('.', '/'));
sb.append('<');
for (Type p : pt.getActualTypeArguments())
{
toGenericSignature(sb, p);
}
sb.append(">;");
}
else if (type instanceof Class)
{
Class clazz = (Class) type;
if (!clazz.isPrimitive() && !clazz.isArray())
{
sb.append('L');
sb.append(clazz.getName().replace('.', '/'));
sb.append(';');
}
else
{
sb.append(clazz.getName().replace('.', '/'));
}
}
else if (type instanceof WildcardType)
{
WildcardType wc = (WildcardType) type;
Type[] lowerBounds = wc.getLowerBounds();
Type[] upperBounds = wc.getUpperBounds();
boolean hasLower = lowerBounds != null && lowerBounds.length > 0;
boolean hasUpper = upperBounds != null && upperBounds.length > 0;
if (hasUpper && hasLower && Object.class.equals(lowerBounds[0]) && Object.class.equals(upperBounds[0]))
{
sb.append('*');
}
else if (hasLower)
{
sb.append("-");
for (Type b : lowerBounds)
{
toGenericSignature(sb, b);
}
}
else if (hasUpper)
{
if (upperBounds.length == 1 && Object.class.equals(upperBounds[0]))
{
sb.append("*");
}
else
{
sb.append("+");
for (Type b : upperBounds)
{
toGenericSignature(sb, b);
}
}
}
else
{
sb.append('*');
}
}
else if (type instanceof TypeVariable)
{
//sb.append("T");
//sb.append(((TypeVariable) type).getName());
//sb.append(";");
// work around: replaces the type variable with it's first bound.
toGenericSignature(sb, ((TypeVariable) type).getBounds()[0]);
}
else
{
throw new IllegalArgumentException("Invalid type: " + type);
}
}

Parse custom Objects with string input

I want to parse objects where I have an input value. The object has subclasses.
I don't want to create an instance when I want to parse the string.
But I want to override the method 'parse'.
class A {
public A(object param) {
//code
}
public A parse(String input) {
//code
}
}
class B extends A {
public B parse(String input) {
//code
}
}
And when I parse the objects I want do it with reflection:
A newObject = Class.forName(className).getMethod("parse", myParseText);
Is there a nice way to do that.
For maximum flexibility I'd move the parsing code out into a separate parser class. The following code is for example a simplified implementation of a parser class I am using in a project myself:
public final class ParseUtil {
public interface IStringParser<E> {
public E parse(String s) throws ParseException;
}
private ParseUtil() {}
private static Map<Class<?>, IStringParser<?>> STRING_PARSERS = new ConcurrentHashMap<>();
public static <E> void registerStringParser(Class<E> c, IStringParser<E> p){
STRING_PARSERS.put(c, p);
}
#SuppressWarnings("unchecked")
public static <E> IStringParser<E> getStringParser(Class<E> c){
return (IStringParser<E>) STRING_PARSERS.get(c);
}
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser == null) {
throw new ParseException(clazz);
}
return stringParser.parse(s);
}
}
One can then register IStringParser implementations for arbitrary classes to the ParserUtil like so:
ParseUtil.registerStringParser(File.class, new IStringParser<File>() {
#Override
public File parse(String s)throws ParseException {
return new File(s);
}
});
Or using a lambda: ParseUtil.registerStringParser(File.class, s -> new File(s));
It is the responsibility of the caller to decide how to parse a class and when the IStringParser is registered/unregistered.
By moving the parsing code away from the class itself, it is easy to change the implementation later simply by registering a different IStringParser or by changing the parse method of the ParseUtil class. For example in my own project I use the following parse method that provides some reasonable defaults for well known Java classes and uses Gson to parse Objects for which no other IStringParser is registered:
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser != null) {
return stringParser.parse(s);
}
if (Number.class.isAssignableFrom(clazz)) {
// simple number
try {
if (clazz == Integer.class) {
return clazz.cast(Integer.parseInt(s));
}
if (clazz == Long.class) {
return clazz.cast(Long.parseLong(s));
}
if (clazz == Double.class) {
return clazz.cast(Double.parseDouble(s));
}
if (clazz == Float.class) {
return clazz.cast(Float.parseFloat(s));
}
if (clazz == Short.class) {
return clazz.cast(Short.parseShort(s));
}
if (clazz == Byte.class) {
return clazz.cast(Byte.parseByte(s));
}
} catch (NumberFormatException e) {
throw new ParseException(clazz, e);
}
}
if (clazz == String.class) {
return clazz.cast(s);
}
if (clazz == Character.class) {
if (s.length() == 1) {
return clazz.cast(s.charAt(0));
} else{
throw new ParseException("Unable to parse Character \"" + s + "\"");
}
}
if (clazz == Boolean.class) {
switch (s) {
case "true":
return clazz.cast(Boolean.TRUE);
case "false":
return clazz.cast(Boolean.FALSE);
case "1":
return clazz.cast(Boolean.TRUE);
case "0":
return clazz.cast(Boolean.FALSE);
default:
throw new ParseException("Unable to parse boolean \"" + s + "\"");
}
}
if (clazz == Class.class) {
try {
return clazz.cast(Class.forName(s));
} catch (ClassNotFoundException e) {
throw new ParseException(clazz, e);
}
}
if (Enum.class.isAssignableFrom(clazz)) {
#SuppressWarnings({ "rawtypes" })
Class c = (Class)clazz;
#SuppressWarnings("unchecked")
Object o = Enum.valueOf(c, s);
return clazz.cast(o);
}
E result = null;
try {
// if all else fails use Gson to parse the class
result = getGSON().fromJson(s, clazz);
} catch (JsonSyntaxException e) {
throw new ParseException(clazz. e);
}
if (result == null) {
throw new ParseException(clazz);
}
return result;
}

Assigning a default value to a generic

I'm calling a method by reflection, and its return type is generic. I don't want the return value to be null, so I in that case I want to assign a default value of that generic type.
That is, after calling the method by reflection, I'd like to execute something like this:
T resultNotNull = (T) reflectionMethodThatCanReturnNull.invoke(anObject);
// If it's null, let's assign something assignable.
if (resultNotNull == null) {
if (resultNotNull.getClass().isAssignableFrom(Long.class)) {
resultNotNull = (T) Long.valueOf(-1);
} else if (resultNotNull.getClass().isAssignableFrom(String.class)) {
resultNotNull = (T) "NOTHING";
}
}
But, of course, if 'resultNotNull == null', we can't call 'resultNotNull.getClass()' without getting an exception.
Any ideas on how to assign a default value depending on the generic type?
The whole code would be something like this:
public class ReflectionTest {
public class Class1ok {
public Long method () { return Long.valueOf(1); }
}
public class Class1null {
public Long method () { return null; } // It returns null
}
public class Class2ok {
public String method () { return "SOMETHING"; }
}
public static void main(String[] args) {
ReflectionTest test = new ReflectionTest();
Long notNullValue1
= test.methodReturnNotNull(Class1ok.class); // 1
Long notNullValue2
= test.methodReturnNotNull(Class1null.class); // -1, not null
String notNullValue3
= test.methodReturnNotNull(Class2ok.class); // "SOMETHING"
}
public <T> T methodReturnNotNull(Class theClass) {
T resultNotNull = null;
try {
Object theObject = theClass.newInstance();
Method methodThatCanReturnNull = theClass.getMethod("method");
resultNotNull = (T) methodThatCanReturnNull.invoke(theObject);
} catch (Exception e) {
// Meh.
}
// If it's null, assign something assignable.
if (resultNotNull == null) {
if (resultNotNull.getClass().isAssignableFrom(Long.class)) {
resultNotNull = (T) Long.valueOf(-1);
} else if (resultNotNull.getClass().isAssignableFrom(String.class)) {
resultNotNull = (T) "NOTHING";
}
}
return resultNotNull;
}
}
You can use getReturnType();
private static final Map<Class, Object> replacementForNull = new HashMap<>();
static {
replacementForNull.put(String.class, "NOTHING");
replacementForNull.put(Long.class, 1L);
}
Method reflectionMethodThatCanReturnNull = ...
T resultNotNull = (T) reflectionMethodThatCanReturnNull.invoke(anObject);
// If it's null, let's assign something assignable.
if (resultNotNull == null) {
Class returnType = reflectionMethodThatCanReturnNull.getReturnType();
resultNotNull = replacementForNull.get(returnType);
}
Why your T could not implements some interface or extend some base class? Then you could call some static default method from T which return default value for given class T. Pros? It will encapsulate your switch code just to right class.
Of course, without any interfaces, all your possible T class could just have some method like getDefaultValue().

Categories

Resources