第32项:谨慎地结合泛型和可变参数(Combine generics and varargs judiciously)

本文讨论了Java中泛型可变参数存在的类型安全问题,包括堆污染和编译器警告。解释了为什么使用泛型可变参数会引发警告,以及如何通过`@SafeVarargs`注解来消除警告。同时强调,只有当方法不会在可变参数数组中存储内容且不暴露数组时,使用`@SafeVarargs`才是安全的。最后,提出使用`List`参数替代可变参数来提高类型安全性。
摘要由CSDN通过智能技术生成

  可变参数方法(第53项)和泛型都在Java 5时添加到了平台中,所以你可能会期望它们会优雅地相互作用;可悲的是,它们不能相互作用。可变的目的是允许客户端将数量可变的参数传递给方法,但它是一个漏洞抽象( leaky abstraction):当你调用可变参数方法时,会创建一个数组来保存可变参数;该数组应该是一个实现细节,是可见的。因此,当可变参数具有泛型或者参数化类型时,会出现令人困惑的编译器警告。

  回顾第28项,不可具体化类型(non-reifiable)是其运行时表示的信息少于其编译时表示的类型,并且几乎所有泛型和参数化类型都是不可恢复的。如果方法声明其可变参数为不可具体化类型,则编译器会在声明上生成警告。如果调用一个包含可变参数的方法时,推断其可变参数类型是不可具体化的,那么编译器也会对调用生成警告。警告如下所示:

warning: [unchecked] Possible heap pollution from
    parameterized vararg type List<String>

  当参数化类型的变量引用不属于该类型的对象时,会发生堆污染(Heap pollution )[JLS, 4.12.2]。它会导致编译器自动生成的数据类型转换失败,违反泛型类型系统的基本保证。例如,考虑这个方法,这是第127页上代码片段的一种伪装变体:

// Mixing generics and varargs can violate type safety!
static void dangerous(List<String>... stringLists) {
   
    List<Integer> intList = List.of(42);
    Object[] objects = stringLists;
    objects[0] = intList; // Heap pollution
    String s = stringLists[0].get(0); // ClassCastException
}

  该方法没有可见(visible)的数据类型转换,但是在使用一个或多个参数调用时抛出ClassCastException。它的最后一行有一个由编译器生成的不可见转换。这种转换失败,说明类型安全性已经受到损害,并且在一般的可变参数数组中存储值是不安全的

  这个例子引出了一个有趣的问题:为什么使用泛型可变参数声明方法是合法的,而显示创建泛型数组是非法的?换句话说,为什么前面显示的方法只生成警告,而127页的代码片段生成错误?答案是,带有泛型或参数化类型的可变参数的方法在实践中非常有用,因此语言设计者选择了忍受这种不一致。事实上,Java库导出了好几个这样的方法,包括Arrays.asList(T... a), Collections.addAll(Collection<? super T> c, T... elements)EnumSet.of(E first, E... rest)。跟前面显示的危险方法不用,这些库方法都是类型安全的。

  在Java 7之前,使用泛型可变参数的方法的作者无法处理调用点(call sites)上的警告。这使得这些API使用起来不愉快。用户必须忍受警告,或者最好在没个调用点使用@SuppressWarnings("unchedked)注释消除警告(第27项)。这是乏味的,损害了可读性,并隐藏了标记真实问题的警告。

  在Java 7中,SafeVarargs注释已经添加到平台中,从而允许具有泛型可变参数的方法的作者可以自动压制客户端警告。本质上,SafeVarargs注释代表了该方法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值