Java üçlü operatörü ve if / else <JDK8 uyumluluğunda


113

Son zamanlarda Spring Framework'ün kaynak kodunu okuyorum. Anlayamadığım bir şey burada:

public Member getMember() {
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
    // as common type, with that new base class not available on older JDKs)
    if (this.method != null) {
        return this.method;
    }
    else {
        return this.constructor;
    }
}

Bu yöntem, sınıfın bir üyesidir org.springframework.core.MethodParameter. Yorumlar zor olsa da kodun anlaşılması kolaydır.

NOT: JDK 8 derleyicisini kullanırken bile JDK <8 uyumluluğunu koruyacak üçlü ifade yok (potansiyel java.lang.reflect.Executableolarak ortak tür olarak seçiliyor , bu yeni temel sınıf eski JDK'larda mevcut değil)

Üçlü ifade kullanmakla if...else...bu bağlamda yapı kullanmak arasındaki fark nedir ?

Yanıtlar:


103

İşlenenlerin türünü düşündüğünüzde, sorun daha belirgin hale gelir:

this.method != null ? this.method : this.constructor

her iki işlenenin de en özelleşmiş ortak tipine sahiptir, yani hem this.methodve hem de için ortak olan en özelleşmiş tip this.constructor.

Java 7'de bu java.lang.reflect.Member, ancak Java 8 sınıf kitaplığı java.lang.reflect.Executable, jenerikten daha özel olan yeni bir tür sunar Member. Bu nedenle, Java 8 sınıf kitaplığında, üçlü ifadenin sonuç türü Executableyerine Member.

Java 8 derleyicisinin bazı (yayın öncesi) sürümleri Executable, üçlü operatörü derlerken üretilen koda açık bir referans oluşturmuş gibi görünmektedir . Bu, bir sınıf yükünü ve dolayısıyla ClassNotFoundException<JDK 8 sınıf kitaplığıyla çalışırken çalışma zamanında bir tetikleyecektir , çünkü Executableyalnızca JDK ≥ 8 için mevcuttur.

Tagir Valeev'in bu yanıtta belirttiği gibi , bu aslında JDK 8'in yayın öncesi sürümlerindeki bir hatadır ve o zamandan beri düzeltilmiştir, bu nedenle hem if-elsegeçici çözüm hem de açıklayıcı yorum artık geçersizdir.

Ek not: Bu derleyici hatasının Java 8'den önce mevcut olduğu sonucuna varılabilir. Bununla birlikte, üçlü için OpenJDK 7 tarafından üretilen bayt kodu, OpenJDK 8 tarafından üretilen bayt koduyla aynıdır. Aslında, ifade çalışma zamanında tamamen bahsedilmeden gider, kod gerçekten sadece test, dallanma, yükleme, herhangi bir ek denetim yapılmadan geri dönme şeklindedir. Öyleyse, bunun (artık) bir sorun olmadığından ve aslında Java 8'in geliştirilmesi sırasında geçici bir sorun gibi göründüğünden emin olabilirsiniz.


1
O zaman JDK 1.8 ile derlenen kod JDK 1.7 üzerinde nasıl çalıştırılır? Daha düşük JDK sürümüyle derlenen kodun daha yüksek JDK sürümünde sorunsuz çalışabileceğini biliyordum. Tersi mi?
jddxf

1
@jddxf Uygun sınıf dosyası sürümünü belirttiğiniz ve sonraki sürümlerde bulunmayan herhangi bir işlevi kullanmadığınız sürece her şey yolunda. Ancak, bu durumda olduğu gibi bu tür bir kullanım dolaylı olarak gerçekleşirse, sorun çıkması kaçınılmazdır.
dhke

13
@jddxf, -kaynak / -hedef javac seçeneklerini kullan
Tagir Valeev 09

1
Özellikle dhke ve kapsamlı bir açıklama verdik Tagir Valeev, Hepinize teşekkürler
jddxf

30

Bu, resmi JDK-8 sürümünden neredeyse bir yıl önce, 3 Mayıs 2013'te oldukça eski bir işlemde tanıtıldı . Derleyici o zamanlar yoğun bir geliştirme altındaydı, bu nedenle bu tür uyumluluk sorunları ortaya çıkabilir. Sanırım, Spring ekibi JDK-8 yapısını test etti ve aslında derleyici problemleri olsalar bile problemleri çözmeye çalıştı. JDK-8'in resmi sürümüyle bu önemsiz hale geldi. Şimdi bu koddaki üçlü operatör beklendiği gibi iyi çalışıyor ( Executablederlenmiş .class dosyasında sınıfa referans yok ).

Şu anda benzer şeyler JDK-9'da görülüyor: JDK-8'de güzelce derlenebilen bazı kodlar JDK-9 javac ile başarısız oldu. Sanırım bu tür sorunların çoğu piyasaya sürülene kadar çözülecek.


2
+1. Peki, bu ilk derleyicide bir hata mıydı? ExecutableBelirtildiği yerde bu davranış, şartnamenin bazı yönlerini ihlal ediyor muydu? Yoksa Oracle, bu davranışı yine de spesifikasyona uyacak şekilde ve geriye dönük uyumluluğu bozmadan değiştirebileceklerini fark etti mi?
ruakh

2
@ruakh, sanırım hata buydu. Bayt kodunda (Java-8 veya önceki sürümlerde) arada yazmak için açıkça çevirmek tamamen gereksizdir Executable. Java-8'de ifade türü çıkarımı kavramı büyük ölçüde değişti ve bu bölüm tamamen yeniden yazıldı, bu nedenle ilk uygulamalarda hatalar olması şaşırtıcı değil.
Tagir Valeev

7

Başlıca fark, bir if elsebloğun bir ifade olmasıdır, oysa üçlü ( Java'da daha çok koşullu operatör olarak bilinir ) bir ifadedir .

Bir ifade , returnbazı kontrol yollarında arayan kişiye benzer şeyler yapabilir . Bir ifade atamada kullanılabilir:

int n = condition ? 3 : 2;

Bu nedenle, koşuldan sonraki üçlü ifadedeki iki ifadenin aynı türe zorlanabilmesi gerekir. Bu, Java'da özellikle otomatik kutulama ve otomatik referans atamada bazı tuhaf etkilere neden olabilir - bu, gönderilen kodunuzdaki yorumdan bahsediyor. Sizin durumunuzdaki ifadelerin zorlanması ( en özel tip olduğu için) bir java.lang.reflect.Executabletüre yönelik olacaktır ve bu, Java'nın eski sürümlerinde mevcut değildir.

Biçimsel olarak bir if else , kod ifadeye benziyorsa blok ve ifade benzeri ise bir üçlü kullanmalısınız.

Elbette, if elsebir lambda işlevi kullanıyorsanız , bir bloğun bir ifade gibi davranmasını sağlayabilirsiniz .


6

Üçlü bir ifadedeki dönüş değeri türü, Java 8'de açıklandığı gibi değişen üst sınıflardan etkilenir.

Neden bir oyuncu kadrosunun yazılmadığını anlamak zor.

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.