Java 8 arayüz yöntemlerinde neden “son” a izin verilmiyor?


335

Java 8'in en kullanışlı özelliklerinden biri defaultarabirimlerdeki yeni yöntemlerdir. Bunların tanıtılmasının esasen iki nedeni vardır (başkaları da olabilir):

Bir API tasarımcının bakış açısından, örneğin arayüz yöntemleri üzerinde diğer değiştiricileri kullanmak isterdim final. Bu, uygulama yöntemleri eklenirken, uygulama sınıflarında "yanlışlıkla" geçersiz kılmaları önlerken yararlı olur:

interface Sender {

    // Convenience method to send an empty message
    default final void send() {
        send(null);
    }

    // Implementations should only implement this method
    void send(String message);
}

Yukarıdaki Senderbir sınıf zaten yaygın bir uygulamadır :

abstract class Sender {

    // Convenience method to send an empty message
    final void send() {
        send(null);
    }

    // Implementations should only implement this method
    abstract void send(String message);
}

Şimdi defaultve finalaçıkça anahtar kelimelerle çelişiyor, ancak varsayılan anahtar kelimenin kendisi kesinlikle gerekli olmayacaktı , bu yüzden bu çelişkinin "vücutla sınıf yöntemleri" (sadece yöntemler) ve "arayüz arasındaki ince farkları yansıtmak için kasıtlı olduğunu varsayıyorum. gövdeli yöntemler " (varsayılan yöntemler), yani henüz anlamadığım farklılıklar.

Bir zamanlar, Brian Goetz'e atıfta bulunarak , arayüz yöntemleri gibi staticve finalarayüz yöntemleri üzerindeki değiştiricilere yönelik destek henüz tam olarak araştırılmadı :

Diğer kısım, son yöntemler, özel yöntemler, korunan yöntemler, statik yöntemler vb.Gibi arayüzlerde sınıf oluşturma araçlarını ne kadar destekleyeceğimizdir. Cevap şu: henüz bilmiyoruz.

2011'in sonundaki o zamandan beri, açıkça, staticarayüzlerdeki yöntemlere destek eklendi. Açıkçası, bu JDK kütüphanelerine kendileri gibi çok değer kattı Comparator.comparing().

Soru:

Java 8 arayüzlerine hiçbir zaman neden olmadı final(ve ayrıca static final)?


10
Islak bir battaniye olduğum için üzgünüm, ancak başlıkta ifade edilen sorunun SO açısından cevaplanmasının tek yolu Brian Goetz veya JSR Expert grubundan bir tekliftir. BG'nin bir kamuoyu tartışması istediğini anlıyorum, ancak 'öncelikle görüşe dayalı' olduğu için SO şartlarına tam olarak aykırı olan şey bu. Bana öyle geliyor ki sorumluluklar burada kayboluyor. Tartışmayı teşvik etmek ve rasyonelleri ortaya çıkarmak Uzman Grubun işi ve daha geniş Java Topluluğu Süreci'nin işidir. SO değil. Bu yüzden 'öncelikle görüşe dayalı' olarak kapanmaya oy veriyorum.
Lorne Marquis

2
Hepimizin bildiği gibi, finalbir yöntemin geçersiz kılınmasını önler ve arabirimlerden miras alınan yöntemleri nasıl geçersiz kılmanız GEREKİR, onu sonlandırmanın neden mantıklı olduğunu görmüyorum. Yöntemin bir kez geçersiz kılınmasından SONRA son olduğunu belirtmedikçe .. Bu durumda, belki qas zorlukları var mı? Bu hakkı anlamıyorsam, lütfen bana kmow izin ver. İlginç görünüyor
Vince Emigh

22
@EJP: "Eğer bunu yapabilirsem, sen de yapabilirsin ve kendi soruna cevap verirsin ki bu da sormayı gerektirmezdi." Bu forumdaki hemen hemen tüm sorular için geçerlidir, değil mi? Google'a her zaman 5 saat boyunca bir konu başlatabilirim ve sonra herkesin eşit derecede zor bir şekilde öğrenmesi gereken bir şey öğrenebilirim. Ya da birisinin gelecekteki herkesin (şimdiye kadar 12 upvotes ve 8 yıldız dahil) kazanç sağlayabileceği daha iyi bir cevap vermesi için birkaç dakika daha bekleriz, çünkü SO Google'da çok iyi referanslandırılmıştır. Yani evet. Bu soru olabilir mükemmel yüzden Q & A forma uygun.
Lukas Eder

5
@VinceEmigh " ... arayüzlerden devralınan yöntemleri nasıl geçersiz kılmanız GEREKTİĞİNİ görüyorsunuz ... " Java 8'de bu doğru değil. Java 8, arayüzlerde yöntemler uygulamanıza izin verir, yani bunları uygulamada uygulamak zorunda değilsiniz sınıflar. Burada, finaluygulama sınıflarının bir arabirim yönteminin varsayılan uygulamasını geçersiz kılmasının önlenmesinde bir kullanımı olacaktır.
awksp

28
@EJP asla bilemezsiniz: Brian Goetz cevap verebilir !
assylias

Yanıtlar:


419

Bu soru, bir dereceye kadar, Java 8 arayüz yöntemlerinde “senkronize edilmesine” izin verilmesinin nedeni nedir?

Varsayılan yöntemler hakkında anlaşılması gereken en önemli şey, birincil tasarım hedefinin "arayüzleri (vasat) özelliklere dönüştürmek değil" arayüz evrimi olmasıdır . İkisi arasında bir miktar örtüşme olsa da, ikincisine engel olmadığı yerde ikincisine uyum sağlamaya çalıştık, bu sorular en iyi bu ışıkta bakıldığında anlaşılır. (Sınıf yöntemlerinin de olan arayüz yöntemlerinden farklı olacak, hiçbir önemi nedir arayüz yöntemleri çoklu miras edilebileceği gerçeğine dayanarak, niyet.)

Varsayılan yöntemin temel fikri şudur: varsayılan uygulamaya sahip bir arabirim yöntemidir ve türetilmiş bir sınıf daha spesifik bir uygulama sağlayabilir. Tasarım merkezi arayüz gelişimi olduğu için, varsayılan yöntemlerin arayüzlere eklenebilmesi kritik bir tasarım hedefiydi , gerçeğe göre kaynak uyumlu ve ikili uyumlu bir şekilde hedefiydi.

"Niçin varsayılan temerrüt yöntemleri değil" için çok basit bir cevap o zaman vücut sadece varsayılan uygulama değil, tek uygulama olacaktır. Bu biraz çok basit bir cevap olsa da, sorunun zaten tartışmalı bir yöne yöneldiğine dair bir ipucu veriyor.

Son arayüz yöntemlerinin sorgulanabilir olmasının bir başka nedeni, uygulayıcılar için imkansız problemler yaratmalarıdır. Örneğin, varsayalım:

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

Burada her şey yolunda; Cdevralır foo()dan A. Şimdi varsayalım B, foovarsayılan olarak bir yönteme sahip olarak değiştirildi :

interface B { 
    default void foo() { ... }
}

Şimdi, yeniden derlemeye gittiğimizde C, derleyici bize hangi davranışın miras alınacağını bilmediğini söyleyecektir foo(), bu yüzden Conu geçersiz kılmalı (ve A.super.foo()aynı davranışı korumak istiyorsa temsilci seçmeyi seçebilir ). Bonun varsayılan yapmıştı finalve Ayazarı kontrolü altında değildir C? Şimdi Ctelafisi mümkün değil; geçersiz kılmadan derleyemez foo(), ancak foo()son halinde geçersiz kılınamaz B.

Bu sadece bir örnektir, ancak mesele, yöntemlerin nihai olmasının, sadece davranışa katkıda bulunan ve çoğaltılabilen arayüzlerden ziyade, tek miras sınıfları (genellikle davranışa çift devlet) davranışı daha anlamlı bir araçtır. miras. "Nihai uygulayıcıya başka hangi arabirimlerin karıştırılabileceği" hakkında bir fikir vermek çok zordur ve bir arabirim yönteminin nihai olmasına izin vermek bu sorunlara neden olabilir (ve arabirimi yazan kişiye değil, uygulamaya çalışan zayıf kullanıcı.)

Onlara izin vermemenin bir başka nedeni de, ne demek istediklerini kastetmemeleridir. Varsayılan uygulama yalnızca sınıf (veya üst sınıfları) yöntemin bir bildirimini (somut veya özet) sağlamadığında dikkate alınır. Varsayılan bir yöntem son halindeydi ancak yöntemi bir üst sınıf zaten uyguladıysa, varsayılan yok sayılır; bu, varsayılan yazarın son olarak bildirirken beklediği şey olmayabilir. (Bu kalıtım davranışı, varsayılan yöntemler için tasarım merkezinin bir yansımasıdır - arayüz gelişimi. Mevcut uygulamalara zaten var olan arayüzlere varsayılan bir yöntem (veya mevcut bir arayüz yöntemine varsayılan bir uygulama) eklemek mümkün olmalıdır. arayüzü uygulayan mevcut sınıfların davranışı,


88
Yeni dil özellikleriyle ilgili soruları yanıtladığınızı görmek harika! Yeni özellikleri nasıl kullanmamız gerektiğine karar verirken tasarımın amacı ve ayrıntıları hakkında açıklama yapmak çok yararlı . Tasarıma katılan diğer insanlar SO'ya katkıda bulunuyor mu - yoksa bunu kendiniz mi yapıyorsunuz? Cevaplarınızı java-8 etiketi altında takip edeceğim - aynı şeyi yapan başka millet olup olmadığını öğrenmek istiyorum, böylece onları da takip edebilirim.
2014'te

10
@Shorn Stuart Marks java-8 etiketinde aktif. Jeremy Manson geçmişte yayınlamıştı. Ayrıca Joshua Bloch'un mesajlarını gördüğümü hatırlıyorum ama şu anda bulamıyorum.
assylias

1
Varsayılan arabirim yöntemlerini, C # 'ın kötü tasarlanmış ve oldukça çirkin uygulanan genişletme yöntemleriyle gerçekleştirdiğini gerçekleştirmenin çok daha zarif bir yolu olarak bulduğunuz için tebrikler. Bu soruya gelince, bir isim çatışmasının çözülmesinin imkansızlığı hakkındaki cevap sorunu çözmektedir, ancak sağlanan nedenlerin geri kalanı ikna edici olmayan filolojidir. (Bir arabirim yönteminin nihai olmasını istiyorsanız, o zaman kimsenin benimkinden farklı bir uygulama sağlamasına izin vermemek için kendi çok iyi nedenlerim olması gerektiğini varsaymalısınız.)
Mike Nakis

1
Yine de, ad çakışması çözümlemesi, uygulanan yöntemin bildirimine uygulanan belirli arabirimin adını ekleyerek C # 'da nasıl yapıldığına benzer bir şekilde elde edilmiş olabilir. Yani, tüm bunlardan eve aldığım, varsayılan arayüz yöntemlerinin Java'da nihai olamayacağı, çünkü sözdiziminde iyi hissetmediğiniz ek değişiklikler gerektirecekti. (Belki de çok keskin mi?)
Mike

18
@Trying Abstract sınıfları hala durumu tanıtmanın veya temel Object yöntemlerini uygulamanın tek yoludur. Varsayılan yöntemler saf davranış içindir ; soyut sınıflar devletle birleşmiş davranışlar içindir.
Brian Goetz

43

Lambda posta listesinde bununla ilgili birçok tartışma var . Tüm bu şeyler hakkında çok fazla tartışma içerdiği düşünülenlerden biri şudur: Çeşitli arayüz yönteminin görünürlüğü (Nihai savunuculardı) .

Bu tartışmada, orijinal sorunun yazarı Talden, sorunuza çok benzer bir şey soruyor:

Tüm arayüz üyelerini halka duyurma kararı gerçekten talihsiz bir karardı. İç tasarımda herhangi bir arayüz kullanımının uygulama özel detaylarını ortaya çıkarması büyüktür.

Dile bazı nüansları kırma ya da uyumsuzluk eklemeden düzeltmek zor. Bu büyüklüğün ve potansiyel inceliğin uyumluluk kırılması mantıksız görünecekti, bu nedenle mevcut kodu kırmayan bir çözümün bulunması gerekiyor.

'Paket' anahtar kelimesi bir erişim belirteci olarak yeniden kullanılabilir olabilir. Bir arabirimde bir belirtecinin olmaması, genel erişim anlamına gelir ve bir sınıfta belirtecin olmaması, paket erişimi anlamına gelir. Bir arabirimde hangi belirteçlerin anlamlı olmadığı belirsizdir - özellikle geliştiriciler üzerindeki bilgi yükünü en aza indirmek için, erişim belirleyicilerin varsa sınıfta ve arabirimde aynı anlama geldiğinden emin olmalıyız.

Varsayılan yöntemlerin yokluğunda, bir arabirimdeki bir üyenin belirticisinin en azından arabirimin kendisi kadar görünür olması gerektiğini (böylece arabirimin tüm görünür bağlamlarda gerçekte uygulanabileceğini) - varsayılan olmayan yöntemlerle çok kesin.

Bunun olası bir kapsam içi tartışma olup olmadığı konusunda net bir iletişim var mı? Değilse, başka bir yerde tutulmalıdır.

Sonunda Brian Goetz'in cevabı şuydu:

Evet, bu zaten araştırılıyor.

Bununla birlikte, bazı gerçekçi beklentiler belirlememe izin verin - dil / VM özelliklerinin uzun bir teslim süresi var, hatta bu kadar önemsiz görünen olanlar bile. Java SE 8 için yeni dil özelliği fikirleri sunma zamanı hemen hemen geçti.

Yani, büyük olasılıkla asla uygulanmadı çünkü asla kapsamın bir parçası değildi. Düşünülmesi asla zaman içinde önerilmemiştir.

Başka bir ateşli tartışmada son defans yöntemleri hakkında konuyla ilgili, Brian tekrar söyledi :

Ve tam olarak ne istediğini aldın. Tam olarak bu özelliğin eklediği şey - davranışların çoklu mirası. Tabii ki insanların onları özellik olarak kullanacağını biliyoruz. Ve sundukları kalıtım modelinin, insanların çok çeşitli durumlarda iyi sonuçlar alabilecek kadar basit ve temiz olmasını sağlamak için çok çalıştık. Aynı zamanda, onları basit ve temiz bir şekilde çalışan şeyin sınırlarının ötesine itmemeyi seçtik ve bu, bazı durumlarda "aw, yeterince ileri gitmediniz" reaksiyonlarına yol açtı. Ama gerçekten, bu ipliğin çoğu camın sadece% 98 dolu olduğu için homurdanıyor gibi görünüyor. Bunu% 98 alacağım ve devam edeceğim!

Bu benim teorimin tasarımının kapsamının ya da bir parçası olmadığı yönünü güçlendiriyor. Yaptıkları şey, API evrimi sorunları ile başa çıkmak için yeterli işlevsellik sağlamaktı.


4
Görüyorum ki, bu sabah googling odyssey'e eski ismi "defans yöntemleri" eklemeliydim. Bunu kazmak için +1.
Marco13

1
Tarihi gerçekleri kazmak güzel. Sonuçlarınız resmi yanıtla
Lukas Eder

Neden uyumluluğunu geriye doğru kıracağını anlamıyorum. finale daha önce izin verilmedi. şimdi özel izin veriyorlar ama korunmuyorlar. myeh ... private uygulanamaz ... başka bir arabirimi genişleten bir arabirim bir üst arabirimin parçalarını uygulayabilir, ancak aşırı yükleme yeteneğini başkalarına göstermesi gerekir ...
mmm

17

Bulmak ve @EJP yorumlarına belirtilen resons için, "" cevabını tespit etmek zor olacaktır: kesin bir cevap verebilir dünya üzerindeki yaklaşık 2 (+/- 2) insan vardır hiç . Ve kuşkusuz, cevap "Son varsayılan yöntemleri desteklemek, dahili çağrı çözümleme mekanizmalarını yeniden yapılandırma çabası değerinde görünmüyordu" gibi bir şey olabilir. Bu elbette spekülasyon, ancak en azından OpenJDK posta listesindeki bu Bildirim (iki kişiden biri tarafından) gibi ince kanıtlarla destekleniyor :

"Sanırım" son varsayılan "yöntemlere izin verildiyse, kullanıcı tarafından görülebilen çağrı arabirimine özel dahili çağrılardan yeniden yazılması gerekebilir."

ve bu gibi önemsiz gerçekler şu anda bir yöntem olduğunda, bir yöntem olduğunda (gerçekten) son bir yöntem olarak kabul edilmez default, şu anda Method :: is_final_method OpenJDK içinde yöntemle.

Dahası, gerçekten "otoriter" bilgiler, aşırı web aramaları ve taahhüt günlüklerini okuyarak bile bulmak zordur. Bu invokeinterfaceyönteme karşılık gelen talimat ve sınıf yöntemi çağrıları ile arayüz yöntemi çağrıları çözümlenmesi sırasında potansiyel belirsizlikler ile ilgili olabileceğini düşündüm invokevirtual: invokevirtualTalimat için, basit bir vtable arama olabilir, çünkü yöntem ya devralınmalıdır veya bir sınıf tarafından doğrudan uygulanır. Bunun aksine, bir invokeinterfaceçağrının, bu çağrının gerçekte hangi arayüze atıfta bulunduğunu bulmak için ilgili çağrı sitesini incelemesi gerekir (bu, HotSpot Wiki'nin InterfaceCalls sayfasında daha ayrıntılı olarak açıklanmaktadır ). Ancak,final yöntemler ya yerleştirilen alamadım vtablehiç veya varolan girdileri değiştirmek vtable (bkz klassVtable.cpp. Hattı 333) ve benzer şekilde, varsayılan yöntemler vtable içindeki mevcut girişlerin yerini almaktadır (bkz. klassVtable.cpp, Satır 202 ). Dolayısıyla, asıl sebep (ve dolayısıyla cevap), (oldukça karmaşık) yöntem çağrısı çözümleme mekanizmalarının içinde daha derine gizlenmelidir, ancak belki de bu referanslar, sadece gerçek cevabı türetmeyi başaranlar için yararlı olarak kabul edilecektir. Bundan.


İlginç fikir için teşekkürler. John Rose'un eseri ilginç bir iz. Yine de @EJP ile aynı fikirde değilim. Karşı bir örnek olarak, Peter Lawrey'in çok ilginç, çok benzer tarzı bir soruya cevabımı kontrol edin . İse tarihi gerçekleri kazmak mümkün ve her zaman Yığın taşması burada onları bulmak için sevindim (başka nerede?). Tabii ki, cevabınız hala spekülatif ve JVM uygulama detaylarının JLS'nin bir veya başka bir şekilde yazılmasının nihai nedeni (cinayet amaçlı) olduğuna ikna olmadım ...
Lukas Eder

@LukasEder Tabii, sorular bu tür şunlardır Q & A kalıbı içine ilginç ve IMHO uyum. Sanırım burada tartışmaya neden olan iki önemli nokta var: Birincisi "sebebi" istemeniz. Bu çoğu durumda resmi olarak belgelenmeyebilir. Örneğin int,
JLS'de

... ... İkincisi, sadece "birkaç düzine" den ... "yaklaşık olarak sıfır" a cevap vermeye cesaret eden insanların sayısını azaltan "yazar alıntıları" istemenizdir. Bunun dışında, JVM'nin gelişiminin ve JLS'nin yazımının nasıl iç içe geçtiğinden emin değilim, yani gelişim JLS'ye yazılanları ne kadar etkilediğinde, ama ... Burada herhangi bir spekülasyondan kaçınacağım; -)
Marco13

1
Hala davamı dinlendiriyorum. Kim diğer soruma cevap verdiğini görün :-) Şimdi sonsuza kadar yetkili bir cevapla açık olacak, burada Stack Overflow'da neden yöntemleri desteklememeye karar synchronizedverildi default.
Lukas Eder

3
@LukasEder Anlıyorum, burada da aynı. Bunu kim bekleyebilirdi? Sebepler, özellikle bu finalsoru için oldukça ikna edicidir ve kimsenin benzer örnekleri düşünmediği (ya da belki de bazılarının bu örnekler hakkında düşünmediği, ancak cevap verecek kadar yetkin hissetmediği) biraz alçakgönüllü. Şimdi (üzgünüm, bunu yapmak zorundayım :) son kelime söylenir.
Marco13

4

Ben finalbir convienience arayüzü yöntemi üzerinde belirtmek için gerekli olduğunu düşünmek olmaz , ben olsa kabul edebilirim yararlı , ama görünüşe göre maliyetleri faydaları fazla kilolu.

Her iki durumda da yapmanız gereken şey, yöntemin tam olarak ne olduğunu ve yapmasına izin verilmediğini gösteren varsayılan yöntem için uygun javadoc yazmaktır. Bu şekilde, arabirimi uygulayan sınıfların hiçbir garantisi olmasa da uygulamayı değiştirmesine izin verilmez.

Herkes Collectionarayüze yapışan bir yazı yazabilir ve daha sonra tamamen sezgisel olan yöntemlerde bir şeyler yapabilir, kapsamlı birim testleri yazmaktan başka, kendinizi bundan korumanın bir yolu yoktur.


2
Javadoc sözleşmeleri sorumda listelediğim somut örnek için geçerli bir geçici çözüm , ancak soru gerçekten kolaylık arayüzü yöntemi kullanım örneği ile ilgili değil. Soru, finalJava 8 interfaceyöntemlerinde izin verilmemesinin nedeninin geçerli bir nedeni ile ilgilidir . Yetersiz maliyet / fayda oranı iyi bir adaydır, ancak şimdiye kadar bu bir spekülasyon.
Lukas Eder

0

Biz eklemek defaultiç Metodumuzun için anahtar kelime interfacebiz sınıf uzanan bildiğinde interfaceolabilir veya olmayabilir overridebizim uygulanmasını. Ancak, herhangi bir uygulayıcı sınıfın geçersiz kılmasını istemediğimiz bir yöntem eklemek istersek ne olur? Bizim için iki seçenek mevcuttu:

  1. Bir default finalyöntem ekleyin .
  2. Bir staticyöntem ekleyin .

Şimdi Java , tam olarak aynı yöntem adı ve imzası olan bir yöntem olan classiki veya daha fazla interfacesuyguladığımız takdirde, defaultyani yinelenen olduklarını söylüyorsa, bu yöntemin sınıfımızda bir uygulama sağlamamız gerektiğini söylüyor. Şimdi default finalyöntem durumunda, bir uygulama sağlayamayız ve sıkışıp kaldık. Bu yüzden finalanahtar kelime arayüzlerde kullanılmaz.

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.