给定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,这更加难以调试,诊断和解决。

07-26 00:50