接口方法@SafeVarargs

2022-09-03 15:36:19

在此代码中,

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
    }
}

由于注释,使用引用时不会发生警告。Type safety: A generic array of T is created for a varargs parameterCollector<T>@SafeVarargs

但是,通过接口访问该方法时,确实会出现警告。在接口方法上,是无效的(这是显而易见的,因为编译器不能对方法体中参数的用法执行任何检查)。CollectorIF<T>@SafeVarargs

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


答案 1

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

的另一个实现可能会误用该参数,使任何调用方容易受到奇怪的运行时行为的影响。你可以提出一个接口和非最终方法应该允许的情况(并要求实现/重写方法被类似的注释),但是目前Java开发人员有意识地决定不支持这种模式。CollectiorIFCollectorIF.addAll()@SafeVarargs

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));
}

然后,您的接口应该定义一个方法,让实现者完全避免通用 varargs 的粗糙世界。addAll(Iterable<T> values)


答案 2