Java中的dynamic通用键入
如果我有一个类使用通用types如
public class Record<T> { private T value; public Record(T value) { this.value = value; } }
如果我知道使用的所有types,例如在这个例子中就是这种情况,那么在devise时input所有内容是非常简单的:
// I type explicitly String myStr = "A"; Integer myInt = 1; ArrayList myList = new ArrayList(); Record rec1 = new Record<String>(myStr); Record rec2 = new Record<Integer>(myInt); Record rec3 = new Record<ArrayList>(myList);
如果我从不知道types的“某处”得到一个对象列表,会发生什么? 我如何分配types:
// now let's assume that my values come from a list where I only know during runtime what type they have ArrayList<Object> myObjectList = new ArrayList<Object>(); myObjectList.add(myStr); myObjectList.add(myInt); myObjectList.add(myList); Object object = myObjectList.get(0); // this fails - how do I do that? new Record<object.getClass()>(object);
Javagenerics不是C ++模板。
Javagenerics是编译时function,而不是运行时function。
这里是一个Javagenerics教程的链接。
这不能用于Java:
new Record<object.getClass()>(object);
你必须使用多态(比如每个对象实现一个已知的接口)或RTTI(instanceof或Class.isAssignableFrom())。
你可能会这样做:
class Record { public Record(String blah) { ... } public Record(Integer blah) { ... } ... other constructors. }
或者你可以使用Builder模式 。
在运行时从通用types创build实例
我不完全清楚你想要完成什么,但是看起来最简单的解决scheme是最好的解决scheme。
可以通过使用脚本环境(Groovy,JavaScript,JRuby,Jython)来解决这个问题,该脚本可以dynamic地评估和执行任意代码来创build对象,但是这个过程非常复杂而且过于复杂,只是为了创build一个对象。
但不幸的是,我认为它有一个非常行人的解决scheme。
只要有一组预定义的支持types,就可以使用Factory
模式。 这里我只是利用javax.inject
/ com.google.inject
包中的Provider<>T
接口。
Q26289147_ProviderPattern.java
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()); } } }
免责声明:
这只是一个概念certificate的例子,我绝对不会在生产代码中使用
switch
语句,我会使用Strategy Pattern
或Chain of Responsibility
模式来封装基于ClassName
键创build的types的逻辑。
这最初看起来像一个generics的问题,这不是,这是一个创造性的问题。
也就是说,您不需要传递Class<?>
实例,您可以在运行时使用Guava的TypeToken
从参数化类中获取Generic Type
信息。
您甚至可以在Guava库中使用TypeToken
在运行时创build任何genericstypes的实例。
主要的问题是不支持这种语法: Geography<myClass.newInstance()> geo;
而且我也想不出以上Provider
实现。
下面是一个如何使用
TypeToken
的稻草人示例,以便您的参数化类将始终知道它们的types!
Q26289147.java
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()) {}; } }
笔记:
- 适用于具有默认无参数构造函数的类。
- 如果没有缺省的arg构造函数,比直接reflection效果更好。
- 应该和Guice一起玩,让你使用“.getRawType()
generated
类to pass to
getInstance()`的一个Injector。 还没有尝试过这个,我只是想到了! - 您可以使用
Class<T>.cast()
来执行不需要@SuppressWarning("unchecked")
的投射。
如果您不知道types,则不能使用generics来强制进行编译时检查。
只是为了使用它,你可以说
new Record<Object>(object);