首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Java SafeVarargs注解,是否存在标准或最佳实践?

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

Stack Overflow用户
提问于 2013-01-09 16:28:27
回答 2查看 29.2K关注 0票数 193

我最近遇到了java @SafeVarargs注释。在谷歌上搜索是什么让Java中的可变函数变得不安全,这让我相当困惑(堆中毒?擦除的类型?),所以我想知道一些事情:

  1. 是什么使得可变Java函数在@SafeVarargs意义上是不安全的(最好用一个深入的例子来解释)?
  2. 为什么这个注解要由程序员来决定?这难道不是编译器应该能够检查的吗?
  3. 为了确保他的函数确实是安全的,有没有什么标准是必须遵守的?如果没有,有什么最佳实践来确保这一点?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-01-10 14:43:43

1)在互联网和StackOverflow上有很多关于泛型和变量的特殊问题的例子。基本上,它是当你有一个类型参数类型的可变数量的参数时:

代码语言:javascript
复制
<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[] { ... } (这里的ObjectT的上限;如果上限不同,那就是Object),然后给你一个编译器警告。

那么,创建new Object[]而不是new T[]或其他什么有什么错呢?Java中的数组在运行时知道它们的组件类型。因此,传递的数组对象在运行时将具有错误的组件类型。

对于varargs最常见的用法,简单地迭代元素,这是没有问题的(您不关心数组的运行时类型),所以这是安全的:

代码语言:javascript
复制
@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

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

代码语言:javascript
复制
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
  • Unsafe:的实例这一事实,如果它依赖于数组是T[]

的实例这一事实

依赖于数组的运行时类型的内容包括:将其作为类型T[]返回,将其作为参数传递给T[]类型的参数,使用.getClass()获取数组类型,将其传递给依赖于数组的运行时类型的方法,如List.toArray()Arrays.copyOf()等。

2)我上面提到的区别太复杂了,很难自动区分。

票数 267
EN

Stack Overflow用户

发布于 2018-07-11 17:48:14

对于最佳实践,请考虑以下内容。

如果你有这个:

代码语言:javascript
复制
public <T> void doSomething(A a, B b, T... manyTs) {
    // Your code here
}

将其更改为:

代码语言:javascript
复制
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

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14231037

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档