Ore*_*ren 166 java variadic-functions java-7
我最近遇到了java @SafeVarargs注释.谷歌搜索使Java中的可变函数不安全的原因让我感到困惑(堆中毒?擦除类型?),所以我想知道一些事情:
是什么让可变参数Java函数在@SafeVarargs意义上不安全(最好以深入的例子的形式解释)?
为什么这个注释留给程序员自行决定?这不是编译器应该能够检查的东西吗?
是否有必须坚持一些标准,以确保他的功能确实安全?如果没有,确保它的最佳做法是什么?
new*_*cct 230
1)Internet和StackOverflow上有很多关于泛型和变量的特定问题的例子.基本上,当你有一个类型参数类型的可变数量的参数时:
<T> void foo(T... args);
Run Code Online (Sandbox Code Playgroud)
在Java中,varargs是一种语法糖,在编译时经历一个简单的"重写":类型的varargs参数X...
被转换为类型的参数X[]
; 每次调用这个varargs方法时,编译器都会收集varargs参数中的所有"变量参数",并创建一个类似的数组new X[] { ...(arguments go here)... }
.
当varargs类型具体时,这很有效String...
.当它是类型变量时T...
,它也适用于T
已知为该调用的具体类型.例如,如果上面的方法是一个类的一部分Foo<T>
,和你有一个Foo<String>
参考,然后调用foo
它会好起来的,因为我们知道T
是String
在代码中的那个点.
但是,当"值" T
是另一个类型参数时,它不起作用.在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
}
}
Run Code Online (Sandbox Code Playgroud)
但是,对于任何依赖于传递的数组的运行时组件类型的东西,它将是不安全的.这是一个不安全和崩溃的简单示例:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是,我们依靠的类型args
是T[]
为了回击T[]
.但实际上,运行时参数的类型不是实例T[]
.
3)如果你的方法有一个类型的参数T...
(其中T是任何类型参数),那么:
T
T[]
依赖于数组的运行时类型的事情包括:将其作为类型返回T[]
,将其作为参数传递给类型的参数T[]
,使用获取数组类型.getClass()
,将其传递给依赖于数组的运行时类型的方法,如List.toArray()
和Arrays.copyOf()
等
2)我上面提到的区别太复杂,不容易自动区分.
小智 6
对于最佳实践,请考虑这一点。
如果你有这个:
public <T> void doSomething(A a, B b, T... manyTs) {
// Your code here
}
Run Code Online (Sandbox Code Playgroud)
改成这样:
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
}
Run Code Online (Sandbox Code Playgroud)
我发现我通常只添加可变参数以方便我的调用者。对于我的内部实现来说,使用List<>
. 因此,为了支持Arrays.asList()
并确保我无法引入堆污染,这就是我所做的。
我知道这只能回答你的#3。newacct 对上面的 #1 和 #2 给出了很好的答案,我没有足够的声誉来把它作为评论。:P
归档时间: |
|
查看次数: |
24342 次 |
最近记录: |