Java varargs yöntemini tek boş bağımsız değişkenle çağırmak?


97

Bir vararg Java yöntemim varsa foo(Object ...arg)ve ararsam foo(null, null), hem arg[0]ve hem arg[1]de nulls var. Ama ararsam foo(null), argkendisi boştur. Bu neden oluyor?

Nasıl çağırmalıdır fooböyle foo.length == 1 && foo[0] == nullolduğunu true?

Yanıtlar:


95

Sorun şu ki, boş değeri kullandığınızda, Java ne tür olması gerektiğini bilmiyor. Boş bir Nesne olabilir veya boş bir Nesne dizisi olabilir. Tek bir argüman için ikincisini varsayar.

İki seçeneğiniz var. Boş değeri açıkça Object'e çevirin veya türü kesin belirlenmiş bir değişken kullanarak yöntemi çağırın. Aşağıdaki örneğe bakın:

public class Temp{
   public static void main(String[] args){
      foo("a", "b", "c");
      foo(null, null);
      foo((Object)null);
      Object bar = null;
      foo(bar);
   }

   private static void foo(Object...args) {
      System.out.println("foo called, args: " + asList(args));
   }
}

Çıktı:

foo called, args: [a, b, c]
foo called, args: [null, null]
foo called, args: [null]
foo called, args: [null]

asListÖrnekte yöntemi ve amacını listelemiş olsaydınız başkalarına yardımcı olabilirdi .
Arun Kumar

5
@ArunKumar asList(), java.util.Arrayssınıftan statik bir içe aktarmadır . Açık olduğunu varsaydım. Şimdi bunu düşünmeme rağmen, Arrays.toString()Listeye dönüştürülmesinin tek sebebi, güzel basılabilmesi için muhtemelen kullanmalıydım.
Mike Deck

24

Aşağıdakileri yapmak için açık bir kadroya ihtiyacınız var Object:

foo((Object) null);

Aksi takdirde, bağımsız değişken, varargların temsil ettiği tüm dizi olarak kabul edilir.


Pek değil, yazıma bakın.
Buhake Sindi

Oops, aynı burada ... üzgünüm, gece yarısı yağı
Buhake Sindi

6

Bunu açıklayan bir Test Senaryosu:

Vararg alma yöntemi bildirimine sahip Java kodu (statiktir):

public class JavaReceiver {
    public static String receive(String... x) {
        String res = ((x == null) ? "null" : ("an array of size " + x.length));
        return "received 'x' is " + res;
    }
}

Bu Java kodu (bir JUnit4 test durumu) yukarıdakileri çağırır (test senaryosunu hiçbir şeyi test etmemek, sadece bazı çıktılar oluşturmak için kullanıyoruz):

import org.junit.Test;

public class JavaSender {

    @Test
    public void sendNothing() {
        System.out.println("sendNothing(): " + JavaReceiver.receive());
    }

    @Test
    public void sendNullWithNoCast() {
        System.out.println("sendNullWithNoCast(): " + JavaReceiver.receive(null));
    }

    @Test
    public void sendNullWithCastToString() {
        System.out.println("sendNullWithCastToString(): " + JavaReceiver.receive((String)null));
    }

    @Test
    public void sendNullWithCastToArray() {
        System.out.println("sendNullWithCastToArray(): " + JavaReceiver.receive((String[])null));
    }

    @Test
    public void sendOneValue() {
        System.out.println("sendOneValue(): " + JavaReceiver.receive("a"));
    }

    @Test
    public void sendThreeValues() {
        System.out.println("sendThreeValues(): " + JavaReceiver.receive("a", "b", "c"));
    }

    @Test
    public void sendArray() {
        System.out.println("sendArray(): " + JavaReceiver.receive(new String[]{"a", "b", "c"}));
    }
}

Bunu bir JUnit testi olarak çalıştırmak:

sendNothing (): alınan 'x', 0 boyutunda bir dizidir
sendNullWithNoCast (): alınan 'x' boş
sendNullWithCastToString (): alınan 'x' 1 boyutunda bir dizidir
sendNullWithCastToArray (): alınan 'x' boş
sendOneValue (): alınan 'x' 1 boyutunda bir dizidir
sendThreeValues ​​(): alınan 'x' 3 boyutlu bir dizidir
sendArray (): alınan 'x' 3 boyutlu bir dizidir

Bunu daha ilginç hale getirmek için receive()Groovy 2.1.2'deki fonksiyonu çağıralım ve ne olacağını görelim. Sonuçların aynı olmadığı ortaya çıktı! Yine de bu bir hata olabilir.

import org.junit.Test

class GroovySender {

    @Test
    void sendNothing() {
        System.out << "sendNothing(): " << JavaReceiver.receive() << "\n"
    }

    @Test
    void sendNullWithNoCast() {
        System.out << "sendNullWithNoCast(): " << JavaReceiver.receive(null) << "\n"
    }

    @Test
    void sendNullWithCastToString() {
        System.out << "sendNullWithCastToString(): " << JavaReceiver.receive((String)null) << "\n"
    }

    @Test
    void sendNullWithCastToArray() {
        System.out << "sendNullWithCastToArray(): " << JavaReceiver.receive((String[])null) << "\n"
    }

    @Test
    void sendOneValue() {
        System.out << "sendOneValue(): " + JavaReceiver.receive("a") << "\n"
    }

    @Test
    void sendThreeValues() {
        System.out << "sendThreeValues(): " + JavaReceiver.receive("a", "b", "c") << "\n"
    }

    @Test
    void sendArray() {
        System.out << "sendArray(): " + JavaReceiver.receive( ["a", "b", "c"] as String[] ) << "\n"
    }

}

Bunu bir JUnit testi olarak çalıştırmak, Java farkı kalın harflerle vurgulanarak aşağıdakileri verir.

sendNothing (): alınan 'x', 0 boyutunda bir dizidir
sendNullWithNoCast (): alınan 'x' boş
sendNullWithCastToString (): alınan 'x' boş
sendNullWithCastToArray (): alınan 'x' boş
sendOneValue (): alınan 'x' 1 boyutunda bir dizidir
sendThreeValues ​​(): alınan 'x' 3 boyutlu bir dizidir
sendArray (): alınan 'x' 3 boyutlu bir dizidir

bu aynı zamanda değişken işlevin parametresiz olarak çağrılmasını da dikkate alır
bebbo

3

Bunun nedeni, bir varargs yönteminin bir dizi dizi öğesi yerine gerçek bir dizi ile çağrılabilmesidir. nullKendi başına belirsiz nullolanı sağladığınızda , bir Object[]. Döküm nullTo Objectbu çözecektir.


1

tercih ederim

foo(new Object[0]);

Boş işaretçi istisnalarını önlemek için.

Umarım yardımcı olur.


14
Peki, bu bir vararg yöntemiyse, neden böyle demiyorsunuz foo()?
BrainStorm.exe

@ BrainStorm.exe cevabınız cevap olarak işaretlenmelidir. Teşekkürler.
MDP

1

Yöntem aşırı yük çözümü için sıralama şu şekildedir : ( https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2 ):

  1. İlk aşama, kutulama veya kutudan çıkarma dönüşümüne veya değişken arity yönteminin kullanılmasına izin vermeden aşırı yük çözümlemesini gerçekleştirir. Bu aşamada uygulanabilir bir yöntem bulunmazsa, işlem ikinci aşamaya devam eder.

    Bu, Java SE 5.0'dan önce Java programlama dilinde geçerli olan çağrıların değişken arity yöntemlerinin, örtük kutulamanın ve / veya kutunun kaldırılmasının bir sonucu olarak belirsiz olarak değerlendirilmemesini garanti eder. Bununla birlikte, bir değişken arity yönteminin (§8.4.1) bildirimi, belirli bir yöntem yöntem çağrı ifadesi için seçilen yöntemi değiştirebilir, çünkü bir değişken arity yöntemi, birinci aşamada sabit bir yöntem olarak ele alınır. Örneğin, m (Object) 'i zaten bildiren bir sınıfta m (Object ...) bildirmek, m (Object)' in artık m (Object [] gibi bazı çağrı ifadeleri (m (null) gibi) için seçilmemesine neden olur. ) daha spesifiktir.

  2. İkinci aşama, kutulamaya ve kutudan çıkarmaya izin verirken aşırı yük çözümü gerçekleştirir, ancak yine de değişken arity yöntemi çağrısının kullanılmasını engeller. Bu aşamada uygulanabilir bir yöntem bulunmazsa, işlem üçüncü aşamaya devam eder.

    Bu, sabit çözüm yöntemi çağrısı yoluyla uygulanabilirse, bir yöntemin asla değişken arity yöntem çağrısı yoluyla seçilmemesini sağlar.

  3. Üçüncü aşama, aşırı yüklemenin değişken arite yöntemleri, kutulama ve kutudan çıkarma ile birleştirilmesine izin verir.

foo(null)ilk aşamada foo(Object... arg)ile eşleşir arg = null. arg[0] = nullasla gerçekleşmeyen üçüncü aşama olacaktır.

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.