为什么TypeToken在使用工厂方法的情况下捕获泛型失败?

Why does TypeToken fail to capture generic type if factory method is used?

跟进 ,似乎如果我使用工厂方法实例化 class,那么 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

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

鉴于没有人愿意将他们的评论转化为正式答复,我将继续这样做:

一样,Java只保留泛型超类的类型参数信息。由于类型参数与方法相关联(与超类相反),<E> 的运行时值不会保留。用

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

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

总而言之,TypeToken 在这里不起作用。保留有关类型参数信息的唯一方法是按如下方式传递 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);
    }
}

根据应如何使用生成的类型,另一种选择实际上是实现 ParameterizedType。这基本上就是 TypeToken.getType() 会 return.

    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;
        }
    };
}