Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

java - Passing a runtime resolved parameter to a method which has multiple bound type, compilation error

I have a method which looks like this:

public static <T extends Enum<T> & Marshallable> String foo(Collection<T> collection, Class<? extends Marshallable>... marshallables);

So I'm expecting the collection passed to be a Enum implementing Marshallable interface. It works fine if I have the concrete Enum types at runtime but I wrote a test method which creates an Enum list(implementing Marshallable) dynamically from class object, and I am having trouble passing this list into method above.

@Test
public void fooTest() {
...
if (clazz.isEnum()) { // collection enum xml
    List<? extends Enum<? extends Marshallable>> enumList = (List<? extends Enum<? extends Marshallable>>) Arrays.asList(clazz.getEnumConstants());
--> String enumListXml = foo(enumList, clazz);
...

Marked line will give compilation error. I couldn't figure out how to pass the list without changing method signatures.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You'll need to use a generic helper method with an unchecked cast:

private static <T extends Enum<T> & Marshallable> void fooHelper(Class<? extends Marshallable> type) {
    if (type.isEnum()) {

        //This is safe because of the isEnum check, and we don't return any
        //type with T (important because the caller can specify what T is).
        @SuppressWarnings("unchecked")
        final Class<T> enumType = (Class<T>)type;

        final List<T> enumConstants = Arrays.asList(enumType.getEnumConstants());
        foo(enumConstants);
    }
}

The reason your version doesn't work is because with T extends Enum<T> & Marshallable, T is recursively bound - something that can only be expressed with type parameters. The wildcard type arguments in ? extends Enum<? extends Marshallable> no longer specify that relationship.

WARNING: It's imperative that fooHelper doesn't return a type containing T because this could cause heap pollution. For example:

private static <T extends Enum<T> & Marshallable> List<T> unsafeFooHelper(Class<? extends Marshallable> type) {
    if (type.isEnum()) {

        //no longer safe!
        @SuppressWarnings("unchecked")
        final Class<T> enumType = (Class<T>)type;

        return Arrays.asList(enumType.getEnumConstants());
    }
    return Collections.emptyList();
}

enum Enum1 implements Marshallable { ONE, TWO }

enum Enum2 implements Marshallable { A, B }

...

//caller lies about what T is:
List<Enum2> enumConstants = Main.<Enum2>unsafeFooHelper(Enum1.class);

//sometime later...
Enum2 enumConstant = enumConstants.get(0); //ClassCastException

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...