简体   繁体   中英

Generics and casting to the right type

My problem can be summed-up by this snippet:

public interface TheClass<T> {
    public void theMethod(T obj);
}

public class A {
    private TheClass<?> instance;

    public A(TheClass<?> instance) {
        this.instance = instance;
    }

    public void doWork(Object target) {
        instance.theMethod(target); // Won't compile!

        // However, I know that the target can be passed to the
        // method safely because its type matches.
    }
}

My class A uses an instance of TheClass with its generics type unknown. It features a method with a target passed as Object since the TheClass instance can be parameterized with any class. However, the compiler won't allow me to pass the target like this, which is normal.

What should I do to circumvent this issue?

A dirty solution is to declare the instance as TheClass<? super Object> TheClass<? super Object> , which works fine but is semantically wrong...

Another solution I used before was to declare the instance as raw type, just TheClass , but it's bad practice, so I want to correct my mistake.

Solution

public class A {
    private TheClass<Object> instance; // type enforced here

    public A(TheClass<?> instance) {
        this.instance = (TheClass<Object>) instance; // cast works fine
    }

    public void doWork(Object target) {
        instance.theMethod(target); 
    }
}
public class A {
    private TheClass<Object> instance;

    public A(TheClass<Object> instance) {
        this.instance = instance;
    }

    public void do(Object target) {
        instance.theMethod(target); 
    }
}

or

public class A<T> {
    private TheClass<T> instance;

    public A(TheClass<T> instance) {
        this.instance = instance;
    }

    public void do(T target) {
        instance.theMethod(target);
    }
}

The solution is to also type A . Using a wildcard ? makes you loose the type information of TheClass and there is no way to recover it later. There are some ugly hacks you could do but your best shot is to also type A :

public interface TheClass<T> {
    public void theMethod(T obj);
}

public class A<T> {
    private TheClass<T> instance;

    public A(TheClass<T> instance) {
        this.instance = instance;
    }

    public void doIt(T target) {
        instance.theMethod(target); 
    }
}

It won't break any API either.

The reason for the compile error is that the ? wildcard indicates the unknown type in Java. You may declare a variable with an unknown generic parameter, but you cannot instantiate one with it. Which means that the in your constructor the passed in generic class could have been created to hold types that are incompatible with what you are trying to later on use. Case in point:

public class A {
    public static void main(String[] args) {
        TheClass<String> stringHolder = null; // should constrain parameters to strings
        A a = new A(stringHolder);
        a.donot(Float.valueOf(13)) ; // this is an example of what could happen
    }

    private TheClass<?> instance;

    public A(TheClass<?> instance) {
        this.instance = instance;
    }

    public void do(Object target) {
        instance.theMethod(target); 
    }
}

In this case the compiler is preventing you from writing code that would have been prone to bugs. As others have pointed out, you should add a generic parameter type to your A class, in order to constrain the allowed types - that will remove the compile time error.

Some suggested reading: Oracle Generics Trail

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