当未指定泛型类型时,为什么不返回基本类型?

时间:2018-06-29 08:26:59

标签: java generics java-8

我有这样的代码:

public class A<T extends String> {

    T field;
    List<T> fields;

    public T getField() {
        return field;
    }

    public List<T> getFields() {
        return fields;
    }

    public static void test(){
        A a = new A();
        String s = a.getField();
        List<String> ss = a.getFields();
    }
}

为什么在创建没有通用类型A的{​​{1}}时返回getFiledString返回getFileds

我必须将List<Object>定义为A才能正常工作。

谢谢

3 个答案:

答案 0 :(得分:6)

在声明*.class中对类型T的擦除为T field,因为String已绑定:

T

<T extends String> 中的任何subtype也是String 1

但是,这不适用于通用String。这是因为泛型不是协变的:

List<T>

声明List<String> str = new ArrayList<String>(); List<Object> obj = str; // Error: incompatible types (even though String extends Object) 中对List<T>的擦除为List<T> fields,特别是因为List可以容纳List<T>或其String > 1 。鉴于subtypes是原始类型,编译器无法猜测A方法返回的列表中元素的类型。

无论如何,这行代码:

getFields()

被编译(Erasure of Generic Types)到:

List<T> fields;

结果,当您写:

List fields;

您会失去类型安全性,并收到“未检查的分配”警告-在这种情况下,编译器无法保证A a = new A(); instead of A<String> a = new A<>(); 恰好是fields list

List<String>

建议。请勿使用原始类型!


1-如您所知,List<String> list = a.getFields(); // Unchecked assignment: 'List' to 'List<String>' ... 类是最终的,不能扩展。替换为任何可以扩展的类型。

答案 1 :(得分:2)

使用原始类型时,您仍然会遇到限制。因此T extends String您知道返回的任何内容都会扩展String。当您使用原始类型时,它像{em> like 具有? extends String

A<?> a = new A<>();
String s = a.getField(); 
List<? extends String> ss = a.getFields();
String s2 = ss.get(0);

当然会有一个NPE错误,但是想法就在那里。

与原始类型的不同之处在于,由于返回了原始类型的列表,因此丢失了get字段中的所有类型信息。

List ss = a.getFields();

这意味着,即使String的边界也丢失了。

此外,String是最终类,因此这里的约束实际上并没有增加任何值,因为我们知道T始终是String。您也可以使用:

class A {
    String field;
    List<String> fields;

    public String getField() {
        return field;
    }

    public List<String> getFields() {
        return fields;
    }

    public static void test(){
        A a = new A();
        String s = a.getField();
        List<String> ss = a.getFields();
    }
}

答案 2 :(得分:1)

这不是一个答案,但是只有在这里我可以放置代码。因此,我尝试了此示例(但我将其命名为TestGenerics,并添加了main而不是test()),然后反编译了它:

javap -c TestGenerics
Compiled from "TestGenerics.java"
public class TestGenerics<T extends java.lang.String> {
  T field;

  java.util.List<T> fields;

  public TestGenerics();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public T getField();
    Code:
       0: aload_0
       1: getfield      #2                  // Field field:Ljava/lang/String;
       4: areturn

  public java.util.List<T> getFields();
    Code:
       0: aload_0
       1: getfield      #3                  // Field fields:Ljava/util/List;
       4: areturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class TestGenerics
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #6                  // Method getField:()Ljava/lang/String;
      12: astore_2
      13: aload_1
      14: invokevirtual #7                  // Method getFields:()Ljava/util/List;
      17: astore_3
      18: return
}

我们在代码fieldgetField中看到,仍然具有通用的T类型,而不是compiled into String。那么为什么getField()可以正常工作?