是否可以解决“为可变参数参数创建 T 的通用数组”编译器警告?

Posted

技术标签:

【中文标题】是否可以解决“为可变参数参数创建 T 的通用数组”编译器警告?【英文标题】:Is it possible to solve the "A generic array of T is created for a varargs parameter" compiler warning? 【发布时间】:2010-11-29 12:51:36 【问题描述】:

这是相关代码的简化版本,一个泛型类使用另一个具有泛型类型参数的类,并且需要将其中一个泛型类型传递给具有可变参数参数的方法:

class Assembler<X, Y> 
    void assemble(X container, Y... args)  ... 


class Component<T> 
    void useAssembler(T something) 

        Assembler<String, T> assembler = new Assembler<String, T>();

        //generates warning:
        // Type safety : A generic array of T is
        // created for a varargs parameter
        assembler.assemble("hello", something);
    

是否有任何正确的方法可以将泛型参数传递给可变参数方法而不会遇到此警告?

当然是这样的

assembler.assemble("hello", new T[]  something );

不起作用,因为您无法创建通用数组。

【问题讨论】:

一个奇怪的。看起来编译器应该能够在这里确保完全的类型安全。 Angelika Langer 的 Java 泛型常见问题解答中的相关条目:angelikalanger.com/GenericsFAQ/FAQSections/… 【参考方案1】:

在Java 6中,除了添加@SuppressWarnings("unchecked"),我不这么认为。

这个bug report 有更多信息,但归结为编译器不喜欢泛型类型的数组。

【讨论】:

忘了说我想避免@SuppressWarnings("unchecked")。那个错误报告给了我一点希望! 正如 Joshua Bloch 在 Effective Java 中所说:“不要混合泛型和数组。” 那么,隐含地:不要使用泛型的可变参数!对...将可变参数映射到 Array 而不是 Collection 的决定将永远困扰 java。干得好,戈斯林先生。【参考方案2】:

在 Java 7 中,使用 @SafeVarargs 注释方法声明

【讨论】:

提到的 Project Coin 功能现在可用 - 请参阅 Java 7 中的 @SafeVarargs。 Bob 提案中的备选 E 很诱人。 Java 8 似乎使用 @SafeVarargs 而不是 @SuppressWarnings("varargs") 不幸的是,该方法必须是 final 才能使 @SafeVarargs 起作用,因此如果您使用抽象方法(就像我一样),它将返回编译器错误:“@SafeVarargs 注释不能应用于非最终实例方法”。【参考方案3】:

如果您追求流畅类型的界面,您可以尝试构建器模式。不像可变参数那样简洁,但它是类型安全的。

静态泛型类型方法可以在使用构建器时消除一些样板,同时保持类型安全。

建造者

public class ArgBuilder<T> implements Iterable<T> 

    private final List<T> args = new ArrayList<T>();

    public ArgBuilder<T> and(T arg) 
        args.add(arg);
        return this;
    

    @Override
    public Iterator<T> iterator() 
        return args.iterator();
    

    public static <T> ArgBuilder<T> with(T firstArgument) 
        return new ArgBuilder<T>().and(firstArgument);
    

使用它

import static com.example.ArgBuilder.*;

public class VarargsTest 

    public static void main(String[] args) 
        doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz"));
        // or
        doSomething(with("foo").and("bar").and("baz"));
    

    static void doSomething(Iterable<String> args) 
        for (String arg : args) 
            System.out.println(arg);
        
    

【讨论】:

组合的力量。我比可变参数更喜欢这个,它更具表现力。 @ChristopherPerry 好吧,您还必须考虑您的代码库。底层的Collection(在这种情况下是ArrayList)被强加给调用者,而他们可能知道LinkedList 更合适,或者是不可变数组本身(例如来自OP 问题的可变参数)。在非专业用例中,这可能是合适的,但只是指出这在某种程度上也是一个限制,具体取决于围绕此的代码和您的需求。【参考方案4】:

在可变参数方法调用中将参数显式转换为 Object 将使编译器满意,而无需诉诸 @SuppressWarnings。

public static <T> List<T> list( final T... items )

    return Arrays.asList( items );


// This will produce a warning.
list( "1", 2, new BigDecimal( "3.5" ) )

// This will not produce a warning.
list( (Object) "1", (Object) 2, (Object) new BigDecimal( "3.5" ) )

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient.
list( (Object) "1", 2, new BigDecimal( "3.5" ) )

我认为这里的问题是编译器需要弄清楚要创建什么具体类型的数组。如果方法不是通用的,编译器可以使用方法中的类型信息。如果该方法是通用的,它会尝试根据调用时使用的参数来确定数组类型。如果参数类型是同质的,那么这项任务很容易。如果它们有所不同,我认为编译器会尝试过于聪明并创建一个联合类型的泛型数组。然后它觉得有必要警告你。一个更简单的解决方案是在无法更好地缩小类型范围时创建 Object[]。上述解决方案正是这样做的。

为了更好地理解这一点,与下面的 list2 方法相比,尝试对上面的 list 方法的调用。

public static List<Object> list2( final Object... items )

    return Arrays.asList( items );

【讨论】:

这也有效,例如: Iterator> it = Arrays.asList((Object)t).iterator; if(if,hasNext()) class= it.next().getClass(); 例如从未知类型的数组中获取对象的类。【参考方案5】:

这是一个很容易解决的问题:使用List&lt;T&gt;

应避免引用类型的数组。

在当前版本的 Java (1.7) 中,您可以使用 @SafeVargs 标记方法,这将删除来自调用者的警告。不过要小心,如果没有遗留数组,你仍然会更好。

List.of() 提供了一种相对简洁的方式来编写(不可修改的)List,直到 Java 获得适当的文字表示。

另请参阅Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods 技术说明。

【讨论】:

这对于可变参数是不可避免的,不是吗? 应该避免引用数组,因为它们是遗留问题并且与泛型的关系很差(例如,它们的子类型规则不正确)。 List 更有用,而数组是一个原始的实现细节。 有一个 JDK7 的提议,允许在 varargs 方法声明而不是它的使用上进行警告抑制。 这完全忽略了作者问题的一个重要方面——可变参数确实创建了一个数组,并产生了这个警告。 我同意@Tom Hawtin - tackline。有关详细信息,请参阅 Bloch > Item 25: Prefer lists to arrays。【参考方案6】:

自 Java 7 起,您可以将 @SafeVarargs 添加到方法中,并且不必在客户端代码上进行注释。

class Assembler<X, Y> 

    @SafeVarargs
    final void assemble(X container, Y... args) 
        //has to be final...
    

【讨论】:

【参考方案7】:

您可以重载方法。 这并不能解决您的问题,但可以最大限度地减少警告的数量(是的,这是一个 hack!)

class Assembler<X, Y> 
  void assemble(X container, Y a1)  ... 
  void assemble(X container, Y a1, Y a2)  ... 
  void assemble(X container, Y a1, Y a2, Y a3)  ... 
  void assemble(X container, Y a1, Y a2, Y a3, Y a4)  ... 
  void assemble(X container, Y... args)  ... 

【讨论】:

Ew。这正是 varargs 应该防止的那种黑客攻击。 这可能是一个有效的方法,例如,看看 Guava 的ImmutableSet.of。【参考方案8】:

当使用泛型类型的数组时,我不得不传递对泛型类型的引用。有了它,我实际上可以使用 java.lang.reflect.Array 编写通用代码。

http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html

【讨论】:

我不使用泛型类型的数组,不是直接使用,只是泛型类型的可变参数。

以上是关于是否可以解决“为可变参数参数创建 T 的通用数组”编译器警告?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以创建具有不同金额的自动支付解决方案?

是否有任何解决方法可以避免 SFSafariViewController 隐藏固定位置的标题?

是否可以像在 Matlab 中一样在 Julia 中嵌套 tic() 和 toc() ?如果不是,那么解决方法是啥?

是否可以让 ASP.NET Core 解决方案包含具有不同目标框架的项目?

是否可以从 p2 repo 解决 maven 插件中的依赖关系

是否有一种解决方法可以使名称以数字开头的 CSS 类有效? [复制]