Üçlü işleçle izin verilen bir int olarak null döndürme ancak if ifadesi


186

Aşağıdaki kod parçasındaki basit Java koduna bakalım:

public class Main {

    private int temp() {
        return true ? null : 0;
        // No compiler error - the compiler allows a return value of null
        // in a method signature that returns an int.
    }

    private int same() {
        if (true) {
            return null;
            // The same is not possible with if,
            // and causes a compile-time error - incompatible types.
        } else {
            return 0;
        }
    }

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.temp());
        System.out.println(m.same());
    }
}

Bu en basit Java kodunda, temp()işlevin dönüş türü olmasına rağmen yöntem , derleyici hatası vermez intve değeri null(ifade aracılığıyla return true ? null : 0;) döndürmeye çalışıyoruz . Derlendiğinde, bu açıkça çalışma zamanı istisnasına neden olur NullPointerException.

Ancak, üçlü işleci derleme zamanı hatası veren bir ifdeyimle ( same()yöntemde olduğu gibi ) temsil edersek aynı şey yanlış görünüyor ! Neden?


6
Ayrıca int foo = (true ? null : 0)ve new Integer(null)her ikisi de iyi derleme, ikincisi otomatik kutulamanın açık şeklidir.
Izkata

2
Soruna @Izkata burada beni derleyici autobox çalışıyor neden anlamak içindir nullüzere Integer... ... Bu sadece "şeyler çalışması yapmak" Bana "tahmin" veya gibi görünecektir
Wallace

1
... Huhm, Tamsayı yapıcısının (bulduğum belgelerin otomatik boks için kullanıldığını söylediği gibi) bir String'i argüman olarak almasına izin verildiğinden (burada null olabilir) bir cevabım olduğunu düşündüm. Ancak, aynı zamanda yapıcı bir boş geçirildikten sonra bir NumberFormatException atacak olan parseInt () yöntemine aynı şekilde hareket ettiğini söylüyorlar.
Izkata

3
@Izkata - Tamsayı için String argümanı c'tor bir otomatik boks oeprasyonu değildir. Bir Dize, bir Tamsayıya otomatik olarak kutulanamaz. (İşlev Integer foo() { return "1"; }derlenmeyecektir.)
Ted Hopp

5
Harika, üçlü operatör hakkında yeni şeyler öğrendim!
oksayt

Yanıtlar:


118

Derleyici , a'ya nullboş bir başvuru olarak yorumlar Integer, koşullu işleç için otomatik kutulama / kutudan çıkarma kurallarını uygular ( Java Dil Belirtimi, 15.25'te açıklandığı gibi ) ve mutlu bir şekilde devam eder. Bu, bir NullPointerExceptionçalışma zamanında oluşturur ve bunu deneyerek onaylayabilirsiniz.


Yayınladığınız Java Dil Spesifikasyonu'nun bağlantısı göz önüne alındığında, yukarıdaki soru söz konusu olduğunda hangi noktanın yürütüldüğünü düşünüyorsunuz? Sonuncusu (çünkü hala anlamaya çalışıyorum capture conversionve lub(T1,T2)) ?? Ayrıca, boş bir değere boks uygulamak gerçekten mümkün mü? Bu "tahmin" gibi olmaz mı ??
Marsellus Wallace

G @ Gevorg Boş gösterici olası her nesnenin geçerli bir göstergesidir, böylece orada kötü bir şey olamaz. Derleyici sadece null değerinin bir integer olduğunu varsayar ve bu sayı daha sonra int için otomatik kutuya gönderilebilir.
Voo

1
@Gevorg - nowaq'in yorumuna ve gönderisine vermiş olduğum yanıta bakın. Bence doğru maddeyi seçti. lub(T1,T2)T1 ve T2'nin tür hiyerarşisinde ortak olan en spesifik referans türüdür. (Her ikisi de en azından Object'i paylaşır, bu yüzden her zaman en spesifik referans türü vardır.)
Ted Hopp

8
@Gevorg - bir Tamsayı olarak kutulanmışnull değildir , bir Tamsayıya başvuru olarak yorumlanır (boş bir başvurudır, ancak bu bir sorun değildir). Null değerinden hiçbir Integer nesnesi oluşturulmaz, bu nedenle NumberFormatException için bir neden yoktur.
Ted Hopp

1
@Gevorg - Boks dönüşümü için kurallara bakar ve null(ilkel sayısal bir tür olmayan) kurallarına uygularsanız , geçerli fıkra " P başka bir türün değeri ise, boks dönüşümü kimlik dönüşümüne eşdeğerdir ". Yani dönüşümünü boks nulliçin Integerverim nullherhangi söz etmeksizin Integeryapıcı.
Ted Hopp

40

Java derleyicisi , örtük olarak dönüştürülebilen ve muhtemelen verilebilen true ? null : 0bir Integerifade olarak yorumlar .intNullPointerException

İkinci durumda, ifade nullözel taşımaktadır null türü See kodu yüzden, return nulltip uyuşmazlığı yapar.


2
Bunun otomatik boksla ilgili olduğunu varsayıyorum? Tahminen ilk dönüş olur değil Java 5 öncesinde, sağ derlemek?
Michael McGowan

@Michael, Eclipse'in uyum düzeyini 5 öncesi olarak ayarlarsanız durum böyle görünüyor.
Jonathon Faust

@Michael: Bu kesinlikle otomatik boks gibi görünüyor (Java için oldukça yeniyim ve daha tanımlanmış bir ifade yapamıyorum - üzgünüm).
Vlad

1
@Vlad nasıl derleyici ucu yukarı yorumlama olur true ? null : 0olarak Integer? 0Önce otomatik kutulama ile ??
Marsellus Wallace

1
@Gevorg: Buraya bakın : Aksi takdirde, ikinci ve üçüncü işlenenler sırasıyla S1 ve S2 türleridir. T1, S1'e boks dönüşümü uygulanmasından kaynaklanan tür olsun ve T2, S2'ye boks dönüşümü uygulanmasından kaynaklanan tür olsun. ve aşağıdaki metni.
Vlad

32

Aslında, bunların hepsi Java Dil Spesifikasyonunda açıklanmıştır .

Koşullu ifadenin türü aşağıdaki gibi belirlenir:

  • İkinci ve üçüncü işlenenler aynı türe sahipse (boş tip olabilir), bu koşullu ifadenin türüdür.

Bu nedenle, "null" (true ? null : 0)bir int türü alır ve sonra tamsayı otomatik kutuya alınır.

Bunu doğrulamak için böyle bir şey deneyin (true ? null : null)ve derleyici hatası alırsınız.


3
Ama kurallar bu hüküm geçerli değildir: İkinci ve üçüncü işlenenler do not aynı tür.
Ted Hopp

1
O zaman cevap aşağıdaki ifadede gibi görünüyor: Aksi takdirde, ikinci ve üçüncü işlenenler sırasıyla S1 ve S2 tipindedir. T1, S1'e boks dönüşümü uygulanmasından kaynaklanan tür olsun ve T2, S2'ye boks dönüşümü uygulanmasından kaynaklanan tür olsun. Koşullu ifadenin türü, lub'a (T1, T2) (§15.12.2.7) yakalama dönüşümü (§5.1.10) uygulanmasının sonucudur.
nowaq

Bence bu geçerli hüküm. Ardından int, işlevden bir NPE'ye neden olan bir değer döndürmek için otomatik kutulamayı uygulamaya çalışır .
Ted Hopp

@nowaq Bunu ben de düşündüm. Denersen Ancak, açıkça kutu nulliçin Integerbirliktenew Integer(null); bir alacağı "Let T1 S1 için boks dönüşüm uygulayarak elde edilen sonuçlar ... bu tip" NumberFormatExceptionve bu ... durum böyle değil
Wallace

@Gevorg Boks yaparken bir istisna oluştuğu için burada HERHANGİ bir sonuç alamadığımızı düşünürüm. Derleyici sadece tanımını izleyen bir kod üretmekle yükümlüdür - biz bitmeden önce sadece istisnayı alırız.
Voo

25

Durumunda ifaçıklamaya nullreferans bir şekilde tedavi edilmezse Integerbir katılan değildir, çünkü referans ekspresyonu bu gibi yorumlanmamalıdır zorlar. Bu nedenle hata derleme zamanında kolayca yakalanabilir çünkü daha açık bir tür hatasıdır.

Koşullu operatöre gelince, Java Dil Spesifikasyonu §15.25 “Koşullu Operatör ? :”, tip dönüşümünün nasıl uygulandığı kurallarında bunu güzel bir şekilde cevaplar:

  • İkinci ve üçüncü işlenenler aynı türe sahipse (boş tip olabilir), bu koşullu ifadenin türüdür.

    Uygulanmaz çünkü nulldeğildir int.

  • İkinci ve üçüncü işlenenlerden biri boolean türünde, diğeri ise Boolean türündeyse, koşullu ifadenin türü boolean olur.

    Ne çünkü geçerli değildir nullne de intbir booleanveya Boolean.

  • İkinci ve üçüncü işlenenlerden biri null tipindeyse ve diğerinin tipi bir referans tipiyse, koşullu ifadenin tipi o referans tipidir.

    Çünkü geçerli değildir nullboş tiptedir, ancak intbir başvuru türü değil.

  • Aksi takdirde, ikinci ve üçüncü işlenenler sayısal türlere dönüştürülebilir (§5.1.8) türlere sahipse, birkaç durum vardır: […]

    Uygulanır:null sayısal bir türe dönüştürülebilir olarak kabul edilir ve §5.1'de tanımlanır. 8 “Kutusundan Kutunun Açılması” a NullPointerException.

Otomatik 0kutulaması yapılmışsa Integer, derleyici Java Dil Belirtimi'nde açıklandığı gibi "üçlü operatör kuralları" nın son durumunu yürütür . Bu doğruysa, o zaman aynı kuralların 3. durumuna atlayacağına inanmak zor ve üçlü operatörün dönüş değerini referans türü (Integer) yapan bir referans türü. .
Wallace

@Gevorg - Üçlü operatörün geri döndüğüne inanmak neden zor Integer? Tam olarak olan bu; NPE, intişlevden bir döndürmek için ifade değerinin kutusundan çıkarılmaya çalışılarak üretilir . Geri dönmek için işlevi değiştirin ve sorunsuz bir Integerşekilde dönecektir null.
Ted Hopp

2
@TedHopp: Gevorg, cevabımın daha önceki bir düzeltmesine yanıt veriyordu; bu yanlıştı. Tutarsızlığı görmezden gelmelisiniz.
Jon Purdy

@JonPurdy "Sayısal bir türse bir türün sayısal bir türe dönüştürülebilir olduğu söylenir ya da kutuyu açarak dönüştürme ile sayısal bir türe dönüştürülebilen bir referans türüdür" ve nullbu kategoride olduğunu düşünmüyorum . Ayrıca, daha sonra "Aksi takdirde, ikili sayısal promosyon (§5.6.2) uygulanır ... İkili sayısal promosyonun dönüş tipini belirlemek için kutulama dönüşümü (§5.1.8) ..." adımını gerçekleştirdiğini unutmayın. Ancak kutudan çıkarma dönüşümü bir NPE oluşturur ve bu üçlü çalışma türünü belirlemeye çalışırken değil yalnızca çalışma zamanında olur. Hala kafam karıştı ..
Marsellus Wallace

@Gevorg: Kutudan çıkarma işlemi zamanında gerçekleşir. Bu null, türü varmış gibi davranılır int, ancak aslında buna eşdeğerdir throw new NullPointerException(), hepsi bu.
Jon Purdy

11

Akılda tutulması gereken ilk şey, Java üçlü operatörlerinin bir "tip" e sahip olması ve ikinci veya üçüncü parametrenin gerçek / gerçek türleri ne olursa olsun, derleyicinin belirleyeceği ve dikkate alacağı şeydir. Çeşitli faktörlere bağlı olarak, üçlü operatör tipi, çizimde gösterildiği gibi farklı şekillerde belirlenir. Java Dil Spesifikasyonu 15.26'da

Yukarıdaki soruda son durumu dikkate almalıyız:

Aksi takdirde, ikinci ve üçüncü işlenenler sırasıyla S1 ve S2 tipindedir. T1 , S1'e boks dönüşümü uygulanmasından kaynaklanan tür olsun ve T2 , S2'ye boks dönüşümü uygulanmasından kaynaklanan tür olsun . Koşullu ifadenin türü, lub'a (T1, T2) (§15.12.2.7) yakalama dönüşümü (§5.1.10) uygulanmasının sonucudur .

Bu, yakalama dönüşümünü (§5.1.10) ve en önemlisi lub'ta (T1, T2) bir kez baktığınızda en karmaşık durumdur .

Sade İngilizce'de ve aşırı basitleştirmeden sonra, süreci ikinci ve üçüncü parametrelerin "En Küçük Ortak Süper Sınıfı" (evet, LCM'yi düşünün) hesaplamak olarak tanımlayabiliriz. Bu bize üçlü operatör "tip" verecektir. Yine, az önce söylediğim şey aşırı basitleştirmedir (birden fazla ortak arabirimi uygulayan sınıfları düşünün).

Örneğin, aşağıdakileri denerseniz:

long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));

Ortaya çıkan koşullu ifadenin java.util.Datetürünün, Timestamp/ Timeçifti için "En Küçük Ortak Üst Sınıf" olduğu için fark edeceksiniz .

Yana nullhiçbir şeye autoboxed edilebilir "Asgari Ortak Üst sınıf" dir Integersınıf ve bu yukarıdaki koşullu ifadenin (üçlü operatör) dönüş tipi olacaktır. Dönüş değeri daha sonra tipte bir boş gösterici olacaktırInteger olacaktır ve üçlü operatör tarafından döndürülecek olan budur.

Çalışma zamanında, Java Sanal Makinesi kutusundan çıktığında Integera NullPointerExceptionatılır. JVM girişimleri işlevini çağırmak için Bunun nedeni null.intValue(), nullAutoboxing sonucudur.

Benim düşünceme göre (ve benim fikrim Java Dil Spesifikasyonunda olmadığı için birçok kişi yine de yanlış bulacak) derleyici sorunuzdaki ifadeyi değerlendirmede kötü bir iş çıkarıyor. true ? param1 : param2Derleyici yazdığınız göz önüne alındığında, derhal ilk parametrenin - null- döndürüleceğini ve bir derleyici hatası oluşturması gerektiğini belirlemelidir . Bu, yazarken while(true){} etc...ve derleyici döngünün altındaki koddan şikayet edip işaretlediğinde biraz benzer Unreachable Statements.

İkinci davanız oldukça basit ve bu cevap zaten çok uzun ...;)

DÜZELTME:

Başka bir analizden sonra, bir nulldeğerin herhangi bir şeye kutulanabileceğini / otomatik olarak kutulanabileceğini söylemenin yanlış olduğuna inanıyorum . Sınıf Integer hakkında konuşmak, açık boks new Integer(...)yapıcı ya da belki Integer.valueOf(int i);(Bu sürümü bir yerde buldum) çağırmak oluşur . Birincisi alamazdı NumberFormatException(ve bu gerçekleşmez), ikincisi mantıklı olmaz çünkü bir intolamaz null...


1
nullOP'ın orijinal kodunda kutulu değildir. Çalışma şekli şöyledir: derleyici null, bir Tamsayıya başvuru olduğunu varsayar . Üçlü ifade türleri için kuralları kullanarak tüm ifadenin bir Tamsayı ifadesi olduğuna karar verir. Daha sonra 1(koşulun değerlendirilmesi durumunda) otomatik kutuya kod oluşturur false. Yürütme sırasında, koşul trueifadeyi değerlendirir null. Bir intişlevden döndürmeye çalışırken null, kutusuzdur. Bu daha sonra bir NPE fırlatır. (Derleyici bunun çoğunu optimize edebilir.)
Ted Hopp

4

Aslında, birinci durumda ifade değerlendirilebilir, çünkü derleyici bunun bir olarak değerlendirilmesi gerektiğini bilir, Integerancak ikinci durumda dönüş değerinin ( null) tipi belirlenemez, bu yüzden derlenemez. Eğer yayın yaparsanız Integer, kod derlenir.


2
private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of unboxing then the returned value could be null
        //in other words I can say x could be null or new Integer(intValue) or a intValue
    }

    return (true ? null : 0);  //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
    //value can be Integer 
    // then null is accepted to be a variable (-refrence variable-) of Integer
}

0

Buna ne dersin:

public class ConditionalExpressionType {

    public static void main(String[] args) {

        String s = "";
        s += (true ? 1 : "") instanceof Integer;
        System.out.println(s);

        String t = "";
        t += (!true ? 1 : "") instanceof String;
        System.out.println(t);

    }

}

Çıktı doğrudur, doğrudur.

Tutulma rengi, koşullu ifadedeki 1 değerini otomatik kutulu olarak kodlar.

Tahminimce derleyici ifadenin dönüş türünü Object olarak görüyor.

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.