Is there any hack to find out the actual concrete class of the shared instance instance at runtime?

I would like to find a hack to print the actual shared instance of another var instance at runtime, without :

  • Changing my required method signature (adding helper parameter Class<T> , obvious way)
  • Having instanceof all possible subtypes in a hard-coded way

     MyInterface<? extends Number> myInterface = whateverReturnsWildcardDoubleInterface(); Class<?> type = inferInstanceType(myInterface); assert type == Double.class; /** This is the method that represents the code I am looking for with the conrete signature**/ public <T extends Number> Class<T> inferInstanceType(MyInterface<T> myInterface){ return T.class; //Concrete T (can or cannot be the very Number) } 

Ideally, it should return Double when T is a specific subtype of Integer, Double .. and Number when T is Number

I checked the reflection, a few "TypeResolver" / "GenericResolver" libs (like in Spring or others on Github), but I can't plan a way to crack it.

EDIT: I came to the conclusion that the only possible way to do this would be a kind of very complex reflection along the stack path up to the acutal line that passes the type in the instance itself

EDIT2: I know this is stupid ... but I solved it by simply adding the T getT() method to my interface so that I can return myInterface.getT().getClass()

+5
source share
2 answers

Disclaimer: This solution is provided as a hack , taking into account my understanding of your installation, that is, one universal interface with one type of parameter, several classes that are not on their own, directly implementing this one interface and not using any other common interfaces, directly or indirectly.

Assuming all of the above is true, there is a fairly simple way to crack the solution: calling getClass().getGenericInterfaces() returns a Type object that provides the actual type with which your common interface was created.

 interface MyInterface<T extends Number> { T getVal(); } class DoubleImpl implements MyInterface<Double> { public Double getVal() {return 42.42; } } ... public static void main (String[] args) throws java.lang.Exception { MyInterface<? extends Number> x = new DoubleImpl(); Type[] ifs = x.getClass().getGenericInterfaces(); System.out.println(ifs.length); for (Type c : ifs) { System.out.println(c); Type[] tps = ((ParameterizedType)c).getActualTypeArguments(); for (Object tp : tps) { System.out.println("===="+tp); // <<== This produces class java.lang.Double } } } 

Demo version

+4
source

As assylias pointed out, erasing Java will make this information inaccessible at runtime - and therefore need to be hacked.

Assuming myInterface has a getter for T , as in, MyInterface.getValue():T (or the hack should have added it), you could do something like this (ignoring the possibility of returning getValue() null ):

 public <T extends Number> Class<T> inferInstanceType(MyInterface<T> myInterface){ return myInterface.getValue().getClass() } 

Below is the full implementation

 public class Q34271256 { public static interface MyInterface<T> { T getValue(); } public static class MyDoubleClass implements MyInterface<Double> { private final Double value; public MyDoubleClass(Double value) { this.value = value; } @Override public Double getValue() { return value; } } public static class MyIntegerClass implements MyInterface<Integer> { private final Integer value; public MyIntegerClass(Integer value) { this.value = value; } @Override public Integer getValue() { return value; } } @SuppressWarnings("unchecked") public static <T extends Number> Class<T> inferInstanceType(MyInterface<T> myInterface){ Number value = myInterface.getValue(); if (value == null) return null; return (Class<T>)value.getClass(); } public static void main(String...args) { List<MyInterface<? extends Number>> list = Arrays.asList( new MyDoubleClass(1.1), new MyIntegerClass(5) ); for (MyInterface<? extends Number> myInterface : list) { Class<?> type = inferInstanceType(myInterface); System.out.printf("%s inferred type is %s\n", myInterface.getClass().getName(), type.getName()); } } } 

And the result should look something like this:

 MyDoubleClass inferred type is java.lang.Double MyIntegerClass inferred type is java.lang.Integer 
-4
source

Source: https://habr.com/ru/post/1238148/


All Articles