我最近遇到了java @SafeVarargs
注释。在谷歌上搜索是什么让Java中的可变函数变得不安全,这让我相当困惑(堆中毒?擦除的类型?),所以我想知道一些事情:
@SafeVarargs
意义上是不安全的(最好用一个深入的例子来解释)?发布于 2013-01-10 14:43:43
1)在互联网和StackOverflow上有很多关于泛型和变量的特殊问题的例子。基本上,它是当你有一个类型参数类型的可变数量的参数时:
<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
是可以的,因为我们知道T
在代码中的那一点是String
。
但是,当T
的"value“是另一个类型参数时,它不起作用。在Java语言中,不可能创建类型参数组件类型(new T[] { ... }
)的数组。所以Java改为使用new Object[] { ... }
(这里的Object
是T
的上限;如果上限不同,那就是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");
}
}
这里的问题是,我们依赖于要为T[]
的args
的类型,以便将其作为T[]
返回。但实际上,运行时的参数类型不是T[]
的实例。
3)如果你的方法有一个T...
类型的参数(其中T是任何类型的参数),那么:
T
T[]
的实例这一事实
依赖于数组的运行时类型的内容包括:将其作为类型T[]
返回,将其作为参数传递给T[]
类型的参数,使用.getClass()
获取数组类型,将其传递给依赖于数组的运行时类型的方法,如List.toArray()
和Arrays.copyOf()
等。
2)我上面提到的区别太复杂了,很难自动区分。
发布于 2018-07-11 17:48:14
对于最佳实践,请考虑以下内容。
如果你有这个:
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()
并确保我不会引入堆污染,这就是我所做的。
我知道这只回答了你的#3。newacct对上面的#1和#2给出了一个很好的答案,而我没有足够的名气来评论这篇文章。:P
https://stackoverflow.com/questions/14231037
复制相似问题