[英]Handling generic class members when there is no generic type variable defined in class
Please have a look at the following code: 请看下面的代码:
public class MyClass<T extends MyObject> {
private Class<T> clazz;
public void setClass1(Class<T> clazz) {
this.clazz = clazz;
}
public Class<T> getClass1() {
return clazz;
}
public T getObject1() {
T o = null;
try {
o = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return o;
}
}
The above works great but requires the class type T
to be known before any MyClass
object is created. 上面的工作很好但是在创建任何
MyClass
对象之前需要知道类类型T
Now if I want to delay specifying T
until setClass1()
is ever called, I have to remove <T extends MyObject>
from MyClass
declaration to something like below: 现在,如果我想延迟指定
T
直到调用setClass1()
,我必须从MyClass
声明中删除<T extends MyObject>
,如下所示:
public class MyClass {
// private Class<T> clazz; // doesn't work anymore as T is unknown, so do the following instead
private Class<? extends MyObject> clazz;
public <T extends MyObject> void setClass1(Class<T> clazz) {
this.clazz = clazz; // OK
}
public void setClass2(Class<? extends MyObject> clazz) {
this.clazz = clazz; // OK
}
public <T extends MyObject> Class<T> getClass1() {
return clazz; // Error, need casting to Class<T>
}
public Class<? extends MyObject> getClass2() {
return clazz; // OK
}
public <T extends MyObject> T getObject1() {
T o = null;
try {
o = clazz.newInstance(); // Error, must cast to T
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return o;
}
public MyObject getObject2() {
MyObject o = null;
try {
o = clazz.asSubclass(MyObject.class).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return o; // OK, but lost the difference between ? extends MyObject and MyObject
}
}
First, why getClass1()
gives error, while setClass1()
is OK? 首先,为什么
getClass1()
给出错误,而setClass1()
是正常的?
Second, why the return in getObject1()
must be cast to (T)
? 第二,为什么
getObject1()
的返回必须强制转换为(T)
?
Third, how do you fix getObject1()
without unnecessary casting nor @SuppressWarnings("unchecked")
? 第三,如何在没有不必要的转换或
@SuppressWarnings("unchecked")
情况下修复getObject1()
@SuppressWarnings("unchecked")
?
Fourth, in my case would changing from using setClass1()
, getClass1()
, getObject1()
to setClass2()
, getClass2()
, getObject2()
better? 第四,在我的情况下会改变从使用
setClass1()
, getClass1()
, getObject1()
到setClass2()
, getClass2()
, getObject2()
更好吗?
First, why getClass1() gives error, while setClass1() is OK?
首先,为什么getClass1()给出错误,而setClass1()是正常的?
In setClass()
you assign a Class<? extends MyObject>
在
setClass()
你分配一个Class<? extends MyObject>
Class<? extends MyObject>
to a variable of type Class<? extends MyObject>
Class<? extends MyObject>
为Class<? extends MyObject>
类型的变量 Class<? extends MyObject>
. Class<? extends MyObject>
。 That is allowed. 这是允许的。
In getClass()
, you want to retrieve a Class<T>
from a variable of type Class<? extends MyObject>
在
getClass()
,您想要从Class<? extends MyObject>
的类型的变量中检索Class<T>
Class<? extends MyObject>
Class<? extends MyObject>
. Class<? extends MyObject>
。 But the compiler has no way to know if what is stored in the variable is really a Class<T>
or perhaps a Class<U>
. 但是编译器无法知道变量中存储的内容是否真的是
Class<T>
或者Class<U>
。 So this is forbidden. 所以这是禁止的。
Second, why the return in getObject1() must be cast to (T)?
第二,为什么getObject1()中的返回必须强制转换为(T)?
Class<? extends MyObject>.newInstance()
Class<? extends MyObject>.newInstance()
returns an object of type ? extends MyObject
Class<? extends MyObject>.newInstance()
返回一个类型为? extends MyObject
的对象 ? extends MyObject
. ? extends MyObject
。 This is not the same as T
. 这与
T
。 That's why you have to cast. 这就是你必须施展的原因。
Third, how do you fix getObject1() without unnecessary casting nor @SuppressWarnings("unchecked")?
第三,如何在没有不必要的转换或@SuppressWarnings(“未选中”)的情况下修复getObject1()?
As explained, the version above does not contain an unnecessary cast. 如上所述,上面的版本不包含不必要的强制转换。 You either have to make
T
a generic parameter of the class itself or you need the cast. 您要么必须使
T
成为类本身的通用参数,要么需要强制转换。
private Class<? extends MyObject> clazz;
public <T extends MyObject> void setClass1(Class<T> clazz) {
this.clazz = clazz; // OK
}
In this example, clazz
is defined as a reference that can hold any kind of Class objects that are of some unknown type that is a subclass of MyObject
. 在此示例中,
clazz
被定义为可以包含任何类型的Class对象的引用,这些类对象是MyObject
的子类。
The any part is important. 任何部分都很重要。 This is why
setClass1
works since it assigns effectively the same thing to clazz
- a subtype of MyObject
. 这就是
setClass1
工作的原因,因为它有效地为clazz
分配了同样的东西 - 一个MyObject
的子类型。 Because of this, the compiler will not complain. 因此,编译器不会抱怨。
Your method setClass1
is effectively a more generic version of this method: 您的方法
setClass1
实际上是此方法的更通用版本:
public void setClass3(Class<MyObject> clazz) {
this.clazz = clazz; // OK
}
since setClass3
assigns the most concrete type Class<MyObject>
to clazz
, which can hold any Class that is MyObject
or a subclass of it. 因为
setClass3
将最具体的类型Class<MyObject>
分配给clazz
,它可以包含任何 MyObject
或其子类。
Moving on. 继续。
public <T extends MyObject> Class<T> getClass1() {
return clazz; // Error, need casting to Class<T>
}
This will not work since T
is a subclass of MyObject
, but can you be certain that clazz
holds Class<MyObject>
or Class<T>
for some value of T? 这不起作用,因为
T
是MyObject
的子类,但是你可以确定clazz
为Class<T>
的某个值保存Class<MyObject>
或Class<T>
吗?
For example, imagine you also have these classes defined somewhere: 例如,假设您还在某处定义了这些类:
public class MySubObject1 extends MyObject {...}
and 和
public class MySubObject2 extends MyObject {...}
Imagine that the wildcard type ?
想象一下通配符类型
?
represents MySubObject1
while T
represents MySubObject2
and since they bear no relationship to each other, the compiler will complain. 代表
MySubObject1
而T
代表MySubObject2
,由于它们彼此没有关系,编译器会抱怨。 Remember, you cannot do this: 请记住,你不能这样做:
MySubObject1 so1 = new MySubObject2(); // compiler error
MySubObject2 so2 = new MySubObject1(); // compiler error
A similar problem exists in getObject1
- the compiler cannot know that T
represents a class or a superclass of clazz
(remember it can hold any subclass of MyObject
), that's why the casting is necessary. getObject1
存在类似的问题 - 编译器无法知道T
表示clazz
的类或超类(记住它可以包含MyObject
任何子类),这就是为什么需要进行转换的原因。
With your current design, you cannot really avoid casting and @SuppressWarnings("unchecked")
in getObject1
. 使用当前的设计,您无法真正避免在
getObject1
转换和@SuppressWarnings("unchecked")
。 If possible, try to avoid mixing generic types and wildcards. 如果可能,请尽量避免混合泛型类型和通配符。
One more thing. 还有一件事。
getClass2
forces its users to deal with a wildcard - not a great API design decision. getClass2
强制其用户处理通配符 - 这不是一个很好的API设计决策。
Methods in your public class MyClass
class have potential type safety issues. public class MyClass
方法public class MyClass
类具有潜在的类型安全问题。
Are you sure that your original public class MyClass<T extends MyObject>
class doesn't meet your needs? 您确定原始
public class MyClass<T extends MyObject>
class不符合您的需求吗? It looks fine to me, except the clazz.newInstance();
它看起来很好,除了
clazz.newInstance();
part since it requires MyObject
and every one of its subclasses to have a zero argument constructor. 因为它需要
MyObject
和它的每个子类都有一个零参数构造函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.