给定AGenericClass
声明如下:
public class AGenericClass<T> {
T subject;
public void setSubject(T subject) {
this.subject = subject;
}
}
变量 a , b 和 c 有什么区别?
AGenericClass<String> a = new AGenericClass<>();
AGenericClass<?> b = new AGenericClass<String>();
AGenericClass c = new AGenericClass<String>();
a.setSubject("L"); // OK.
b.setSubject("M"); // Error: setSubject(capture<?>) cannot be
// applied to (java.lang.String)
c.setSubject("N"); // Warning: Unchecked call to 'setSubject(T)'
// as a member of raw type 'AGenericClass'
无需声明IDE即可声明 a b 和 c ,但是在调用
setSubject
时,它们的行为都不同。 最佳答案
两者之间的差异主要与编译时获得的检查有关。鉴于泛型旨在防止用户在运行时进行不安全的转换,因此它们之间的关键和主要差异就植根于此。AGenericClass<String>
声明一个类型为String
的泛型类的实例。使用此泛型执行的任何需要泛型参数的操作均会将该参数绑定到String
,并且将在编译时强制类型检查和类型安全 。
也就是说,如果您具有AGenericClass<String> a = new AGenericClass<>();
并尝试调用a.setSubject(3)
,则Java将不允许编译应用程序,因为类型不匹配。AGenericClass<?>
声明具有未知类型的泛型类的实例。形式上,?
是通配符,可以是任何类型,如果要从中检索元素很好,但是如果要从中添加元素就不好。
原因? AGenericClass<?>
实际上是AGenericClass<? extends Object>
,这很重要,因为in and out principle。通常(尽管这不是严格的保证),但与extends
通用的任何内容都意味着只读操作。
就像我之前说的,可以很好地阅读,因为可以保证一个类最多是Object
,但是由于您不知道也不可以保证您写哪种对象,所以无法对其进行写操作从任何给定的呼叫中添加。
没有类型声明的AGenericClass
是原始类型。 You can read more about them here,但是知道它们存在是出于遗留兼容性的原因。如果您的代码使用原始类型,则会丢失编译时检查,并且您的代码可能会在运行时抛出ClassCastException
,这更加难以调试,诊断和解决。