我有一个返回List<Property<?>>
。
属性是具有一个通用参数的类型:
public class Property<T extends Comparable<T>> { ... }
有一个混合类型属性的列表,我不知道特定元素有什么类型参数。
我想做那样的事情:
List<Property<?>> list = getList();
for(Property<?> crt : list)
{
PropertyWrapper<?> crtWrapper = new PropertyWrapper(crt.getGenericType());
// I know this doesn't exist ----^
}
在一句话中:我需要PropertyWrapper
具有与当前Property
相同的通用模板参数。有任何方式吗?
我可以应用https://stackoverflow.com/a/3437930/146003中所述的建议,但即使我这样做,如何实现相应的PropertyWrapper<XXX>
,只有Class<T>
的实例?
如果需要,我可以修改Property<?>
。我也不介意是否需要使用反射(我认为需要反思)
编辑:我忘记了什么。实际上我不能通过行
来实现包装器PropertyWrapper<?> crtWrapper = new PropertyWrapper(crt.getGenericType());
因为我有专门的子类(PropertyWrapper_String
)。
现在我看到两种可能性:
1:按字符串对类进行实例化:
String strGenericType = "";
Class<?> wrapperClass = Class.forName("PropertyWrapper_" + strGenericType);
2:有没有办法在没有创建子类的情况下专门化泛型类?
非常感谢您的提示
答案 0 :(得分:7)
尝试创建以下方法:
<T> PropertyWrapper<T> createWrapper(Property<T> property){
return new PropertyWrapper<T>(property);
}
然后这样称呼它。
List<Property<?>> list = getList();
for(Property<?> crt : list)
{
PropertyWrapper<?> crtWrapper = createWrapper(crt);
}
上述工作的原因是泛型类型T
是从参数中推断出来的,并且对整个方法都是锁定的。与在循环中使用<?>
不同,其中<?>
的每个实例都被推断为不同的类型。
编辑:
要根据包装器中的类来处理具有不同类类型的问题,请考虑一个Map,其中键是要包装的类,值是包装器。
Map<Class<?>, Class<?>> myMap;
然后你可以这样:
Class<?> wrappedType = property.getGenericType();
Class<?> wrapperClass = myMap.get(wrappedType);
PropertyWrapper<?> wrapper = (PropertyWrapper<?>) wrapperClass.newInstance();
如果您需要传递参数,可能需要执行类似的操作。
Constructor<?> constructor = wrapperClass.getDeclaredConstructor(property.getClass());
PropertyWrapper<?> wrapper = (PropertyWrapper<?>) constructor.newInstance(property);
答案 1 :(得分:1)
如果你有一个Class<T>
,你可以拿这个类,通过调用类对象上的newInstance()
来创建一个实例并将其转换为T
。
您遇到的问题是获取crt
的通用参数。如果您具有Property
的具体子类,例如StringProperty extends Property<String>
,您可以使用反射获取类型。但是,如果您只创建没有具体子类的Property<T>
实例,并且您不知道列表元素的创建位置,那么AFAIK就不可能获得泛型类型(即使您知道元素的创建位置)它可能是不可能的。)
因此,您可能获得属性包装器以了解属性类型的唯一方法可能是在属性本身中存储类型参数(类)。然后包装器可以查询属性的类型/类成员变量。
编辑:解释为什么这不可能或至少非常困难。
泛型的问题在于,由于类型擦除,当使用new Property<SomeType>()
创建属性时,泛型类型会丢失。这里没有可用于检索SomeType
的运行时信息。
如果你有具体的子类(定义具体的泛型类型),你可以在运行时获得每个类的泛型参数的反射信息。然后,您可以获取每个属性的实际类,并检索该类的反射数据。
如果你有定义这些类型的方法或字段并返回/保持对属性的引用,这也是可能的。但是,我怀疑你有这些信息,因为你似乎得到了一些列表,并且不确切知道该列表的元素在何处以及如何创建。
我进一步假设属性'类只是Property
,而不是子类。因此,唯一的方法是自己提供运行时信息,即通过将类型类的引用作为构造函数参数传递。
答案 2 :(得分:0)
好的,我要回答自己。
我现在正在将Class<?>
的实例传递给Property
- 类。
然后,我提取属性的基本名称,并简单地删除"java.lang."
,这可能与大多数情况下一样,我这样做是为了原始数据类型 - resp。他们的自动装箱课程。
此外,我只是通过名称实例化一个新的包装器实例,并将to be wrapped属性作为参数传递给适用于它的构造函数。
这里有一些感兴趣的代码:
String template = "..."; // some package definition
for (Property<?> crt : bag)
{
String className = template + crt.getClassName();
Class<? extends PropertyWrapper<?>> wrapperClass = null;
wrapperClass = (Class<? extends PropertyWrapper<?>>) Class.forName(className);
Constructor<? extends PropertyWrapper<?>> constructor = wrapperClass.getConstructor(new Class<?>[] {Property.class});
PropertyWrapper<?> wrapper = constructor.newInstance(crt);
// Further operations using the wrapper
}
为简单起见,我省略了错误处理部分。
答案 3 :(得分:0)
为什么不让Property
知道如何创建自己的包装器,而不是从调用站点实例化包装器?类似的东西:
public class Property<T extends Comparable<T>> {
PropertyWrapper<T> newWrapper();
}
但是,这只有一半的帮助。你真正的问题在于像你这样的循环:
List<Property<?>> list = getList();
for(Property<?> crt : list)
{
PropertyWrapper<?> crtWrapper = crt.newWrapper();
}
PropertyWrapper和Property具有“相同”类型这一事实并不是很有用,因为该类型只是未绑定的通配符。
我真的很讨厌给出其中一个“你真正想做什么”的答案,但是 - 你真的想做什么?一般来说,一旦你到达一个未绑定的通配符,所有的希望都会丢失(除非你愿意做不安全的,不可检查的强制转换或者用反射检查向后弯曲)。解决此问题的一种方法是在将该属性放入Property<T>
之前,尽可能多地将操作放在List<Property<?>>
中。我的newWrapper()
就是一个例子:它将创建PropertyWrapper<T>
的行为放入Property<T>
本身。如果您想要在属性更改时注册回调,那么您也可以在每个地方执行非通配符Property<Whatever>
。例如:
Property<UserLogin> property = new Property<UserLogin>();
SomeListener<UserLogin> listener = whatever();
property.addListener(listener);
wildcardedPropertiesList.add(property);
这个特殊的例子可能无济于事,但希望它会给你适用的想法。