簡體   English   中英

java 警告:Varargs 方法可能會導致不可具體化的 varargs 參數造成堆污染

[英]java warning: Varargs method could cause heap pollution from non-reifiable varargs parameter

我在 JDK 1.8 上使用 IntelliJ IDEA 和 javac。 我有以下代碼:

class Test<T extends Throwable>
{
    @SafeVarargs
    final void varargsMethod( Collection<T>... varargs )
    {
        arrayMethod( varargs );
    }

    void arrayMethod( Collection<T>[] args )
    {
    }
}

IntelliJ IDEA 不會突出顯示上述代碼中的任何內容作為警告。 但是,在編譯時,“消息”視圖的“制作”選項卡中會出現以下行:

警告:(L, C) java: Varargs 方法可能會導致不可具體化的 varargs 參數 varargs 造成堆污染

注意#1:我已經指定了@SafeVarargs

注意 #2: Warning:(L,C)指向作為varargs傳遞給arrayMethod() varargs參數

假設我知道我在做什么,並假設我很確定不會有堆污染,或者我保證我不會以某種可能導致堆污染的時髦方式調用這個方法,我需要做什么如何抑制此警告消息?

注意:關於 varargs 方法的 stackoverflow 有很多問題,但似乎沒有解決這個特定問題。 事實上,整個interwebz 似乎對這個特定問題的回答相當糟糕。

我在這個問題上看到的所有答案在我看來都不是令人滿意的,所以我想我會嘗試一下。

這是我的看法:

  1. @SafeVarargs
  • 抑制警告: [unchecked] Possible heap pollution from parameterized vararg type Foo
  • 是方法契約的一部分,因此注釋具有運行時保留
  • 是對方法調用者的承諾,即該方法不會使用泛型 varargs 參數弄亂堆。
  1. @SuppressWarnings("varargs")
  • 抑制警告: [varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar
  • 對於方法的代碼發生,而不是方法的合同問題上治愈,因此為什么注解只有源代碼保留
  • 告訴編譯器它不需要擔心方法代碼調用的被調用方方法使用不可具體化的可變參數產生的數組搞亂堆。

因此,如果我對 OP 的原始代碼進行以下簡單的修改:

class Foo {
    static <T> void bar(final T... barArgs) {
        baz(barArgs);
    }
    static <T> void baz(final T[] bazArgs) { }
}

$ javac -Xlint:all Foo.java使用 Java 9.0.1 編譯器的輸出是:

Foo.java:2: warning: [unchecked] Possible heap pollution from parameterized vararg type T
    static <T> void bar(final T... barArgs) {
                                   ^
  where T is a type-variable:
    T extends Object declared in method <T>bar(T...)
1 warning

我可以通過將bar()標記為@SafeVarargs消除該警告。 這既使得警告消失,通過增加可變參數安全的方法合同,確保任何人誰呼吁bar不會有任何壓抑警告可變參數。

然而,它也讓 Java 編譯器更仔細地查看方法代碼本身 - 我想是為了驗證bar()可能違反我剛剛與@SafeVarargs簽訂的合同的簡單情況。 它看到bar()調用baz()傳入barArgs和數字,因為baz()由於類型擦除而采用Object[]baz()可能會barArgs ,從而導致bar()傳遞地執行它。

因此,我還需要將@SuppressWarnings("varargs")添加到bar()以使有關bar()代碼的警告消失。

事實上,您不應該以這種方式編寫代碼。 考慮以下示例:

import java.util.*;

class Test<T extends Throwable>
{
    @SafeVarargs
    @SuppressWarnings("varargs")
    final void varargsMethod( Collection<T>... varargs )
    {
        arrayMethod( varargs );
    }

    void arrayMethod( Collection<T>[] args )
    {
        Object[] array = args;
        array[1] = new Integer(1);
        //
        //ArrayList<Integer> list = new ArrayList<>();
        //list.add(new Integer(1));
        //array[1] = list;
    }

    public static void main(String[] args)
    {
        ArrayList<Exception> list1 = new ArrayList<>();
        ArrayList<Exception> list2 = new ArrayList<>();
        (new Test<Exception>()).varargsMethod(list1, list2);
    }
}

如果您運行代碼,您將看到一個 ArrayStoreException,因為您將一個 Integer 放入Collection<T>數組中。

但是,如果替換 array[1] = new Integer(1); 用三個注釋行(即把一個ArrayList<Integer>放入數組中),由於類型擦除,沒有拋出異常,也沒有出現編譯錯誤。

您想要一個Collection<Exception>數組,但現在它包含一個ArrayList<Integer> 這是非常危險的,因為您不會意識到存在問題。

需要一個額外的(看起來很多余) @SuppressWarnings( "varargs" )來抑制警告,如下所示:

@SafeVarargs
@SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
    arrayMethod( varargs );
}

假設我知道我在做什么

我拒絕假設這一點,因為這似乎非常錯誤。

您在這里遇到的情況是,因為泛型不可具體化,所以您要聲明一個泛型數組,這通常是不受歡迎的。 泛型和數組不能很好地混合,因為數組是協變的String[]是一個Object[]String是一個Object方式相同),而泛型是不變的List<String>不是List<Object> ,即使StringObject )。

如果你想要一個集合的集合......只需通過一個。 它比混合數組和泛型更安全。

final void varargsMethod(Collection<<Collection<? super T>> collections) { }

這可能解釋了原因。 我剛剛從 Effective Java 2nd Edition, Item 25 復制了它。希望它能有所幫助。

禁止創建通用數組可能很煩人。 例如,這意味着泛型類型通常不可能返回其元素類型的數組(但部分解決方案請參見條款 29)。 這也意味着在將可變參數方法(條目 42)與泛型類型結合使用時,您可能會得到令人困惑的警告。 這是因為每次調用 varargs 方法時,都會創建一個數組來保存 varargs 參數。 如果此數組的元素類型不可具體化,則會收到警告。 除了抑制它們(條款 24)以及避免在 API 中混合泛型和可變參數之外,您對這些警告幾乎無能為力。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM