Varargs parametresi ile olası yığın kirliliği


433

Genel bir tür ile varargs kullanırken bu Java 7 ile meydana anlıyorum;

Ama sorum şu:

Eclipse "kullanımı potansiyel olarak yığını kirletebilir" derken tam olarak ne anlama geliyor?

Ve

Yeni @SafeVarargsek açıklama bunu nasıl önler?




Bunu editörümde görüyorum:Possible heap pollution from parameterized vararg type
Alexander Mills

Yanıtlar:


252

Yığın kirliliği teknik bir terimdir. İşaret ettikleri nesnenin bir süper tipi olmayan bir türe sahip referansları ifade eder.

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As

Bu "açıklanamaz" ClassCastExceptionlara yol açabilir .

// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0); 

@SafeVarargsbunu hiç engellemez. Bununla birlikte, yığını açıkça kirletmeyecek yöntemler vardır, derleyici bunu kanıtlayamaz. Daha önce, bu tür API'leri arayanlar, tamamen anlamsız olan ancak her çağrı sitesinde bastırılması gereken can sıkıcı uyarılar alacaktı. Artık API yazarı, bildirim sitesinde bir kez bastırabilir.

Ancak, yöntem gerçekten güvenli değilse , kullanıcılar artık uyarılmayacaktır.


2
Öyleyse, yığının kirlendiğini söylüyoruz çünkü türleri beklediğimiz gibi olmayan referanslar içeriyor mu? (
Örneğinizde


30
Bu cevap, yığın kirliliğinin ne olduğunun iyi bir açıklamasıdır, ancak varargs'ın neden belirli bir uyarıyı garanti etmek için neden bu kadar muhtemel olduğunu açıklamamaktadır.
Dolda2000

4
Ben de, kodumun bu sorunu içermediğinden nasıl emin olduğum konusunda bilgi eksik (örneğin, @SafeVarargs eklemek için yeterince sertleştiğini nasıl bilebilirim)
Daniel Alder

237

Beyan ettiğinde

public static <T> void foo(List<T>... bar) derleyici onu

public static <T> void foo(List<T>[] bar) sonra

public static void foo(List[] bar)

Tehlike daha sonra listeye yanlışlıkla yanlış değerler atamanız ve derleyicinin herhangi bir hatayı tetiklememesi nedeniyle ortaya çıkar. Örneğin, bir ise T, Stringaşağıdaki kod hatasız olarak derlenir ancak çalışma zamanında başarısız olur:

// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;

// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));

// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);

Bu tür güvenlik açıkları içermediğinden emin olmak için yöntemi incelediyseniz @SafeVarargs, uyarıyı bastırmak için ek açıklama ekleyebilirsiniz . Arayüzler için kullanın @SuppressWarnings("unchecked").

Bu hata mesajını alırsanız:

Varargs yöntemi, yeniden kullanılamayan varargs parametresinden yığın kirliliğine neden olabilir

ve kullanımınızın güvenli olduğundan eminseniz, @SuppressWarnings("varargs")bunun yerine kullanmalısınız . Bkz. @SafeVarargs bu yöntem için uygun bir ek açıklama mı? ve bu ikinci tür hatanın güzel bir açıklaması için https://stackoverflow.com/a/14252221/14731 adresini ziyaret edin .

Referanslar:


2
Daha iyi anladığımı düşünüyorum. Tehlikeyi varargs attığınızda ortaya çıkar Object[]. Yayın yapmadığınız sürece Object[], iyi olmanız gerektiği gibi geliyor.
djeikyb

3
Yapabileceğin bir aptal şey Örnek olarak: static <T> void bar(T...args) { ((Object[])args)[0] = "a"; }. Ve sonra ara bar(Arrays.asList(1,2));.
djeikyb

1
@djeikyb Eğer tehlike sadece eğer derlerse Object[]neden olmasaydı derleyici bir uyarı tetikler? Sonuçta bunu derleme zamanında kontrol etmek oldukça kolay olmalı (benzer bir imzayla başka bir işleve geçirmemem durumunda, bu durumda diğer işlev bir uyarı tetiklemelidir). Bunun gerçekten uyarının özü olduğuna inanmıyorum ("Yapmazsan güvende olursun") ve hala hangi durumda iyi olduğumu anlamıyorum.
Qw3ry

5
@djeikyb Aynı aptal şeyi parametreli varargs (örn. bar(Integer...args)) olmadan da yapabilirsiniz . Öyleyse bu uyarının anlamı ne?
Vasiliy Vlasov

3
@ VasiliyVlasov Bu sorun yalnızca parametreli varargs için geçerlidir. Aynı şeyi yazılmamış dizilerle yapmaya çalışırsanız, çalışma zamanı diziye yanlış tür eklemenizi engeller. Derleyici parametresi türü zamanında bilinmediği için çalışma zamanı yanlış davranışları önlemek mümkün olacağını uyarıyor demektir (aksine, diziler do zamanında onların olmayan jenerik elemanların tipini biliyorum).
Gili

8

@SafeVarargs bunun olmasını engellemez, ancak onu kullanan kodu derlerken derleyicinin daha katı olmasını zorunlu kılar.

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html bunu daha ayrıntılı olarak açıklıyor.

Yığın kirliliği, ClassCastExceptiongenel bir arabirimde bir işlem yaparken ve bildirilenden farklı bir tür içerdiğinde ortaya çıkar.


Kullanımıyla ilgili ek derleyici kısıtlamaları özellikle ilgili görünmemektedir.
Paul Bellora

6

Varargs kullandığınızda Object[], bağımsız değişkenleri tutmak için bir anahtar oluşturabilir .

Kaçış analizi nedeniyle, JIT bu dizi oluşturma işlemini optimize edebilir. (Bunu buldum birkaç kez biri) Uzak optimize edilmesi garanti değil, ama bellek profiler bir sorun görmedikçe ben bu konuda endişe olmaz.

AFAIK @SafeVarargsderleyici tarafından bir uyarıyı bastırır ve JIT'in davranış biçimini değiştirmez.


6
İlginç olsa da onun sorusuna gerçekten cevap vermiyor @SafeVarargs.
Paul Bellora

1
Hayır! Öbek kirliliği bu değildir. "Yığın kirliliği, parametreli tipteki bir değişken, parametreli tipte olmayan bir nesneyi ifade ettiğinde ortaya çıkar." Ref: docs.oracle.com/javase/tutorial/java/generics/…
Doradus

1

Bunun nedeni, varargs'ın parametrelenmemiş bir nesne dizisiyle çağrılma seçeneğini vermesidir. Dolayısıyla türünüz Liste <A> ... ise, List [] varargs olmayan türle de çağrılabilir.

İşte bir örnek:

public static void testCode(){
    List[] b = new List[1];
    test(b);
}

@SafeVarargs
public static void test(List<A>... a){
}

Gördüğünüz gibi Liste [] b herhangi bir tüketici türü içerebilir, ancak bu kod derlenir. Varargs kullanıyorsanız, sorun yoktur, ancak tür-silme - void testinden (List []) sonra yöntem tanımını kullanırsanız, derleyici şablon parametre türlerini denetlemez. @SafeVarargs bu uyarıyı engelleyecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.