Java SafeVarargs注解,是否存在标准或最佳实践?

nwnhqdif  于 2022-12-25  发布在  Java
关注(0)|答案(3)|浏览(99)

我最近遇到了java @SafeVarargs注解,在Google上搜索是什么使得Java中的变量函数不安全让我很困惑(堆中毒?擦除类型?),所以我想知道一些事情:
1.从@SafeVarargs的意义上说,是什么使得可变Java函数不安全(最好以深入示例的形式解释)?
1.为什么这个注解要留给程序员来判断呢?这难道不是编译器应该能够检查的吗?
1.是否有一些必须遵守的标准,以确保他的功能确实是varags安全的?如果没有,什么是最好的做法,以确保它?

rqmkfv5c

rqmkfv5c1#

1)在Internet和StackOverflow上有很多关于泛型和varargs的例子,基本上,这是当你有一个类型参数类型的变量数量时的情况:

<T> void foo(T... args);

在Java中,varargs是一种语法糖,在编译时会经历简单的“重写”:将类型X...的varargs参数转换为类型X[]的参数;每次调用这个varargs方法时,编译器都会收集varargs参数中的所有“变量参数”,并创建一个类似于new X[] { ...(arguments go here)... }的数组。
当varargs类型是String...这样的具体类型时,这很有效。当它是T...这样的类型变量时,当T已知是该调用的具体类型时,它也有效。例如,如果上面的方法是Foo<T>类的一部分,并且您有一个Foo<String>引用,那么在它上面调用foo是可以的,因为我们知道在代码的那个点上TString
但是,当T的“值”是另一个类型参数时,它就不起作用了,在Java中,不可能创建一个类型参数组件类型(new T[] { ... })的数组,所以Java改为使用new Object[] { ... }(这里ObjectT的上界;如果上限不同,则应该是that而不是Object),然后向您提供编译器警告。
那么创建new Object[]而不是new T[]或其他什么东西有什么问题呢?Java中的数组在运行时知道它们的组件类型,因此,传递的数组对象在运行时会有错误的组件类型。
对于varargs最常见的用法,简单地迭代元素,这是没有问题的(你不关心数组的运行时类型),所以这是安全的:

@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

但是,对于依赖于传递数组的运行时组件类型的任何东西,它都将是不安全的。下面是一个不安全和崩溃的简单示例:

class UnSafeVarargs
{
  static <T> T[] asArray(T... args) {
    return args;
  }

  static <T> T[] arrayOfTwo(T a, T b) {
    return asArray(a, b);
  }

  public static void main(String[] args) {
    String[] bar = arrayOfTwo("hi", "mom");
  }
}

这里的问题是,我们依赖于args的类型是T[],以便将其返回为T[],但实际上参数在运行时的类型不是T[]的示例。
3)如果方法具有T...类型的参数(其中T是任何类型参数),则:

  • 安全:如果您的方法仅依赖于数组元素是T的示例这一事实
  • 不安全:如果它取决于数组是T[]的示例这一事实

取决于数组运行时类型的内容包括:以T[]类型返回,将其作为实参传递给T[]类型的参数,使用.getClass()获取数组类型,将其传递给依赖于数组的运行时类型的方法,如List.toArray()Arrays.copyOf()等。
2)我上面提到的区别太复杂了,很难自动区分。

vfh0ocws

vfh0ocws2#

要获得最佳实践,请考虑以下内容。
如果你有这个:

public <T> void doSomething(A a, B b, T... manyTs) {
    // Your code here
}

将其更改为:

public <T> void doSomething(A a, B b, T... manyTs) {
    doSomething(a, b, Arrays.asList(manyTs));
}

private <T> void doSomething(A a, B b, List<T> manyTs) {
    // Your code here
}

我发现我通常只添加varargs来使调用者更方便。对于我的内部实现来说,使用List<>几乎总是更方便。因此,为了在Arrays.asList()上搭载并确保没有办法引入堆污染,这就是我所做的。
我知道这只回答了你的第三个问题。newacct已经为上面的第一个和第二个问题给出了很好的答案,我没有足够的声誉来留下评论。:P

eyh26e7m

eyh26e7m3#

@SafeVarargs用于指示方法不会导致堆污染。
堆污染是当我们在泛型数组中混合不同的参数化类型时。
例如:

public static <T> T[] unsafe(T... elements) {
        return elements; 
    }

    Object [] listOfItems =  unsafe("some value", 34, new ArrayList<>());
    String stringValue = (String) listOfItems[0]; // some value
    String intValue = (String) listOfItems[1]; // ClassCastException

如您所见,如果我们不使用类型进行猜测,这样的实现很容易导致ClassCastException

相关问题