java - Reflectively checking whether a object is a valid generic argument to a method -


how check using reflection whether given object valid parameter of method (where parameter , object generic types)?

to bit of background, i'm trying achieve:

while playing reflective method calls thought nice call methods have parameter of specific type. works raw types can call isassignablefrom(class<?> c) on class objects. however, when start throwing in generics mix isn't easy because generics weren't part of reflection's original design , because of type erasure.

the problem larger boils down following:

ideal solution

ideally code

import java.lang.reflect.*; import java.util.*;   public class reflectionabuse {      public static void callmemaybe(list<integer> number) {         system.out.println("you called me!");     }      public static void callmeagain(list<? extends number> number) {         system.out.println("you called me again!");     }      public static void callmenot(list<double> number) {         system.out.println("what's wrong you?");     }      public static <t> void reflectivecall(list<t> number){         for(method method : reflectionabuse.class.getdeclaredmethods()) {             if(method.getname().startswith("call")) {                 if(canbeparameterof(method, number)) {                     try {                         method.invoke(null, number);                     } catch (exception e) {                         e.printstacktrace();                     }                 }             }         }     }      public static <t> boolean canbeparameterof(method method, list<t> number) {         // fixme checks missing         return true;     }      public static void main(string[] args) {         reflectivecall(new arraylist<integer>());     }  } 

would print

you called me! called me again! 

this should possible regardless of how t (i.e. generic type, such list<list<integer>>).

obviously can't work because type t erased , unknown @ runtime.

attempt 1

the first thing working this:

import java.lang.reflect.*; import java.util.*;   public class reflectionabuse {              public static void callmemaybe(arraylist<integer> number) {                 system.out.println("you called me!");             }              public static void callmeagain(arraylist<? extends number> number) {                 system.out.println("you called me again!");             }              public static void callmenot(arraylist<double> number) {                 system.out.println("what's wrong you?");             }      public static <t> void reflectivecall(list<t> number){         for(method method : reflectionabuse.class.getdeclaredmethods()) {             if(method.getname().startswith("call")) {                 if(canbeparameterof(method, number)) {                     try {                         method.invoke(null, number);                     } catch (exception e) {                         e.printstacktrace();                     }                 }             }         }     }      public static <t> boolean canbeparameterof(method method, list<t> number) {         return method.getgenericparametertypes()[0].equals(number.getclass().getgenericsuperclass());     }      public static void main(string[] args) {         reflectivecall(new arraylist<integer>(){});     }  } 

which prints

you called me! 

however, has additional caveats:

  • it works direct instances , inheritance hierarchy not taken account type interface not provide needed methods. cast here , there find out (see second attempt)
  • the argument of reflectivecall needs subclass of wanted parameter type (notice {} in new arraylist<integer>(){} create anonymous inner class). that's less ideal: creates unnecessary class objects , error prone. way think of getting around type erasure.

attempt 2

thinking missing type in ideal solution due erasure, 1 pass type argument gets quite close ideal:

import java.lang.reflect.*; import java.util.*;   public class reflectionabuse {      public static void callmemaybe(list<integer> number) {         system.out.println("you called me!");     }      public static void callmeagain(list<? extends number> number) {         system.out.println("you called me again!");     }      public static void callmenot(list<double> number) {         system.out.println("what's wrong you?");     }      public static <t> void reflectivecall(list<t> number, class<t> clazz){         for(method method : reflectionabuse.class.getdeclaredmethods()) {             if(method.getname().startswith("call")) {                 type n = number.getclass().getgenericsuperclass();                 if(canbeparameterof(method, clazz)) {                     try {                         method.invoke(null, number);                     } catch (exception e) {                         e.printstacktrace();                     }                 }             }         }     }      public static <t> boolean canbeparameterof(method method, class<t> clazz) {         type type = ((parameterizedtype)method.getgenericparametertypes()[0]).getactualtypearguments()[0];         if (type instanceof wildcardtype) {             return ((class<?>)(((wildcardtype) type).getupperbounds()[0])).isassignablefrom(clazz);         }         return ((class<?>)type).isassignablefrom(clazz);     }      public static void main(string[] args) {         reflectivecall(new arraylist<integer>(), integer.class);     }  } 

which prints correct solution. not without negative sides:

  • the user of reflectivecall needs pass type parameter unnecessary , tedious. @ least correct call checked @ compile time.
  • inheritance between type parameters not taken account , there many cases left need implemented in canbeparameterof (such typed parameters).
  • and biggest problem: type parameter can't generic in itself, list of lists of integers can't used argument.

the question

is there differently close possible goal? stuck either using anonymous subclasses or passing type parameter? moment, i'll settle giving parameter gives compile time safety.

is there need aware of when recursively checking parameter types?

is there possibility allow generics type parameter in solution 2?

actually, learning purposes roll own solution instead of using library although wouldn't mind taking @ inner workings of some.


just make things clear, i'm example aware of following try keep examples clean:

  • potentially solved without reflection (while redesigning of requirements , using example interfaces , inner classes). learning purposes. problem i'd solve quite bit larger boils down to.
  • instead of using naming pattern use annotations. that, i'd keep examples self-contained possible.
  • the array returned getgenericparametertypes() empty, let's assume methods have argument , checked beforehand.
  • there non-static methods fail when null called. assume there aren't.
  • the catch conditions more specific.
  • some casts need more safe.

am stuck either using anonymous subclasses or passing type parameter?

more or less, better subclass using super type token pattern instead of subclassing value type. after all, value type might not allow subclasses. using type token pattern allows accept generic types, whereas accepting class type parameter allows raw types (which why had take component type of list, not type itself).

public static <t> void reflectivecall(typetoken<t> type, t value) 

guava has great support creating , using typetokens. far simplest way create 1 creating anonymous subclass (note: if it's going reused, make constant):

reflectivecall(new typetoken<list<integer>>() {}, new arraylist<integer>()); 

once have this, canbeparameterof becomes easier implement.

public static boolean canbeparameterof(method method, typetoken<?> giventype) {     type[] argtypes = method.getgenericparametertypes();      return argtypes.length != 0 &&          typetoken.of(argtypes[0]).isassignablefrom(giventype); } 

Comments

Popular posts from this blog

java - Jmockit String final length method mocking Issue -

asp.net - Razor Page Hosted on IIS 6 Fails Every Morning -

c++ - wxwidget compiling on windows command prompt -