6

I have problem to use generics in one of my class. These are my classes:

public interface InterE<PK> {}

public interface Inter2<O extends ClassO, P extends ClassP> {}

public class ClassU<O extends ClassO, P extends ClassP> implements InterE<Long> {}

public class ClassP {}

public class ClassO {}

public class Class1<PK, T extends InterE<PK>> {
    public Class1 (Class<T> clazz) {}
}

public class Class2<O extends ClassO, P extends ClassP> extends Class1<Long, ClassU<O, P>> implements Inter2<O, P> {
    public Class2 () {
        //old constructor
        //super(ClassU.class);
        //new - here is problem
        super(ClassU<O, P>.class);
    }
}

If ClassU does not use generics, then old constructor in Class2 works fine, but I need to use generics in ClassU, so I have problem to call super constructor in Class2.

Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145
podmak
  • 151
  • 5
  • 1
    What exactly happens when you just use the constructor in the "old" way? – Ray Jun 16 '15 at 06:21
  • http://stackoverflow.com/questions/3437897/how-to-get-class-instance-of-generics-type-t – serg.nechaev Jun 16 '15 at 06:21
  • @serg.nechaev not quite, he's not looking for `O.class` or `P.class`. – Dan Getz Jun 16 '15 at 06:22
  • 1
    old way has compilation error - The constructor Class1>(Class) is undefined – podmak Jun 16 '15 at 06:25
  • [This other question](http://stackoverflow.com/q/2390662/3004881) explains why you're having a problem, but I'm not seeing any good solutions there... – Dan Getz Jun 16 '15 at 06:28
  • 3
    There is probably no safe way around it, but you can use an unsafe double cast: `super((Class>)(Class)ClassU.class)`. See here https://stackoverflow.com/questions/7502243/java-casting-class-operator-used-on-a-generic-type-e-g-list-to-classlist – dhke Jun 16 '15 at 06:31
  • @dhke No I can't. there is "Cannot cast from Class to ClassU" error – podmak Jun 16 '15 at 06:41
  • Using `(Class>)` instead of `(Class)` for the first cast worked for me. – Dan Getz Jun 16 '15 at 06:51
  • @podmak: Can you try again with the corrected version? There was an error in my initial version. The correction gives an unchecked cast warning, but compiles and runs just fine with Java 8. – dhke Jun 16 '15 at 06:54
  • it looks like super((Class) ClassU.class); is without compilation error too. I ran some tests. Thanks a lot guys – podmak Jun 16 '15 at 06:55

5 Answers5

2

Short story: change Class1 constructor parameter type - from Class<T> to Class<?>.

Why?

Class1 only needs T to extend InterE. Class2 declares ClassU to be this T argument. Since ClassU implements InterE, everything is OK for Class1.

Do you really need this <T> information in Class1 constructor? I think no:
1) T is already present in class declaration: Class1<PK, T extends InterE<PK>>, you can use it inside the class to generify code and make it compile-time safe:

 public class Class1<PK, T extends InterE<PK>> {
     Map<PK, T> map = new HashMap<>(); // can still use T, compile-time safe
 }

2) What can Class<T> do, that Class<?> can not do? - it can create newInstance() safe and do some more stuff, but you most likely won't use this possibility. Use still can operate with T, for example call T instance = (T) class.newInstance();, so you will not have any insuperable limitations.

AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • Yes, you are right. In Class1 is nothing special. It is used mostly for checking to avoid bad input to constructor and exception during runtime – podmak Jun 16 '15 at 09:23
  • @podmak if the answer solves your problem, you should mark it as accepted, so other users could use it. otherwise, write why it doesn't solve the problem or edit the original question. – AdamSkywalker Jun 16 '15 at 12:28
  • You should not use raw types, see [my comment on podmaks answer](http://stackoverflow.com/questions/30860063/migrating-java-classes-to-use-generics/30863772#comment49790093_30865422). – siegi Jun 16 '15 at 17:39
  • @siegi ok, i changed it – AdamSkywalker Jun 16 '15 at 17:51
  • @AdamSkywalker is absolutely right that T is already available on Class1 to add type-safety and Class2 extends Class1, so requiring it to be passed in is redundant – Danikov Jun 17 '15 at 09:26
1

It is not clear from your example why you need to preserve the parameter; once you understand type erasure and how generics are a compile-time check, not a runtime check, not all generics will necessarily make sense.

If you do want to preserve and operate upon parametrized types at runtime, Google's Guava library provides TypeToken, e.g.

public class Class1<PK, T extends InterE<PK>> {
    public Class1 (TypeToken<T> token) {}
}

public class Class2<O extends ClassO, P extends ClassP> extends Class1<Long, ClassU<O, P>> implements Inter2<O, P> {
    public Class2 () {
        super(new TypeToken<ClassU<O, P>>() {});
    }
}
Danikov
  • 735
  • 3
  • 10
1

dkhe answer was right: super((Class<ClassU<O, P>>) (Class) ClassU.class)

and also worked for me: super((Class) ClassU.class)

podmak
  • 151
  • 5
  • You should not use `Class` without type parameters (called a _raw type_). From the [Java Language Specification, §4.8](https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.8): _The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types […] is strongly discouraged. It is possible that future versions […] will disallow the use of raw types._ Better use `Class>` as [Damsen shows in his answer](http://stackoverflow.com/a/30863772/1347968). – siegi Jun 16 '15 at 17:34
  • casting to `Class>` results as compilation error - undifined constructor – podmak Jun 17 '15 at 05:44
  • All the casting is bending over backwards to make the compiler happy by casting, which isn't type-safe. This defeats the point of trying to use the generics in the first place - to provide type safety at compile time - as the types are erased come runtime. – Danikov Jun 17 '15 at 09:19
  • @podmak: The first form should work when `(Class)` is replaced with `(Class>)`. The second form disables generics completely for this statement as you are using a _raw type_. I guess you got the compilation error when adding the `>` to the second form (which is expected not to work)… – siegi Jun 18 '15 at 06:42
  • @Danikov: As I already said [in this comment](http://stackoverflow.com/questions/30860063/migrating-java-classes-to-use-generics/30865422?noredirect=1#comment49789856_30863772): I agree that casting defeats the purpose of type safety _in most cases_. But can you show me an example of a problem caused by casting a `Class` instance as in the case above? – siegi Jun 18 '15 at 06:59
  • @siegi In this specific case, all the generics are doing is checking that you cast correctly. Casting doesn't check that the cast is possible, hence being an unsafe operation. You could put *anything* to the right of the cast and it will compile, at in same manner, you could cast to anything in the cast. If either of these ever refactored to be incompatible, the issue won't be discovered until runtime because of the lack of safety in the cast operation. This is the opposite of what generics are meant to achieve. – Danikov Jun 18 '15 at 09:22
  • @Danikov: You cannot know what the OP wants to use the `Class` object for or why he designed the generic types of the classes and interfaces the way he did, at least I do not. Each cast is unsafe by definition. If it would be safe, the compiler would not require the cast in the first place. Nevertheless, if you take the definition of the classes, interfaces and constructors as fixed, you have no option but to cast. So it's the only valid answer to the OPs question. Of course the use of `TypeToken` as you demonstrate in your answer is better, but it requires altering the constructors signature. – siegi Jun 18 '15 at 18:04
0

Try this:

super((Class<ClassU<O, P>>)(Class<?>)ClassU.class);

Reference: Class object of generic class (java)

Community
  • 1
  • 1
Damsen
  • 31
  • 3
  • Wanton casting isn't type-safe, which defeats the point of having the generics in the first place. – Danikov Jun 16 '15 at 13:49
  • @Danikov: I agree that this type of casting defeats the purpose of generics _in most cases_ but `Class` is a special case as you cannot get a `Class` object with a specific generic type. – siegi Jun 16 '15 at 17:27
  • @siegi It's very easy to get `Class`. The problem is something like `Class>`. – Danikov Jun 17 '15 at 09:21
  • @Danikov: Yes, sorry, I wasn't clear on that. But the point remains: How are you supposed to get a `Class>` or a `Class>` without casting? – siegi Jun 18 '15 at 06:37
  • @siegi You can't, but more importantly, it doesn't make sense to. Remember that types are erased at runtime and only checked at compile time, but also that generics are only resolved with instances. When you ask for the Class of an object, that is a static property. The information doesn't exist at that scope. That's why Guava's `TypeToken` works both at compile and runtime, due to the anonymous instantiation. – Danikov Jun 18 '15 at 09:14
  • @Danikov: I understand that and, as I said, I agree that casting can be problematic. I agree `TypeToken` is a better option than `Class` for generic types. But how could you ever call a method like `void foo(Class clazz, T object)` (that may be defined in a third party library) on an object with `T` bound to `List` without casting? Such a method should probably be redesigned but I would be very surprised if there is no library containing such a method. – siegi Jun 18 '15 at 18:35
  • @siegi The answer is that you cannot submit `List`, you can only capture List as a raw type, e.g. `List.class`. new ArrayList().getClass() only returns ArrayList>, which makes sense because of erasure; fundamentally type safety has been achieved at compile time, not runtime. – Danikov Jun 19 '15 at 09:11
-1

Simplest way is:

public class Class1<PK, T extends InterE<PK>> {
     public Class1 (Class<? extends T> clazz) {}
}


public class Class2<O extends ClassO, P extends ClassP> extends Class1<Long, ClassU<O, P>> implements Inter2<O, P> {
    public Class2 () {
       super((Class<? extends ClassU<O, P>>) ClassU.class);
    }}

EDIT

Valid for Java 1.6

Denys Denysiuk
  • 775
  • 5
  • 11