简体   繁体   中英

Java call super class constructor with generic class object

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

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. in Class<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>> 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>> Class<? super GenericClass<T>> . You'll find there are many other limitations with this solution. It all depends on your goal.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM