4

I'd like to find a hack to infer the actual generic instance of another instance's var in runtime, without:

  • Changing my needed method signature (adding the helper parameter Class<T>, the obvious way)
  • Having to instanceof all possible subtypes in a hardcoded 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 particular subtype Integer,Double.. and Number when T is Number

I checked reflection, several "TypeResolver"/"GenericResolver" libs (as the one in Spring or others in Github), but I cannot fin a way to hack it.

EDIT: I reached the conclusion that he only feasible way to do that would be some kind of very complex reflection through the stack trace up to the acutal line that passes the type in the very instantiation

EDIT2: I know it's stupid... but I solved it by simply adding a T getT() method to my interface, so I could return myInterface.getT().getClass()

Whimusical
  • 6,401
  • 11
  • 62
  • 105
  • 4
    Not possible due to erasure... – assylias Dec 14 '15 at 15:56
  • Yes, because it's _impossible_. Learn about type erasure. – SLaks Dec 14 '15 at 15:57
  • 1
    I don't want to disregard you, but I've seen these impossibles in a lot of questions and eventually somebody finds a smart tricky way to approach the problem, that is why I ask about a hack. I know you can get the types using reflection if the instance class is typed through an interface (like in MyInterfaceDouble extends MyInterface, for instance – Whimusical Dec 14 '15 at 16:02
  • 1
    Yes. And in this case, you want to find out what `?` is through reflection and this is not possible. What is your use-case? – Tunaki Dec 14 '15 at 16:04
  • It looks like you might be testing or debugging as you are looking for a "hack." If you know the concrete type of `MyInterface`, and it has a field of type `T`, you can get the `Class>` for that field using reflection and get its type just as if it were not generic. – David V Dec 14 '15 at 16:05
  • The use case is too complex and abstract, and I know it is not a good pattern, so I'll avoid it, just experimenting with the posisbilities of the compiler. Perhaps a non-reflection way? – Whimusical Dec 14 '15 at 16:05
  • 3
    ... in certain very narrow cases. – Brian Goetz Dec 14 '15 at 18:32
  • I EDITED the question at the end to wonder about the only possible approach that could work in my case – Whimusical Dec 14 '15 at 19:26
  • 1
    First, there is no guaranty that the creator of an object is on the stack at all, second, Reflection doesn’t provide you the ability to inspect the code at all, not to speak of such deep analysis, third, type erasure affects the executable byte code as well, don’t expect to find more information there than you already have. – Holger Dec 15 '15 at 10:21

2 Answers2

3

Disclaimer: This solution is provided as a hack tailored to my understanding of your setup, i.e. one generic interface with a single type parameter, multiple classes, which are not themselves generic, directly implementing this one interface alone, and implementing no other generic interfaces, directly or indirectly.

Assuming that all of the above is true, there is a relatively straightforward way of hacking a solution: calling getClass().getGenericInterfaces() returns a Type object that provides the actual type with which your generic interface has been instantiated.

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.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 6
    @dasblinkenlight Yes, shame on you for saying "here's the hack" without a detailed "here's the special cases in which it works." What's common to all these hacks is: there needs to be a 1:1 correspondence between the classfile and the specific type arguments. So this works for generic specializations like DoubleImpl (rare), and also for inner class instances (slightly less rare). But this fails for the other (100-epsilon)% of the cases. – Brian Goetz Dec 14 '15 at 17:02
  • 3
    What makes you think that his case matches the special case *you* have chosen? And…do you actually know what kind of special case you have chosen and why it works (in contrast to other scenarios)? – Holger Dec 14 '15 at 17:19
  • 2
    @Holger Why do you insist that I have chosen a special case? Haven't OP described *his* case well enough? I mean, he's got one interface with one generic parameter constrained as `` and multiple classes implementing it; he wants to pull the actual `T` from an object instance implementing that interface, without having static access to the actual type `T`. My hack does exactly that. My biggest assumption is that OP' concrete classes do not implement other generic interfaces. – Sergey Kalinichenko Dec 14 '15 at 17:29
  • 1
    @Holger Perhaps it would be easier if you forked [this demo](http://ideone.com/jpvuHV) and modify it in a way that it returns a wrong result? – Sergey Kalinichenko Dec 14 '15 at 17:31
  • 1
    http://ideone.com/SB6Te9 just two different examples, both parametrized with ``, neither of them processed correctly. Note that these are the common cases, e.g. like with `List l=new ArrayList<>();` You don’t create a subclass of `ArrayList` to aid such a hack… If you wanted to do that, you had to create a subclass for each possible parametrization. – Holger Dec 14 '15 at 17:42
  • 2
    @dasblinkenlight The value of StackOverflow (in the words of Joel Spolsky), is *not* primarily about answering the OPs question, but about creating a persistent artifact that many others users will land on and learn from. Saying "there's a simple workaround" and failing to characterize the limitations of that workaround is confusing thousands of future visitors. And for no benefit at all! You probably knew the limitations -- what's wrong with just saying them straight out? The result is, your answer is fundamentally misleading. And for no good reason. – Brian Goetz Dec 14 '15 at 17:44
  • 1
    @BrianGoetz Personally, I strongly dislike "don't try" answers, for they discourage the reader from trying. I find this particular solution useful enough to post as an answer, because the dismissive "not possible due to type erasure" is simply not true. – Sergey Kalinichenko Dec 14 '15 at 17:51
  • 1
    @Holger Thanks for a useful example! This one fork of the code is worth a dozen of comments with explanations of what's wrong. – Sergey Kalinichenko Dec 14 '15 at 18:00
  • 4
    The difference between “not possible” and “possible in a single narrow case” is thin, but you can let the OP judge, if you put everything on the table. Btw., it’s even possible to [have a class that fits into the description but fails](http://ideone.com/8naAxH) because it’s not Generic but has a type parameter in its surrounding context. – Holger Dec 14 '15 at 18:03
  • Thanks for your effort. This is not resolving the problem in the fashion of my sample with a method to fullfill, but imposing me to declare every typeable interface as a concrete subinterface (then those libs I tested would work as well). I already knew that trick as I showed in some comment, but as I said, I'd like to respect the signature of my helper method, Sorry if I was not clear enough. Look the new EDIT, perhaps it helps to show a way. – Whimusical Dec 14 '15 at 19:22
  • I mean a way to iterate over the instance passings through the stack using reflection until we can find someway the original line that instantiates the instance and then we can infer the diamond generic used for so: whatever. I know it's kinda ugly, but its the only thing it could work I think as long as reflection API allows it – Whimusical Dec 15 '15 at 00:18
  • 1
    @Whimusical It may not always be possible to catch the instantiation, because it may be off the stack by then. I think I understand your solution, but I am not sure that I fully understand the reason making you jump through all these hoops, e.g. avoiding the idiomatic solution of passing the class? – Sergey Kalinichenko Dec 15 '15 at 02:30
  • 1
    I think I found a very obvious solution.... :p, which stil complies with my 2 requirements... simply adding a `T getT()` method in my interface,, so I can do return `myInterface.getT().getClass()`... I know its stupid... I did not realize – Whimusical Dec 15 '15 at 23:52
-3

As assylias pointed out, Java's erasure will make that information unavailable at runtime - and thus a need for a hack.

On the assumption that myInterface has a getter for T, as in, MyInterface.getValue():T (or the hack would be to add it) you could do something like this (ignoring the possibility that getValue() could return 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 output should look something like this:

MyDoubleClass inferred type is java.lang.Double
MyIntegerClass inferred type is java.lang.Integer
SireInsectus
  • 137
  • 1
  • 3
  • 2
    The value being of type `Double` or `Integer` doesn’t prove that `T` is `Double` or `Integer`. It’s still possible that `T` is `Number`, `Serializable` or `Object`, to name some examples. – Holger Dec 14 '15 at 17:02