0

I would like to get the class of Foo< T >.class (exactly Foo < T>, neither T.class nor Foo.class)

public class A extends B<C<D>>{
   public A() {
     super(C<D>.class); // not work
   }
}

On StackOverflow has a instruction for obtaining generic class by injecting into constructor but it's not my case because C< D>.class (e.g List< String>.class) is syntax error. At here it seems relate to syntax more than code structure.

To show more detail, higher level view, the original code is the following, its HATEOAS module in Spring framework:

public class CustomerGroupResourceAssembler extends ResourceAssemblerSupport<CustomerGroup, CustomerGroupResource>{
   public CustomerGroupResourceAssembler() {
      super(CustomerGroupController.class, CustomerGroupResource.class);
   }
}
public class CustomerGroupResource extends ResourceSupport {
   private CustomerGroup data;
}

But now I want to parameterize the CustomerGroupResource to

public class Resource<T> extends ResourceSupport {
   private T data;
}

and then

    public class CustomerGroupResourceAssembler extends ResourceAssemblerSupport<CustomerGroup, Resource<CustomerGroup>>{
      public CustomerGroupResourceAssembler() {
         super(CustomerGroupController.class, Resource<CustomerGroup>.class); // not work here, even Resource.class
      }
    }
user123
  • 457
  • 3
  • 18
  • 3
    There is no such thing as C.class. What precisely are you trying to achieve? – JB Nizet Jul 29 '18 at 07:52
  • I would like to get class of C, but it's a type parameter one. So when a method needs class of C (maybe include that type parameter because when I pass C.class it was not accepted) . So I dont know how to get that. – user123 Jul 29 '18 at 07:59
  • That I understand. But at a much higher level, what are you trying to achieve. What are you using this class instance for? Why is it needed? To do what? – JB Nizet Jul 29 '18 at 08:00
  • Thank you JB Nizet, I edited my question for more detail. Please read again! – user123 Jul 29 '18 at 08:23
  • And you still haven't explained why ResourceAssemblerSupport needs class instances and what it does with them. – JB Nizet Jul 29 '18 at 08:25
  • You can get the information you want without having to provide it again via reflection, see my answer. – Peter Lawrey Jul 29 '18 at 08:28
  • @JB Nizet It's core Spring framework, exactly HATEOAS library, the CustomerGroupResourceAssembler structure with the constructor calling super method is the same and madatory for everyone using the library. – user123 Jul 29 '18 at 08:29

2 Answers2

0

Unfortunately what you are trying to do is impossible due to type erasure.

Information about generic types is only avalilable at compile time and not at run time. This is one of biggest limitations of using generics in Java. The reason why it was done like this is to preserve backwards compatibility.

See Java generics type erasure: when and what happens?

jurez
  • 3,422
  • 1
  • 7
  • 15
0

Due to type erasure, the generic only applies at compile time and doesn't mean anything at runtime. What you can do is

public class A extends B<C<D>>{
   public A() {
     super((Class<C<D>>) C.class);
   }
}

However, you won't be able to determine the type of D at runtime. You can use reflection to get the super type however.

public class Main {
    public static abstract class B<X> {
        protected B() {
            Type type = getClass().getGenericSuperclass();
            System.out.println(type);
        }
    }

    public static class A extends B<Supplier<String>> {
        public A() {
        }
    }

    public static void main(String[] args) {
        new A();
    }
}

prints

Main.Main$B<java.util.function.Supplier<java.lang.String>>

EDIT For your specific example you can do.

import java.lang.reflect.Type;

public interface Main {

    class ResourceSupport {

    }

    class CustomerGroup {

    }

    public class Resource<T> extends ResourceSupport {
        private T data;
    }

    abstract class ResourceAssemblerSupport<C, R> {
        protected ResourceAssemblerSupport() {
            Type type = getClass().getGenericSuperclass();
            System.out.println(type);

        ParameterizedType pt = (ParameterizedType) type;
        Type[] actualTypeArguments = pt.getActualTypeArguments();
        Class first = (Class) actualTypeArguments[0];
        ParameterizedType second = (ParameterizedType) actualTypeArguments[1];

        System.out.println(pt.getRawType() + " <" + first + ", " + second + ">");
        }
    }

    public class CustomerGroupResourceAssembler extends ResourceAssemblerSupport<CustomerGroup, Resource<CustomerGroup>>{
        public CustomerGroupResourceAssembler() {
        }
    }

    public static void main(String[] args) {
        new CustomerGroupResourceAssembler();
    }
}

prints

Main.Main$ResourceAssemblerSupport<Main$CustomerGroup, Main.Main$Resource<Main$CustomerGroup>>
class Main$ResourceAssemblerSupport <class Main$CustomerGroup, Main.Main$Resource<Main$CustomerGroup>>

A generic way to do what you want is to use a helper function, however I think this isn't needed in your case.

public static void main(String[] args) {
    System.out.println(new ClassType<List<String>>() {}.getType());
}

interface ClassType<T> {
    default Type getType() {
        ParameterizedType type = (ParameterizedType) getClass().getGenericInterfaces()[0];
        return type.getActualTypeArguments()[0];
    }
}

prints

java.util.List<java.lang.String>
Peter Lawrey
  • 498,481
  • 72
  • 700
  • 1,075
  • @downvoter any idea why? – Peter Lawrey Jul 29 '18 at 08:21
  • 1
    Upvote for your enthusiasm (downvote not me). I'll give it a try. Thanks you so much. – user123 Jul 29 '18 at 08:34
  • 1
    @ngocchien while you can use my last example, I think it would be better not to have to specify it in the first place. – Peter Lawrey Jul 29 '18 at 08:45
  • 1
    I tried your last but super() in my code did not permit to call an instance method (it should be the first call in constructor, anyway). Maybe I should copy paste more to ease the hassle :), thank you again for your help. – user123 Jul 29 '18 at 08:50
  • @ngocchien you can assign it to a static field so it doesn't get an instance context. – Peter Lawrey Jul 29 '18 at 15:23