[译] 泛型 7.细则

本文深入探讨Java泛型的工作原理,解释泛型类实例共享、类型转换与instanceOf的使用限制,以及为何不能创建参数化类型的数组。同时,介绍了如何使用类字面量作为运行时类型标记。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<< 与老式代码交互 · 目录 · 类字面量作为运行时类型标记 >>

                                                                                  

细则 (The Fine Print)

泛型类由其所有调用共享

下面的代码片段会打印什么结果?

List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

你可能会想说false,但你错了。打印true,因为泛型类的所有实例都具有相同的运行时类,而不管它们的实际类型参数如何。

实际上,一个泛型类对其所有的类型参数都具有相同的行为;可以将同一个类视为具有许多不同的类型。

因此,类的静态变量和静态方法也在所有实例之间共享。这就是为什么在静态方法或静态初始化器 以及 在静态变量的声明或初始化器 中引用类型声明的类型参数是非法的。

类型转换和instanceOf

泛型类在其所有实例之间共享的另一个含义是——检查实例是否是泛型类型的实例通常是没有意义的:

Collection cs = new ArrayList<String>();
// Illegal.
if (cs instanceof Collection<String>) { ... }

类似地,如类型转换:

// Unchecked warning,
Collection<String> cstr = (Collection<String>) cs;

会给出一个未经检查的警告,因为运行时系统不会检查的。

类型变量也是如此:

// Unchecked warning. 
<T> T badCast(T t, Object o) {
    return (T) o;
}

类型变量在运行时不存在。这意味着它们在时间和空间上都不需要性能开销,这一点很好。不幸的是,这也意味着您不能在强制转换中可靠地使用它们。

数组

数组对象的元素类型不能是类型变量或参数化类型,除非它是(无界的)通配符类型。你可以声明元素类型为类型变量或参数化类型的数组类型,但不能声明数组对象。

这确实很烦人。这个限制对于避免以下情况是必要的:

// 实际上不允许的
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// 不健全,但能通过运行时存储检查
oa[1] = li;

// 运行时错误: ClassCastException.
String s = lsa[1].get(0);

如果参数化类型的数组被允许的,则前面的示例将在编译时没有任何未检查的警告,但在运行时失败。我们已经将类型安全作为泛型的主要设计目标。特别是,该语言旨在保证,如果您的整个应用程序在使用javac -source 1.5进行编译时没有未经检查的警告,那么它是类型安全的。

但是,仍然可以使用通配符数组。相对前一段代码,以下更改 放弃了数组对象和元素类型参数化的数组类型的使用。因此,我们必须显式地转换以从数组中获取字符串:

// 无界通配符类型数组
List<?>[] lsa = new List<?>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// 正确
oa[1] = li;
// 运行时错误,但显式强转了
String s = (String) lsa[1].get(0);

在下一个导致编译时错误的变体中,我们避免创建一个元素类型是参数化的数组对象,但仍然使用带有参数化元素类型的数组类型:

// Error.
List<String>[] lsa = new List<?>[10];

类似地,试图创建元素类型为类型变量的数组对象会导致编译时错误:

<T> T[] makeArray(T t) {
    return new T[100]; // Error.
}

由于类型变量在运行时不存在,因此无法确定实际的数组类型。

解决这些限制的方法是使用类文本作为运行时类型标记,如下一节所述,类字面量作为运行时类型标记

                                                                                  

<< 与老式代码交互 · 目录 · 类字面量作为运行时类型标记 >>

本文译自:https://docs.oracle.com/javase/tutorial/extra/generics/fineprint.html

转载于:https://my.oschina.net/tita/blog/2886682

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值