如果我有一个类使用泛型类型,如
public class Record<T> {
private T value;
public Record(T value) {
this.value = value;
}
}
在设计时输入所有内容非常简单,如果我知道所有使用的类型,例如本例中的情况:
// 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);
如果我从“某处”得到一个我不知道类型的对象列表会怎么样?如何指定类型:
// 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);
答案 0 :(得分:17)
Java泛型不是C ++模板。
Java泛型是编译时功能,而不是运行时功能。
以下是Java generics Tutorial的链接。
这对Java无法解决:
new Record<object.getClass()>(object);
您必须使用多态(例如,每个对象实现一个已知接口)或RTTI(instanceof或Class.isAssignableFrom())。
您可以这样做:
class Record
{
public Record(String blah) { ... }
public Record(Integer blah) { ... }
... other constructors.
}
或者您可以使用Builder pattern。
答案 1 :(得分:3)
我并不完全清楚你要完成什么,但首先看起来最简单的解决方案是最好的解决方案。
可以使用可以动态评估和执行任意代码来创建对象的脚本环境(Groovy,JavaScript,JRuby,Jython)来解决这个问题,但这样做会非常复杂且过于复杂,只是为了创建一个对象。 / p>
但不幸的是,我认为它有一个非常行人的解决方案。
只要有一组预定义的受支持类型,您就可以使用Factory
模式。在这里,我只使用Provider<>T
/ javax.inject
包中的com.google.inject
界面。
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的Generic Type
从参数化类中获取TypeToken
信息。
您甚至可以在运行时使用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()) {};
}
}
generated
类to pass to
getInstance()`。还没试过这个,我只是想到了它!的Class<T>.cast()
在整个地方进行不需要@SuppressWarning("unchecked")
的投射。答案 2 :(得分:1)
如果您不知道类型,则无法使用泛型强制执行编译时检查。
只是为了使用它你可以说
new Record<Object>(object);