简体   繁体   中英

Constructing a Java generic with an unknown subtype

I have a method like this:

protected <T> void addThing(T obj) {
    process(new Blah<T>(obj));
}

The code that is calling into this doesn't know the exact concrete class of the thing it is calling addThing() on:

Animal f = new Giraffe();
addThing(f);

So what I end up with is Blah<Animal> . But what I actually want to get is Blah<Giraffe> . Is this possible? The only solution I could get so far was kind of awkward. I end up taking the data object into Blah as an Object and downcasting it according to the template:

Blah(Object obj) {
    // Undesirable downcast.
    this.thing = (T)obj;
}

protected <T> void addThing(Class<T> clazz, Object obj) {
    process(new Blah<T>(obj));
}

Animal f = new Giraffe();
addThing(f.getClass(), f);

I guess I'm going to have to do a potentially unsafe downcast somewhere, as I don't know the subtype?

The code that interacts with the Blah makes use of the T class to do some reflection. It needs a concrete T instead of some parent class. – evilfred

No, the code can't interact with T at runtime, because it never gets to know what T is. Generics are ONLY relevant at compile time. What happens is that generics are preprocessed BEFORE the Java code is compiled into byte code. Byte code knows nothing about generics, ever.

In:

protected <T> void addThing(T obj) {
    process(new Blah<T>(obj));
}

Blah[Animal] is fixed at compile-time and you cannot turn the process method's signature into Blah[Giraffe]. Not an option.

What the code deals with at runtime is Blah[Animal], not T (or Blah< T >). Ever. What ever the subtype of what you create with new Blah< T > is, byte code will consider it as Blah[Animal]. You can't create new types at runtime.

Your idea of a solution:

Blah(Object obj) {
    // Undesirable downcast.
    this.thing = (T)obj;
}

is not going to work, because T will be resolved at compile time.

Now if you want to create Blah[Giraffe], don't delegate the creation of new Blah to the addThing method. Do something like this:

private class Blah<T> { }
private class Giraffe { }

public Test_1() {
    Blah<Giraffe> f = new Blah<Giraffe>();
    addThing2(f);
}

public <T> void addThing2(Blah<T> obj) {
    process(obj);
}

public void process(Blah<?> obj) { }

If you can't modify addThing, then you probably don't need to worry about creating a generic with an unknown subtype at runtime in the first place (because is it is impossible in Java). Your issue is/would actually be a non-problem (for the process method at least).

I thought generic parameter types are allowed in constructors as well?

class B<T> {
   private T thing;
   // I thought one could do generic parameter types in constructors as well?
   public B(T thing) { this.thing = thing; }
}
class C {
  public void whatever() {
    Animal f = new Giraffe();
    addThing(f);
  }
  protected <K> void addThing(K object) {
    // if you need reflection, object.getClass() will be the K type at run time.
    process(new B<K>(object));
  }

  // process method elided
}

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