我有这样的代码:
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}}时返回getFiled
但String
返回getFileds
?
我必须将List<Object>
定义为A
才能正常工作。
谢谢
答案 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
}
我们在代码field
和getField
中看到,仍然具有通用的T
类型,而不是compiled into String
。那么为什么getField()
可以正常工作?