在运行时从泛型类型创建实例
我不完全确定您要完成什么,但乍一看似乎最简单的解决方案就是最好的解决方案。
这可以通过使用脚本环境(Groovy、JavaScript、JRuby、Jython)来解决,该环境可以动态地评估和执行任意代码来创建对象,但是仅仅为了创建一个对象就变得非常复杂和过于复杂。
但不幸的是,我认为它有一个非常普通的解决方案。
只要有一组预定义的受支持类型,您就可以使用Factory 模式。这里我只是利用javax.inject/com.google.inject 包中的Provider<>T 接口。
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
@Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
@Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
@Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(@Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
@Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
免责声明:
这只是一个概念证明示例,我永远不会使用switch
在生产代码中这样的语句我会使用 Strategy
Pattern 或 Chain of Responsibility 模式来封装逻辑
根据ClassName 键创建什么类型。
这最初看起来像是一个泛型问题,其实不是,这是一个创建问题。
也就是说,您不需要传递 Class<?> 的实例,您可以在运行时使用来自 Guava 的 TypeToken 从参数化类中获取 Generic Type 信息。
您甚至可以在运行时使用来自 Guava 库的 TypeToken 创建任何泛型类型的实例。
主要问题是不支持这种语法:Geography<myClass.newInstance()> geo;,除了上面的Provider 实现之外,我无论如何也想不出伪造它。
这是一个稻草人示例,说明如何使用TypeToken,以便您的
参数化的类总是知道它们的类型!
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
注意事项:
- 非常适合具有默认无参数构造函数的类。
- 如果没有默认的 no arg 构造函数,比使用直接反射效果更好。
- 应该与 Guice 配合使用,允许您使用
.getRawType() 生成的 Class<T> 传递给注入器的 getInstance()。 还没有尝试过,我只是想到了!
- 您可以使用
Class<T>.cast() 进行不需要@SuppressWarning("unchecked") 的强制转换。