这三个参数化变量有何不同?

时间:2016-03-17 05:03:01

标签: java generics

鉴于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'

a b c 所有声明都没有来自IDE的投诉,但是当调用setSubject时它们的行为都不同

3 个答案:

答案 0 :(得分:3)

三者之间的差异主要与编译时获得的检查有关。鉴于泛型是为了在运行时保护用户免受不安全的转换,它们之间的关键和主要差异根源于此。

AGenericClass<String>声明一个特定于类型String的泛型类的实例。使用此泛型需要通用参数的任何操作都将该参数绑定到String,并强制执行类型检查并在编译时键入安全性

也就是说,如果你有AGenericClass<String> a = new AGenericClass<>();而你试图调用a.setSubject(3),那么Java将不允许应用程序编译,因为这些类型不匹配。

AGenericClass<?>声明具有 unknown 类型的泛型类的实例。正式地,?是一个通配符,它​​可以是任何类型,如果要从中检索元素,正常,但不是< / em>如果你想从中添加元素,那就很好。

原因? AGenericClass<?>实际上是AGenericClass<? extends Object>,由于in and out principle,这一点非常重要。通常(虽然这不是严格保证),但extends通用的任何内容都意味着只读操作。

正如我之前所说的那样,阅读起来没什么问题,因为您确保了最多 Object的课程,但您可以&#39写信给你,因为你不知道,也不能保证你从任何给定的电话中添加什么样的对象。

AGenericClass 没有类型声明是原始类型。 You can read more about them here,但知道它们是出于传统兼容性原因而存在的。如果您的代码使用了原始类型,那么会丢失编译时间检查,并且您的代码可能会在运行时抛出ClassCastException,这在调试,诊断和解决时要困难得多

答案 1 :(得分:2)

AGenericClass<String> a = new AGenericClass<>();

所谓的“Diamond”泛型实例化。在泛型类型的构造函数上设置空<>时,编译器将尝试从上下文中确定类型。在示例中,编译器将使用<String>,因为a<String>一起声明。

AGenericClass<?> b = new AGenericClass<String>();

这名为“Unbounded Wildcards”。 b将创建为未知类型的通用。当您尝试使用setSubject()时,编译器无法检查参数的类型。这就是为什么你会例外。要解决此问题,可以使用已定义的泛型类型创建辅助方法。

AGenericClass c = new AGenericClass<String>();

在这种情况下,c声明为“Raw type”。 c将通过<Object>参数创建为通用名称。由于您可以将String分配给Object,因此方法setSubject()将与String一起使用。实际上,它将适用于从Object继承的任何类型,并且可能会导致错误。编译器会警告你。

答案 2 :(得分:1)

AGenericClass<String> a = new AGenericClass<>();

表示这是一个String类型的AGenericClass,它将接受参数中的String代替类型参数T.所以当你调用a.setSubject("L");时它不会抱怨。

AGenericClass<?> b = new AGenericClass<String>();

?表示它是unbounded通配符,表示

  

类型未指定或未知

如果你有一个类似Object的方法,所有这些保证是返回类型public T getSubject()。您可以分配给Object类型的变量,或将其作为期望Object类型的参数传递。无法执行设定操作。 extendssuper是两个关键字,您可以与?一起使用来设置您可能希望进一步探索的下限/上限。

AGenericClass c = new AGenericClass<String>();

Warning: Unchecked call to 'setSubject(T)'..表示此通话是以原始类型进行的,因此即使您拨打setSubject(new Integer(1))它也不会投诉,即它不会执行任何检查类型安全。但随后当你试图获得这个主题并将其转换为String时,你会遇到ClassCastException因为你忽略了编译器警告而我们没有得到泛型提供的类型安全性好处。