简体   繁体   中英

Instantiate and apply generic method with unknown generic type - Java

I need to instantiate an object with an unknown generic type and then apply a generic method to it. Here is where I stand :

public static void main(String[] args)
{
    BarIf bar = newRandomBarImpl();

    Foo foo1 = newFooBar(bar.getClass()); // warning
    Foo<?> foo2 = newFooBar(bar.getClass()); // error
    Foo<? extends BarIf> foo3 = newFooBar(bar.getClass()); // error

    foo1.doSomething(bar); // warning
    foo2.doSomething(bar); // error
    foo3.doSomething(bar); // error
}

static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}

static <T extends BarIf> T newRandomBarImpl(){}

interface FooIf<T extends BarIf>
{
    public void doSomething(T t);
}

interface BarIf{}

class Foo<T extends BarIf> implements FooIf<T>
{
    public void doSomething(T t){}
}

The strange thing is that for foo2 and foo3, the newFooBar() method returns FooIf rather than Foo. I guess the type inference is messy. But I can't pass the method generic parameters since I don't know the Bar type.

What I would need is Foo<bar.getClass()> . Is there a way to do it?

I tried using TypeToken but I end up with a T type rather than the actual Bar type. Any chance using that?

First of all, a declaration like

static <T extends BarIf> T newRandomBarImpl(){}

is nonsense. It basically says “whatever the caller substitutes for T , the method will return it”. In other words, you can write

ArbitraryTypeExtendingBarIf x=newRandomBarImpl();

without getting a compiler warning. Obviously, that can't work. newRandomBarImpl() doesn't know anything about ArbitraryTypeExtendingBarIf . The method name suggests that you actually want to express that newRandomBarImpl() can return an arbitrary implementation of BarIf , but that's an unnecessary use of Generics,

BarIf newRandomBarImpl(){}

already expresses that this method can return an arbitrary subtype of BarIf . In fact, since BarIf is an abstract type, this method must return a subtype of BarIf and it's nowhere specified which one it will be.

The same applies to the declaration

static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}

It also claims that the caller can choose which implementation of FooIf the method will return. The correct declaration would be

static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}

as the method decides which implementation of FooIf it will return, not the caller.


Regarding your other attempts to deal with FooIf , you can't work this way using a type parametrized with a wildcard, nor can you fix it using Reflection. But you can write generic code using a type parameter:

public static void main(String[] args)
{
    BarIf bar = newRandomBarImpl();
    performTheAction(bar.getClass(), bar);
}
static <T extends BarIf> void performTheAction(Class<T> cl, BarIf obj) {
    FooIf<T> foo=newFooBar(cl);
    foo.doSomething(cl.cast(obj));
}
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}
static BarIf newRandomBarImpl(){}

interface FooIf<T extends BarIf> {
    public void doSomething(T t);
}
interface BarIf{}

The method performTheAction is generic, in other words, works with an unknown type expressed as type parameter T . This method can be invoked with an unknown type ? extends BarIf ? extends BarIf as demonstrated in the main method.

However, keep in mind, that every reference to a type X implies that the referred object might have a subtype of X without the need to worry about it.

You can simply use the base class BarIf here, regardless of which actual subtype of BarIf the object has:

BarIf bar = newRandomBarImpl();
FooIf<BarIf> foo=newFooBar(BarIf.class);
foo.doSomething(bar);

Note that when you want to use methods of the actual implementation type Foo , not specified in the interface, you will have to cast the FooIf to Foo . You can cast a FooIf<BarIf> to Foo<BarIf> without a warning as the generic type conversion is correct if Foo<X> implements FooIf<X> .

However, it can fail at runtime as the method newFooBar is not required to return an instance of Foo rather than any other implementation of FooIf . That's why an explicit type cast is the only correct solution as it documents that there is an assumption made about the actual runtime type of an object. All other attempts will generate at least one compiler warning somewhere.

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