1

we can declare a method in Java as

public static <T extends ClassTakesA<A>, A extends ClassTakesBC<B, C>, B extends ClassB, C extends ClassC> T MyFunc(Class<T> clazz) {
    T obj = null;
    try {
        obj = clazz.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
    return obj;
}

So that at compile time or in IDE we already know the parameter to MyFunc() must be bounded by T. This works great because

MyFunc(Object.class) // good, gives compile time error as expected
MyFunc(ClassTakesA.class) // good, although need @SuppressWarnings("unchecked")

Now if I need to declare the parameter to MyFunc() first, how should I declare it so that I can get compile time (or in IDE) error if the parameter is not bounded by T? e.g.

public static void main(String[] args) {
    Class myClass = Object.class;
    MyFunc(myClass);  // no error at compile time, but failed in runtime
}

Tried this but its wrong syntax

    Class<T extends ClassTakesA<A>, A extends ClassTakesBC<B, C>, B extends ClassB, C extends ClassC> myClass;

And by the way, how do you get rid of the @SuppressWarnings("unchecked")?


A suggestion was to use

Class<ClassTakesA> myClass = ClassTakesA.class;

But I do not allow passing

ClassTakesA<ClassZ> classTakesANot = new ClassTakesA<ClassZ>();
Class<ClassTakesA> myClass = classTakesANot.getClass();
MyFunc(myClass);

as ClassZ does not extend ClassTakesBC<B, C>

user1589188
  • 5,316
  • 17
  • 67
  • 130
  • Just for the record: method names start lowercase in java. Always. – GhostCat Oct 12 '16 at 06:56
  • 1
    A sidenote: prefer `return class.newInstance();` in `try`-blocks, and `return null` (if it's really worthy) or `throw new RuntimeException(ex)` in the `catch`-block. For good. – Lyubomyr Shaydariv Oct 12 '16 at 06:58
  • 1
    @GhostCat it's a naming convention not a hard rule. – Mikhail Kuchma Oct 12 '16 at 07:04
  • If you change `Class myClass` to `Class> myClass` you will get the error – poozmak Oct 12 '16 at 07:05
  • `Class myClass = Object.class; MyFunc(myClass);` will give you a compile-time error. – Spotted Oct 12 '16 at 07:06
  • @Spotted [really](http://ideone.com/38G8Uk)? – Andy Turner Oct 12 '16 at 07:07
  • @Spotted I understand what you are trying to do. But the purpose is not to get compile time error so that the program won't run at all. I need the correct way to declare myClass so that wrong assignment can be captured at compile time. – user1589188 Oct 12 '16 at 07:20
  • 3
    See: [What is a raw type and why shouldn't we use it](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) for explanation of why `Class myClass = Object.class;` is a bad idea. – Andy Turner Oct 12 '16 at 07:21

3 Answers3

2

As for method invocation you should declare it as

    Class<ClassTakesA> myClass = ClassTakesA.class;
    MyFunc(myClass);

This way you provides compiler some information about myClass so it can check during compilation.

As for warning you cannot rid of them because generics are not exist in runtime, where class objects live.

-- Add guava TypeToken --

Also you can try to use Guava's TypeToken to get rid of warning, but it will introduce another warning in obj = (T)...

public static <T extends ClassTakesA<A>, A extends ClassTakesBC<B, C>, B extends ClassB, C extends ClassC> T foo(TypeToken<T> clazz) {
    T obj = null;
    try {
        obj = (T) clazz.getRawType().newInstance();
        System.out.println("1 " + obj);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
    return obj;
}

private <T extends ClassTakesA<A>, A extends ClassTakesBC<B, C>, B extends ClassB, C extends ClassC> void test() {
    TypeToken<T> typeToken = new TypeToken<T>(ClassTakesA.class) { };
    foo(typeToken);
}
Mikhail Kuchma
  • 583
  • 2
  • 9
1

If you want to use T, A, B , C you will need to add them to the class declaration, not only the method declaration:

  public class MyClass <T extends ClassTakesA<A>, A extends ClassTakesBC<B, C>, B extends ClassB, C extends ClassC>  {

public T MyFunc(Class<T> clazz) {
  T obj = null;
  try {
    obj = clazz.newInstance();
  } catch (InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
  }
  return obj;
}

}

And then you can create a class which extends ClassTakesA, i.e:

public class ExtendsClassTakesA<A> extends ClassTakesA<A>{}

And call your function with:

Class<ExtendsClassTakesA> myClass = ExtendsClassTakesA.class;
MyFunc(myClass);
poozmak
  • 414
  • 2
  • 11
1

Finally got it working. It turns out to be a simple replacement of the T, A, B, C to ? e.g.

Class<? extends ClassTakesA<? extends ClassTakesBC<? extends ClassB, ? extends ClassC>>> myClass;

user1589188
  • 5,316
  • 17
  • 67
  • 130