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 PatternChain 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()) {}; } } 

笔记:

  1. 适用于具有默认无参数构造函数的类。
  2. 如果没有缺省的arg构造函数,比直接reflection效果更好。
  3. 应该和Guice一起玩,让你使用“.getRawType() generatedto pass to getInstance()`的一个Injector。 还没有尝试过这个,我只是想到了!
  4. 您可以使用Class<T>.cast()来执行不需要@SuppressWarning("unchecked")的投射。

如果您不知道types,则不能使用generics来强制进行编译时检查。

只是为了使用它,你可以说

 new Record<Object>(object);