如果使用工厂方法,为什么TypeToken无法捕获泛型类型?

时间:2016-06-27 06:44:19

标签: java generics guava

跟进How to use TypeToken to get type parameter?,似乎如果我使用工厂方法实例化一个类,那么TypeToken将无法再捕获泛型类型参数。

import com.google.common.reflect.TypeToken;

public class Test<E extends Enum<E>> {

    private static enum MyEnum {
        FIRST,
        SECOND
    };

    private final TypeToken<E> enumType = new TypeToken<E>(getClass()) {
    };

    public static void main(String[] args) {
        Test<MyEnum> container = new Test<MyEnum>() {
        };
        System.out.println("constructor: " + container.enumType.getRawType());
        System.out.println("factory    : " + build(MyEnum.class).enumType.getRawType());
    }

    public static <E extends Enum<E>> Test<E> build(Class<E> type) {
        return new Test<E>() {
        };
    }
}

以上示例输出:

constructor: class Test$MyEnum
factory    : class java.lang.Enum

为什么这不起作用,可以修复吗?

2 个答案:

答案 0 :(得分:1)

看到没有人费心将他们的评论转换为正式答案,我将继续这样做:

作为previously discussed,Java仅保留有关通用超类的类型参数的信息。由于type-parameter与方法(而不是超类)相关联,因此不保留<E>的运行时值。用

替换工厂方法
public static Test<MyEnum> of() {
    return new Test<MyEnum>() {
    };
}

将产生正确的值,但显然这会破坏工厂方法的目的,因为我们被迫使用硬编码的枚举类型。

总结一下,TypeToken在这里工作。保留有关type-parameter的信息的唯一方法是传递Class<E>,如下所示:

public class Test<E extends Enum<E>> {

    private static enum MyEnum {
        FIRST,
        SECOND
    };

    private final Class<E> type;

    public Test(Class<E> type) {
        this.type = type;
    }

    public static void main(String[] args) {
        Test<MyEnum> container = new Test<>(MyEnum.class);
        System.out.println(container.type);
        System.out.println(of(MyEnum.class).type);
    }

    public static <E extends Enum<E>> Test<E> of(Class<E> type) {
        return new Test<>(type);
    }
}

答案 1 :(得分:0)

根据应该如何使用生成的类型,另一个选项实际上是实现ParameterizedType。这基本上是TypeToken.getType()将返回的内容。

    public static ParameterizedType parameterizedType(Class<?> rawType, Type... actualTypeArguments) {
    return new ParameterizedType() {
        @Override
        public Type[] getActualTypeArguments() {
            return actualTypeArguments;
        }

        @Override
        public Class<?> getRawType() {
            return rawType;
        }

        @Override
        public Type getOwnerType() {
            return null;
        }
    };
}