假设我的班级Context
是这样的。
public class Context {
Set<A> sA;
Set<B> sB;
Set<C> sC;
}
然后我有一个泛型类Performer
,它希望基于Context
类中的泛型类型以下列方式执行操作 - 在Java中是不可能的。
public class Performer<T> { // T can be type of A,B or C
public void saveToSet(T t) {
Context.Set<T>.add(t);
}
}
然后,如果我说saveToSet(B b)
之类的内容,则会调用Context
中的右侧集合,在这种情况下为sB (Set<B>)
,新实例将添加到该集合中。< / p>
好的,问题是......在Java中怎么做? :d
答案 0 :(得分:7)
遗憾的是,由于类型擦除,这种方法在Java中不起作用。
基本上所有运行时都会看到while($line = mysqli_fetch_assoc($result)){
// rest of the code }
并且您的字段已经崩溃到Context.Set<java.lang.Object>.add(t);
的{{1}},因此无法消除歧义。
您可以通过编写Set
,java.lang.Object
等重载来解决此问题。
在很多方面,包括这个,Java泛型都是C ++模板的表兄弟。
答案 1 :(得分:0)
//Not very ice, but something like this can be useful
if (x instanceof A) {
sA.add((A)x);
return;
}
if (x instanceof B) {
sB.add((B)x);
return;
}
答案 2 :(得分:0)
由于Java中称为 type erasure 的概念,这将无法工作。基本上,泛型类型在运行时被擦除,并且运行时将不知道它是哪种类型的对象。所以不,你不能做那样的事情。
但是,您可以创建单独的方法,或者可以在该重写方法中创建继承的类并实现特定的逻辑。
答案 3 :(得分:0)
Java的一种解决方法是使用丰富的类型。这些基本上是将自己的通用参数保存为Class<T>
属性的类型。因此,使用您自己的存储类的实现,而不是使用标准Set
。类似的东西:
class TypeStoringSet<T> extends HashSet<T> {
private Class<T> clazz;
... getter and setter here
public TypeStoringSet<T>(Class<T> clazz) {
super();
this.clazz = clazz;
}
}
答案 4 :(得分:0)
也许你可以使用这种方法。
class Context {
Map<String,Set<Object>> datasets = new HashMap<>();
public <T> void add(T data){
final String name = data.getClass().getName();
if(!datasets.containsKey(name)) datasets.put(name, new HashSet<>());
datasets.get(name).add(data);
}
public <T> Set<T> get(Class<T> type){
return (Set<T>) datasets.get(type.getName());
}
}
class Performer<T> { // T can be ANY type
public void saveToSet(T t) {
final Context context = new Context();
context.add(t);
}
}
final Set<A> aSet = context.get(A.class);
final Set<B> bSet = context.get(B.class);
final Set<C> cSet = context.get(C.class);
其他可能的指甲方法是使用Guice命名注入和方法拦截(减:低性能)
并且需要对此代码进行大量优化(类类型处理,可能是从Guice MapMultibinding注入datasets
)
答案 5 :(得分:0)
实际上,您可以使用反射实现目标并绕过类型擦除。但仅当您的Context
具有明确定义的泛型类型参数的字段时。
因此,如果你不害怕性能损失,那么反思就是你的朋友。您可以像这样反映每个Set
字段的类型参数:
// you can fill that lookup map upon Producer creation
Map<Type, Field> lookup = new HashMap<>();
for (Field field : Context.class.getDeclaredFields()) {
// this code relies on fact that Set has only one
// type argument which is not parametrized
// also you need of course do some sanity checks
// (i.e field is a type of Set, etc)
Type setType = field.getGenericType();
Type setGenericArgument =
((ParameterizedType) superClass).getActualTypeArguments()[0];
field.setAccisseble(true);
lookup.put(setGenericArgument, field);
}
之后,您可以使用获取的类型信息来实施saveToSet
方法
public void saveToSet(T t) {
// also do exception processing and lookup
// map checks here
Field field = lookup.get(t.getClass());
((Set<T>) field.get(context)).add(t);
}