当没有可能的泛化时,正确地转换为未知类型作为参数传递

时间:2017-07-14 19:08:58

标签: java generics reflection enums interface

我有一个特殊类型的枚举来定义一个类,然后该类可以直接从枚举值实现。为实现这一点,每个枚举实现以下接口:

public interface IEnumWithClass {
    public Class<?> getAssociatedClass();
}

一个枚举就是这样:

public enum Foo implements IEnumWithClass {

    eEnumValue(Goo.class);

    private final Class<?>    mAssociatedClass;

    Foo(Class<?> iAssociatedClass) {
        mAssociatedClass = iAssociatedClass;
    }

    @Override
    public Class<?> getAssociatedClass() {
        return mAssociatedClass;
    }
}

然后可以使用以下帮助程序实例化该类:

public class EnumWithClassHelper extends EnumWithValueHelper {
    private static Object getInstance(Class<?> type) throws Exception
    {    
        Constructor<?> constructor  = type.getConstructor();        
        return constructor.newInstance();
    }   
}

和以下一样的电话:

Goo myGoo = (Goo) EnumWithClassHelper.getInstance( Foo.eEnumValue.getAssociatedClass() );

问题:我想在这里避免演员,并直接获得一个Goo。

我是使用以下助手制作的:

@SuppressWarnings("unchecked")
private static <T> T getInstance(Class<?> type) throws Exception
{    
    Constructor<?> constructor  = type.getConstructor();        
    return (T) constructor.newInstance();
}

但在这种情况下,我有一个未经检查的强制转换异常警告,我也不想这样做。

任何方式安全地做到这一点还是我坚持第一个解决方案?由于接口和枚举限制,我似乎无法将Class作为参数传递(我没有成功地为枚举指定泛型参数)。

谢谢! :)

1 个答案:

答案 0 :(得分:1)

不幸的是,您需要在某个时候进行未经检查的强制转换,因为任何编译时类型检查都不会出现反射。这又回到了类型转换的基础; JVM不知道它将在代码中实例化的对象的类型(按设计),直到代码被执行为止,因此无法确定是否可以使用相应的类型而未经检查的强制转换将保持未选中状态由于您在界面中使用的泛型。同时,如果你打算使用它,没有理由不转换你反射性实例化的对象,我会质疑为什么你不想要一个类型转换,因为所有这一切确保你拥有的变量实际上是它所声称的类型只会使你的生活更容易出错。

这个答案可能不太令人满意,所以让我建议你使用另一个模型:

public interface IEnumWithClass<T> {
    public Class<T> getAssociatedClass();
}

public abstract class Foo<T> implements IEnumWithClass<T> {
    // Put any other code that shouldn't be instance-specific here
}

public final class ActualFoos{
    public static final Foo<Goo> goo = new Foo<Goo>() {
        @Override
        public Class<String> getAssociatedClass() {
            return Goo.class;
        }
    };
    public static final Foo<Integer> integer = new Foo<Integer>() {
        @Override
        public Class<Integer> getAssociatedClass() {
            return Integer.class;
        }
    };
}

除此之外,您的getInstance方法可以重新定义:

public static <T> T getInstance(Class<T> type) throws Exception
{
    Constructor<T> c = type.getConstructor();
    c.setAccessible(true);
    return c.newInstance();
}

这允许您使用(例如)getInstance作为参数调用ActualFoos.goo.getAssociatedClass()并直接获取Goo对象,而无需任何强制转换或未经检查的强制转换。因此,例如,如果我们假设方法getInstance是类Bar的一部分,则代码可能如下所示:

Goo g = Bar.getInstance(ActualFoos.goo.getAssociatedClass());

或者,如果您只是指您知道可以访问的构造函数(可能是公共的),那么您可以完全抛弃辅助方法:

Goo g = ActualFoos.goo.getAssociatedClass().getConstructor().newInstance();

TLDR; 是一种使用不同系统来处理所需内容的方法,但不完全是您想要使用系统的方式。