0

I've a class like this:

public class GenericClass<T> {
    public void wrap(T item) {...}
}

public abstract class AbstractClass<T, G extends GenericClass<T>> {
    protected G wrapper;

    public AbstractClass(Class<G> generic, T something) {
        wrapper = generic.newInstance();
        wrapper.wrap(something);
    }

    public G wrapAndGet(T item) throws Exception {
        wrapper.wrap(item);
        return wrapper;
    }
}

// 90% of the time people only need this:
public class GeneralClass<T> extends AbstractClass<T, GenericClass<T>> {
    public GeneralClass(T something) {
        super(GenericClass.class, something);    // !error, asking for Class<GenericClass<T>>
    }
}

// and use it like this:
new GeneralClass<String>("foo").wrapAndGet("bar");

// but sometimes you might need:
public class AdvancedWrapper<T> extends GenericClass<T> {
    public T advancedMethod();
}

public class ClassUseAdvancedWrapper<T> extends AbstractClass<T, AdvancedWrapper<T>> {
    public ClassUseAdvancedWrapper(T something) {
        super(AdvancedWrapper.class, something);    // error, but let's say it compiles
    }
}

// then you can use:
new ClassUseAdvancedWrapper<String>("foo").wrapAndGet("bar").advancedMethod();

This doesn't make sense to me: aren't GenericClass<T> and GenericClass having the same class at runtime? How can I get the class object of GenericClass<T> then?

EDIT: add use case

NSF
  • 2,499
  • 6
  • 31
  • 55

2 Answers2

5

Class literals, ex. Foo.class, don't include generic type information. The JLS states

The type of C.class, where C is the name of a class, interface, or array type (§4.3), is Class<C>.

This also means that the type of the expression contains a raw type, ie. inClass<GenericClass>, GenericClass is not parameterized and is therefore raw.

So, the problem here is your abstract class' constructor expects

Class<G> generic

where G is

G extends GenericClass<T>>

so (close to)

Class<G extends GenericClass<T>>

but you are providing

Class<GenericClass>

Those are not assignable.

You ask

How can I get the class object of GenericClass then?

You can't. It doesn't exist. At runtime, there is no Class object for GenericClass<T>, there's only a Class object for GenericClass.

You'd have to explain what you are trying to do in more detail, but you could make your code compile with

abstract class AbstractClass<T, G extends GenericClass<T>> {
    public AbstractClass(Class<? super G> generic, T something) {
    }
}

Now your constructor expects a Class<? super G extends GenericClass<T>> and you are providing a Class<GenericClass>. In your super constructor invocation GenericClass<T> is bound to G and GenericClass is a supertype of GenericClass<anything>, so Class<GenericClass> is assignable to a Class<? super GenericClass<T>>. You'll find there are many other limitations with this solution. It all depends on your goal.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Actually `GeneralClass extends AbstractClass>` shows in that case `G` is just `GenericClass`. So essentially the compiler refuses to assign `Class` to `Class>`. However at runtime these should be the same class so why isn't casting even allowed? ` super G>` is not an option as the bound is not effective any more (you can even pass Class). – NSF Aug 29 '14 at 02:35
  • @NSF Put aside runtime, this is a static type analysis at compilation time. [This is a limitation/feature of generics](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p). The compiler cannot guarantee that a raw `Class` is truly a `Class>`. You would be setting yourself up for `ClassCastException`s left and right. – Sotirios Delimanolis Aug 29 '14 at 03:02
2

This is what I had to do to get some code to compile. I don't know what you are trying to do, but at least this gives a blueprint to follow. The main problem I saw was that AbstractClass needs two arguments for its CTOR, and you can't do that by passing only one argument to the CTOR of GeneralClass, so I had to add a second argument.

public class GenericTest {
   public static void main(String[] args) {
      GenericClass<Integer> generic = null;
      AbstractClass<Integer, GenericClass<Integer>> abstr = new AbstractClass( generic.getClass(), 1 );
      GeneralClass<Integer, GenericClass<Integer>> general = new GeneralClass( generic.getClass(), 1 );
   }
}

class GenericClass<T> {
}

class AbstractClass<T, G extends GenericClass<T>> {
   public AbstractClass(Class<G> generic, T something) {
   }
}

class GeneralClass<T,G extends GenericClass<T>> 
extends AbstractClass<T, G> {
   public GeneralClass( Class<G> generic, T something) {
      super( generic, something);
   }
}

EDIT: So to echo the discussion in the comments, I made AbstractClass concrete so I could show what arguments its constructor takes. (Also, I had to do that in my own code to figure out what the heck was going on, so I'm showing my work.)

I hope the examples/test cases in the main method above make it clear what's going on. To reiterate what Sotirios Delimanolis said:

How can I get the class object of GenericClass then?

You can't. It doesn't exist. At runtime, there is no Class object for GenericClass, there's only a Class object for GenericClass.

GenericClass in this case sets T = Integer. There's clearly no relationship between the system class Integer and the class you just declared. There's no get() you can call on an integer to get your class GenericClass. I think my example makes this plain as day.

You could I suppose make some sort of processing tool that scans .java files or .class files, and builds a database of type parameters used by GenericClass (and its subclasses?), but that's boarding on crazy unfeasible. If you have the resources, it could be done, but it won't make sense for any but the largest projects, and probably only a public framework at that.

markspace
  • 10,621
  • 3
  • 25
  • 39
  • I'm not sure if this is semantically equivalent to what the OP was asking for -- in particular, making AbstractClass no longer `abstract` and adding the second type parameter to `GeneralClass`. – Patrick Collins Aug 29 '14 at 01:22
  • Making `AbstractClass` concrete just let me show in the main method how its constructor works. It's kinda immaterial to the question about `GeneralClass`. I left it as-is because it let me show my test cases in the main method. – markspace Aug 29 '14 at 01:24
  • Fair. I don't think changing `GeneralClass`'s type parameter is immaterial, though. – Patrick Collins Aug 29 '14 at 01:26
  • Yeah, but this is the only way to get the code to compile. Can the OP use it? I dunno. It fair to say it's different, it is. – markspace Aug 29 '14 at 01:28
  • The answer is long but not solving the problem in the right direction: the purpose of my code is that you only need to specify T when declaring GeneralClass, but your code essentially makes AbstractClass and GeneralClass the same thing then why would I bother creating it? – NSF Aug 29 '14 at 02:38