Java SafeVarargs ek açıklaması, standart veya en iyi uygulama var mı?


175

Geçenlerde java @SafeVarargsnotuna rastladım . Java'da değişken bir işlevi yapan şey için googling yapmak beni oldukça karışık bıraktı (yığın zehirlenmesi? Silinen tipler?), Bu yüzden birkaç şey bilmek istiyorum:

  1. Değişken bir Java işlevini bu @SafeVarargsanlamda güvensiz yapan nedir (tercihen derinlemesine bir örnek şeklinde açıklanır)?

  2. Bu ek açıklama neden programcının takdirine bırakılmıştır? Bu derleyicinin kontrol etmesi gereken bir şey değil mi?

  3. İşlevinin gerçekten çok güvenli olduğundan emin olmak için uyulması gereken bir standart var mı? Değilse, bunu sağlamak için en iyi uygulamalar nelerdir?


1
JavaDoc'ta örnek (ve açıklama) gördünüz mü ?
jlordo

2
Üçüncü soru için, bir uygulama her zaman birinci elemanı ve başkalarını sahip olmaktır: retType myMethod(Arg first, Arg... others). Belirtmezseniz first, boş bir diziye izin verilir ve aynı ada sahip, aynı döndürme türüne sahip, bağımsız değişken içermeyen bir yönteme sahip olabilirsiniz; bu, JVM'nin hangi yöntemin çağrılması gerektiğini belirlemekte zorlanacağı anlamına gelir.
fge

4
@jlordo yaptım, ama neden bir varargs işlevi dışında bu durumu kolayca çoğaltabilirsiniz varargs bağlamda verilen anlamıyorum (bu doğrulandı, beklenen tür güvenlik uyarıları ve çalışma zamanında hataları ile derler) ..
Oren

Yanıtlar:


244

1) İnternette ve StackOverflow'da jenerikler ve varargs ile ilgili belirli bir konu hakkında birçok örnek vardır. Temel olarak, bir type-parametre türünde değişken sayıda bağımsız değişkeniniz olduğunda:

<T> void foo(T... args);

Java'da varargs, derleme zamanında basit bir "yeniden yazma" geçiren sözdizimsel bir şekerdir: türdeki bir varargs parametresi, türdeki X...bir parametreye dönüştürülür X[]; ve bu varargs yöntemine her çağrı yapıldığında, derleyici varargs parametresine giren tüm "değişken argümanlarını" toplar ve aynen bir dizi oluşturur new X[] { ...(arguments go here)... }.

Varargs tipi beton gibi olduğunda bu iyi çalışır String.... Bu bir tür değişkeni T...olduğunda T, o çağrı için somut bir tür olduğu da bilinir. Örneğin, yukarıdaki yöntem bir sınıfın parçasıysa Foo<T>ve bir Foo<String>referansınız varsa, kodda bu noktada olduğunu foobildiğimiz Tiçin çağırmak uygun olacaktır String.

Ancak, "değeri" Tbaşka bir tür parametresi olduğunda çalışmaz . Java'da, tür parametresi bileşen türünde ( new T[] { ... }) bir dizi oluşturmak imkansızdır . Java bunun yerine kullanır new Object[] { ... }(burada Objectüst sınır T; üst sınır farklı bir şey olsaydı, bunun yerine olurdu Object) ve sonra derleyici uyarısı verir.

Peki ya yaratma new Object[]yerine yanlış olan new T[]ne var? Java dizileri çalışma zamanında bileşen türlerini bilir. Böylece, iletilen dizi nesnesi çalışma zamanında yanlış bileşen türüne sahip olacaktır.

Muhtemelen en yaygın varargs kullanımı için, sadece öğeler üzerinde tekrarlamak için, bu sorun değildir (dizinin çalışma zamanı türünü umursamazsınız), bu yüzden güvenlidir:

@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

Ancak, geçirilen dizinin çalışma zamanı bileşen türüne bağlı olan herhangi bir şey için güvenli olmaz. Güvensiz ve çöken bir şeyin basit bir örneği:

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");
  }
}

Buradaki sorun biz türüne bağlıdır olmasıdır argsolmak T[]olarak dönebilmek için T[]. Ancak aslında çalışma sırasındaki argümanın türü bunun bir örneği değildir T[].

3) Yönteminizde bir tür bağımsız değişken varsa T...(burada T herhangi bir tür parametresidir), o zaman:

  • Güvenli: Metodunuz sadece dizinin elemanlarının T
  • Güvensiz: Dizinin, T[]

Dizinin çalışma zamanı türüne bağlı olan şeyler şunlardır: tür olarak döndürme, tür T[]parametresine bağımsız değişken olarak geçirme T[], dizi türünü kullanma .getClass(), dizinin çalışma zamanı türüne bağlı yöntemlere geçirme, List.toArray()ve Arrays.copyOf(), vb.

2) Yukarıda bahsettiğim ayrım otomatik olarak kolayca ayırt edilemeyecek kadar karmaşık.


7
şimdi her şey mantıklı. Teşekkürler. sadece seni tamamen anladığımı görmek için, diyelim ki bir sınıfımız var FOO<T extends Something>, bir varargs yönteminin T [] sini bir dizi olarak döndürmek güvenli olur Somthingmu?
Oren

30
Kullanmak için (muhtemelen) her zaman doğru olan bir durumun @SafeVarargs, dizi ile yaptığınız tek şeyin zaten bu kadar açıklamalı başka bir yönteme geçirilmesi olduğunu belirtmek gerekir (örneğin: sık sık kendimi vararg yöntemleri yazarken buluyorum kullanarak bir liste için kendi bağımsız değişken dönüştürmek için mevcut Arrays.asList(...)ve bir yönteme geçmek, böyle bir durum, her zaman ek not edilebilir @SafeVarargs, çünkü Arrays.asList) ek açıklama vardır.
Jules

3
@newacct Java 7'de durumun bu olup olmadığını bilmiyorum, ancak @SafeVarargsyöntem olmadığı sürece Java 8 izin vermez staticveya finalaksi takdirde güvenli olmayan bir şeyle türetilmiş bir sınıfta geçersiz kılınabilir.
Andy

3
Java 9'da da privategeçersiz kılınamayan yöntemler için de izin verilir .
Dave L.

4

En iyi uygulamalar için bunu düşünün.

Eğer buna sahipseniz:

public <T> void doSomething(A a, B b, T... manyTs) {
    // Your code here
}

Bunu şu şekilde değiştirin:

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
}

Ben genellikle sadece arayanlar için daha uygun hale getirmek için varargs eklemek buldum. Dahili uygulamamın kullanımı neredeyse her zaman daha uygun olacaktır List<>. Öyleyse domuzcukları geri almak Arrays.asList()ve Yığın Kirliliğini tanıtmamın hiçbir yolu olmadığından emin olmak için yaptığım şey bu.

Bunun sadece # 3'üne cevap verdiğini biliyorum. newacct yukarıdaki # 1 ve # 2 için harika bir cevap verdi ve bunu bir yorum olarak bırakmak için yeterli üne sahip değilim. : P

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.