关于接口方法的@SafeVarargs

And*_*ter 12 java generics variadic-functions

在这段代码中,

package com.example;

interface CollectorIF<T> {
    // @SafeVarargs         // Error: @SafeVarargs annotation cannot be applied to non-final instance method addAll
    void addAll(T... values);   
}

class Collector<T> implements CollectorIF<T> {

    @SafeVarargs
    public final void addAll(T... values) {
    }
}

class Component<T> {

    public void compute(T value) {
        Collector<T> col1 = new Collector<>();
        col1.addAll(value);   // No warning

        CollectorIF<T> col2 = new Collector<>();
        col2.addAll(value);   // Type safety: A generic array of T is created for a varargs parameter
    }
}
Run Code Online (Sandbox Code Playgroud)

Type safety: A generic array of T is created for a varargs parameter使用时,不会出现警告Collector<T>的参考,由于@SafeVarargs注解.

但是,通过接口访问方法时发生警告CollectorIF<T>.在接口方法上,@SafeVarargs无效(这很明显,因为编译器无法对方法体中参数的使用情况进行任何检查).

通过接口访问方法时如何避免警告?

dim*_*414 9

没有办法避免这种警告,因为没有办法安全地定义具有通用varargs方法的接口.

另一个实现CollectiorIF可能会滥用参数,使任何调用者都CollectorIF.addAll()容易受到奇怪的运行时行为的攻击.您可以假设接口和非最终方法应该允许@SafeVarargs(并且要求类似地注释实现/重写方法),但是目前Java开发者有意识地决定不支持该模式.

JLS提供了更多背景知识:

在发生方法重写的情况下,注释不可用.注释继承仅适用于类(而不是方法,接口或构造函数),因此@ SafeVarargs样式的注释不能通过类或通过接口中的实例方法传递.

~JLS§9.6.4.7

与此同时,你有两个选择; 忽略警告或重构您的API.

重构您的API实际上可能正是您想要的,因为通用的vararg方法应该仅用作实际的,通用的通用实现的桥梁.不是将其定义为接口的一部分(因此需要所有实现实现它),而是将其作为静态实用程序方法提供,从而使接口的API更小,同时仍然为调用者提供使用varargs的灵活性.从Java 8开始,甚至可以在界面中定义实用方法.

@SafeVarargs
public static <T> void addAll(CollectorIF<T> collector, T... values) {
  collector.addAll(Arrays.asList(values));
}
Run Code Online (Sandbox Code Playgroud)

然后,您的界面应该定义一个addAll(Iterable<T> values)方法,让实现者完全避免泛型varargs的狡猾世界.