Ne zaman kullanılır: Java 8+ arabirimi varsayılan yöntemi, soyut yöntemi karşılaştırması


540

Java 8, Varsayılan Yöntemler adı verilen arabirimlerde yöntemlerin varsayılan olarak uygulanmasına izin verir .

Ne zaman interface default methodbir abstract class(ile abstract method(s)) yerine, bu tür kullanacağım arasında kafam karıştı .

Öyleyse varsayılan yöntemlerle arayüz ne zaman kullanılmalı ve soyut sınıf (soyut yöntem (ler) ile) ne zaman kullanılmalıdır? Soyut sınıflar bu senaryoda hala faydalı mı?


38
Belki de soyut sınıfta iken arayüzlerde alanlar, özel yöntemler vb. Hala olamaz.
sp00m

2
Daha önce bu konuyu merak ediyordum, şimdi açıkım. @Narendra Pathai. Her ikisi de şüphelerim olduğu için, aynı konuyla ilgili olarak sizin tarafınızdan sorulan başka bir konunun bağlantısını eklemek istiyorum. stackoverflow.com/questions/19998309/…
Ashutosh Ranjan

Bu konuda güzel bir yazı bulabilirsiniz: blog.codefx.org/java/everything-about-default-methods
Fabri

Temel sınıfın durumu olsa bile, bazen bir baz sınıfını arabirim olarak kodlayabilirsiniz. Sadece arayüz, devlet için ayarlayıcıları ve alıcıları tanımlamalı ve somut sınıflar bunları uygulamalı ve alanı tanımlamalıdır. Bu konuda bir kısıtlama, soyut bir sınıfta, fasulye özelliğinin özel veya korunmuş olabileceğidir. Arayüzlerde sadece genel yöntemler vardır. Bu nedenle, soyut bir temel sınıf kullanmanızın bir nedeni, sınıflarınızın özel veya korunması gereken bir özelliğe sahip olup olmadığıdır.
DaBlick

@DaBlick Bir HashMap aracılığıyla bir arabirimdeki durum sorununu çözemez misiniz. Örn: int a, b, String c içeren bir Foo sınıfı istiyorsanız. ve durumlarının olmasını istiyorsanız, Foo nesnesinin bir HashMap </ * adı oluşturun * / String, / * alanların haritası * / Hashmap </ * ada özgü Alan * / String, / * alan değeri * / Object >> map . Teorik Foo sınıfını "başlatmak" istediğinizde, alanların bir HashMap <String, Object> fields.put ("a", new) olduğu map.put (nameOfFoo, fields) yöntemini içeren instantiate (String nameOfFoo) yöntemine sahipsiniz. int ( "5")); fields.put ("b", yeni int ("6")); fields.put ("c", "blah"));
George Xavier

Yanıtlar:


307

Soyut sınıflarda varsayılan yöntem uygulamalarından (özel durum gibi) çok daha fazlası vardır, ancak Java 8'den itibaren, her ikisinden birini seçerseniz default, arabirimdeki defender (aka. ) Yöntemiyle gitmelisiniz .

Varsayılan yöntemin kısıtlaması, belirli bir uygulamanın durumuna başvurmadan yalnızca diğer arabirim yöntemlerine çağrılar açısından uygulanabilmesidir. Yani ana kullanım durumu daha üst düzey ve kolaylık yöntemleridir.

Bu yeni özellik hakkında iyi olan şey, kolaylık yöntemleri için soyut bir sınıf kullanmaya zorlanmadan önce, uygulayıcıyı tek mirasla sınırlayan, şimdi sadece arayüz ve minimum uygulama ile gerçekten temiz bir tasarıma sahip olabilmenizdir. çaba programcı üzerinde zorladı.

defaultJava 8'e yöntem tanıtmak için orijinal motivasyon , Koleksiyon Çerçevesi arayüzlerini mevcut uygulamaları bozmadan lambda odaklı yöntemlerle genişletme arzusuydu. Bu, halk kütüphanelerinin yazarlarıyla daha alakalı olsa da, aynı özelliği projenizde de yararlı bulabilirsiniz. Yeni kolaylık eklemek için tek bir merkezi yeriniz var ve diğer tür hiyerarşisinin nasıl göründüğüne güvenmek zorunda değilsiniz.


34
Bu nedenle, ekleyecekleri bir sonraki şey varsayılan yöntem bildirimleridir. Bu konuda hala emin değilim, özellik daha çok kötüye kullanım için herkese maruz kalan bir kesmek gibi görünüyor.
panter

3
Görebildiğim Java 8 döneminde Soyut Sınıfların tek kullanımı, son olmayan alanları tanımlamak içindir. Arayüzlerde alanlar varsayılan olarak son şeklindedir, bu nedenle atandıktan sonra bunları değiştiremezsiniz.
Anuroop

7
@Anuroop Sadece varsayılan olarak değil --- bu tek seçenektir. Arabirimler örnek durumunu bildiremez, bu nedenle soyut sınıflar burada kalır.
Marko Topolnik

2
@PhilipRego Soyut yöntemler hiçbir uygulama demezler, çünkü uygulamaları yoktur. Bir sınıfta uygulanan yöntemler, sınıfın durumuna (örnek değişkenler) erişebilir. Arabirimler bunları bildiremez, böylece varsayılan yöntemler bunlara erişemez. Devlete erişen uygulamalı bir yöntem sağlayan sınıfa güvenmek zorundadırlar.
Marko Topolnik

2
Marko Topolnik, cevabınız öldü. Ancak cevabınız için bir güncelleme öneriyorum. Varsayılan yöntemlerin güzelliğinin, arabirim yeni varsayılan yöntemler eklerse, bu arabirimin önceki uygulamanızın bozulmayacağı olduğunu eklemek isteyebilirsiniz. Bu, Java 8'den önce doğru değildi.
hfontanez

125

Birkaç teknik fark var. Soyut sınıflar, Java 8 arabirimlerine kıyasla daha fazlasını yapabilir:

  1. Soyut sınıf bir kurucu olabilir.
  2. Soyut sınıflar daha yapılandırılmıştır ve bir durumu olabilir.

Kavramsal olarak, defender yöntemlerinin temel amacı Java 8'de yeni özelliklerin (lambda işlevleri olarak) piyasaya sürülmesinden sonra geriye dönük bir uyumluluktur.


20
Bu cevap aslında doğru olduğunu ve markaları özellikle anlamda "Kavramsal olarak, savunma oyuncusu yöntemlerinin temel amacı bir geriye dönük uyumluluk olduğu"
çalışılıyor

1
@UnKnown bu sayfa daha fazla bilgi verir: docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
bernie

@UnKnown, temelde bir arayüze yöntemler eklemenize ve o arayüzü uygulayan sınıfların otomatik olarak bu işlevselliği almasına izin verir.
LegendLength

3
Nokta no hakkında daha ince bir nokta. 2 yukarıda "tutabilir devlet budur" hakkında. Soyut sınıflar daha sonra değiştirilebilecek durumlara sahip olabilir. Arabirimler de durumu tutabilir, ancak örnek oluşturulduktan sonra bir durum atandığında durum değiştirilemez.
Anuroop

3
@Auroop public static finalBir arabirimin alanlarını "durum" olarak tanımlamam . staticHer bir belirli örneği ile ilgili olmayan the're parçası anlamına gelir. Bunlar, örnek oluşturma sonrasındaki gibi olmayan sınıf somutlaştırmasında atanır .
geronimo

65

Bu, bu makalede açıklanmaktadır . forEachKoleksiyonları düşünün .

List<?> list = 
list.forEach(…);

ForEach tarafından bildirilmedi java.util.Listne de java.util.Collectionhenüz arayüzüne. Açık bir çözüm, sadece yeni yöntemi mevcut arayüze eklemek ve JDK'da gerektiğinde uygulamayı sağlamak olacaktır. Ancak, yayınlandıktan sonra, mevcut uygulamayı bozmadan bir arayüze yöntem eklemek imkansızdır.

Varsayılan yöntemlerin getirdiği avantaj, şimdi arabirime yeni bir varsayılan yöntem eklemek mümkün olmasıdır ve uygulamaları bozmaz.


1
'Mevcut uygulamayı bozmadan bir arayüze yöntem eklemek imkansız' - değil mi?
Andrey Chaschev

26
@AndreyChaschev Arabirime yeni bir yöntem eklerseniz, tüm uygulayıcıların bu yeni yöntemi uygulaması gerekir. Bu nedenle mevcut uygulamaları kırar.
Marko Topolnik

4
@ MarkoTopolnik teşekkürler, bunu kaçırdı. Sadece, bu yöntemi varsayılan bir soyut uygulamada sunarak kısmen önlemenin bir yolu olduğunu belirtmek isteriz. Bu örnek için bu AbstractList::forEachbir UnsupportedOperationException.
Andrey Chaschev

3
@AndreyChaschev Evet, bu eski yoldu (khm ... şu anki yoldur :), uygulayıcıyı sağlanan soyut uygulamadan tek mirasla kısıtlama eksikliği ile.
Marko Topolnik

Öyle olursa, tüm uygulamalar bu yöntemi içeriyordu. Bu mümkün değil ama mümkün.
George Xavier

19

Bu ikisi oldukça farklı:

Varsayılan yöntemler, mevcut sınıflara durumlarını değiştirmeden harici işlevsellik eklemektir .

Ve soyut sınıflar normal bir kalıtım türüdür, uzatılması amaçlanan normal sınıflardır .


18

Bu makalede açıklandığı gibi ,

Java 8'de arabirimlere karşı soyut sınıflar

Varsayılan Yöntemi uygulamaya koyduktan sonra arayüzler ve soyut sınıflar aynı görünüyor. Ancak, Java 8'de hala farklı bir kavramdır.

Soyut sınıf yapıcıyı tanımlayabilir. Daha yapılandırılmıştır ve kendileriyle ilişkili bir duruma sahip olabilirler. Aksine, varsayılan yöntem yalnızca belirli bir uygulamanın durumuna başvurmadan yalnızca diğer arabirim yöntemlerini çağırmak açısından uygulanabilir. Bu nedenle, hem farklı amaçlar için kullanın hem de ikisi arasında seçim yapmak senaryo bağlamına bağlıdır.


Abstract sınıfının, Interface'den farklı olarak tanımlanabilecek bir Yapıcı olduğuna inanıyorum. Java 8'de de bu nedenle ikisi de birbirinden farklıdır.
Hemanth Peela

1
soyut bir sınıfın örneklemesi yapılamıyorsa neden bir kurucu vardır?
George Xavier

Soyut sınıfın yapıcısını çağıracak çocuk sınıfından super () diyebiliriz, bu da soyut sınıfın durumunu etkiler.
Sujay Mohan

14

Sorgunuzla ilgili olarak

Öyleyse varsayılan yöntemlerle arayüz ne zaman kullanılmalı ve soyut bir sınıf ne zaman kullanılmalıdır? Soyut sınıflar bu senaryoda hala faydalı mı?

Java belgeleri mükemmel cevap verir.

Arayüzlere Göre Soyut Sınıflar:

Soyut sınıflar arayüzlere benzer. Onları başlatamazsınız ve bunlar, bir uygulama ile veya bir uygulama olmadan bildirilen çeşitli yöntemleri içerebilir.

Ancak, soyut sınıflarla, statik ve son olmayan alanları bildirebilir ve genel, korunan ve özel somut yöntemleri tanımlayabilirsiniz.

Arabirimlerle, tüm alanlar otomatik olarak genel, statik ve son şeklindedir ve bildirdiğiniz veya tanımladığınız tüm yöntemler (varsayılan yöntemler olarak) geneldir. Buna ek olarak, soyut olsun ya da olmasın yalnızca bir sınıfı genişletebilirsiniz, oysa istediğiniz sayıda arabirim uygulayabilirsiniz.

Her biri için kullanım örnekleri aşağıdaki SE yazısında açıklanmıştır:

Bir arayüz ile soyut sınıf arasındaki fark nedir?

Soyut sınıflar bu senaryoda hala faydalı mı?

Evet. Hala faydalıdırlar. Onlar olmayan statik olmayan nihai yöntemlerini içerebilir ve özelliklerini ( korunan, kamuya ek olarak özel hatta Java 8 arayüzlü mümkün değildir).


Artık arayüzlerin de özel yöntemleri var howtodoinjava.com/java9/java9-private-interface-methods
valijon

13

Soyut sınıf ve arayüz arasında bir seçim yaptığımızda, her zaman (neredeyse) varsayılan (defender veya sanal uzantılar olarak da bilinir) yöntemleri tercih etmeliyiz.

  1. Varsayılan yöntemler, klasik arabirim kalıbına ve bu arabirimdeki yöntemlerin çoğunu veya tamamını uygulayan bir eşlik sınıfına son vermiştir. Bir örnek Collection and AbstractCollection. Şimdi, varsayılan işlevselliği sağlamak için yöntemleri arayüzün kendisinde uygulamalıyız. Arabirimi uygulayan sınıfların yöntemleri geçersiz kılma veya varsayılan uygulamayı devralma seçenekleri vardır.
  2. Varsayılan yöntemlerin bir başka önemli kullanımı interface evolution. Varsayalım ki bir sınıf Ball'um vardı:

    public class Ball implements Collection { ... }

Şimdi Java 8'de yeni bir özellik yayınlandı. streamArabirime eklenen yöntemi kullanarak bir akış elde edebiliriz . Eğer streamtüm uygulamalar varsayılan bir yöntem değildi Collectionbu yeni yöntemi uygulayan olacağı için arayüzde kırık olurdu. Bir arabirime varsayılan olmayan bir yöntem eklemek değildir source-compatible.

Ancak, sınıfı yeniden derlemediğimizi ve bu sınıfı içeren eski bir jar dosyası kullandığımızı varsayalım Ball. Sınıf, bu eksik yöntem olmadan iyi yüklenir, örnekler oluşturulabilir ve her şey iyi çalışıyor gibi görünüyor. AMA program streamörneğini çağırırsa örnek Ballalacağız AbstractMethodError. Böylece yöntem varsayılan yapmak her iki sorunu da çözdü.


9

Java arayüzünde Standart yöntemler sağlayan arayüz evrimi .

Mevcut bir arabirim göz önüne alındığında, arabirimin eski sürümleriyle ikili uyumluluğu bozmadan bir yöntem eklemek isterseniz, elinizde iki seçenek vardır: varsayılan veya statik bir yöntem ekleyin. Gerçekten de, arabirime eklenen herhangi bir soyut yöntemin, bu arabirimi uygulayan sınıflar veya arabirimler tarafından uygulanması gerekir.

Statik bir yöntem sınıfa özgüdür. Varsayılan yöntem, sınıfın bir örneğine özgüdür.

Varolan bir arabirime varsayılan bir yöntem eklerseniz, bu arabirimi uygulayan sınıfların ve arabirimlerin bunu uygulamasına gerek yoktur. Yapabilirler

  • varsayılan yöntemi uygulayın ve uygulanan arabirimdeki uygulamayı geçersiz kılar.
  • soyut yapan yöntemi (uygulama olmadan) yeniden beyan eder.
  • hiçbir şey yapma (uygulanmış arabirimin varsayılan yöntemi basitçe devralınır).

Konu hakkında daha fazla bilgi burada .


7

Her ne kadar eski bir soru olsa da benim girdi vereyim.

  1. abstract class: abstract sınıfında alt sınıf için gerekli olan örnek değişkenleri bildirebiliriz

    Arayüz: Arayüz içinde her değişken her zaman genel statiktir ve son olarak örnek değişkenleri beyan edemeyiz

  2. soyut sınıf: Soyut sınıf nesnenin durumu hakkında konuşabilir

    Arayüz: Arayüz asla nesnenin durumu hakkında konuşamaz

  3. soyut sınıf: Soyut sınıf içinde yapıcıları ilan edebiliriz

    Arabirim: Arabirim içinde,
    yapıcıların amacı örnek değişkenleri başlatmak olduğundan yapıcıları bildiremeyiz . Peki, arayüzlerde örnek değişkenlere sahip olamıyorsak, burada yapıcıya ihtiyaç vardır .

  4. soyut sınıf: Soyut sınıf içinde örnek ve statik bloklar bildirebiliriz

    Arayüz: Arayüzlerde örnek ve statik bloklar olamaz.

  5. soyut sınıf: Soyut sınıf lambda ifadesine başvuramaz

    Arayüzler: Tek soyut yöntemle arayüzler lambda ifadesini ifade edebilir

  6. abstract class : Abstract class içinde OBJECT CLASS yöntemlerini geçersiz kılabiliriz

    Arayüzler: Arayüzler içindeki NESNE SINIFI yöntemlerini geçersiz kılamayız.

Ben şu notta sona erecek:

Varsayılan yöntem kavramları / arabirimdeki statik yöntem kavramları sadece uygulama sınıflarını kaydetmek için gelmiş, ancak anlamlı faydalı uygulama sağlamak için gelmemiştir. Varsayılan yöntemler / statik yöntemler bir tür kukla uygulamadır, "isterseniz bunları kullanabilir veya uygulama sınıfında bunları (varsayılan yöntemler durumunda) geçersiz kılabilirsiniz" Böylece, arabirimlerdeki yeni yöntemler her zaman uygulama sınıflarında yeni yöntemler uygulamaktan bizi kurtarır. eklendi. Bu nedenle arabirimler hiçbir zaman soyut sınıflara eşit olamaz.


5

Remi Forax kuralı Soyut sınıflarla tasarım yapmamanızdır . Uygulamanızı arayüzlerle tasarlarsınız . Watever, dil ne olursa olsun, Java'nın sürümüdür. Bu tarafından desteklenmektedir ben nterface ayrımı ilkesine içinde SOL I D ilkelerine.

Daha sonra kodu çarpanlarına ayırmak için Abstract sınıflarını kullanabilirsiniz. Şimdi Java 8 ile doğrudan arayüzde yapabilirsiniz. Bu bir tesis, daha fazla değil.


2

varsayılan yöntemlerle arayüz ne zaman kullanılmalı ve soyut bir sınıf ne zaman kullanılmalıdır?

Geriye dönük uyumluluk: Arayüzünüzün yüzlerce sınıf tarafından uygulandığını düşünün, arayüzün değiştirilmesi tüm arayüzü yeni eklenen yöntemi uygulamaya zorlayacaktır, ancak arayüzünüzü uygulayan diğer birçok sınıf için gerekli olmasa da, arayüzünüze izin verir bir olmak işlevsel arayüz

Gerçekler ve Kısıtlamalar:

1-Bir sınıf veya soyut sınıf içinde değil, sadece bir arayüz içinde ilan edilebilir.

2-Beden sağlamalı

3-Bir arayüzde kullanılan diğer normal yöntemler gibi soyut olduğu varsayılmamaktadır.


1

Java 8'de, bir arayüz soyut bir sınıfa benziyor, ancak aşağıdaki gibi bazı farklılıklar olabilir:

1) Soyut sınıflar sınıflardır, bu nedenle Java'daki arabirimin diğer kısıtlamaları ile sınırlı değildir, örneğin soyut sınıfın durumu olabilir , ancak Java'daki arabirimdeki duruma sahip olamazsınız.

2) Varsayılan yöntemlerle arabirim ve soyut sınıf arasındaki başka bir anlamsal fark, soyut bir sınıf içindeki yapıcıları tanımlayabilmenizdir , ancak Java'daki arabirim içinde yapıcıyı tanımlayamazsınız


# 2 ile hemfikirim ama # 1 için, sadece arayüzü uygulayamaz mısınız ve böylece uygulama sınıfı aracılığıyla bir durumunuz var mı?
George Xavier

0

Java Arabirimindeki varsayılan yöntemler, bir işlevin kukla bir şekilde uygulanmasını sağlamak için daha fazla kullanılmalıdır, böylece bu arabirimin herhangi bir uygulayıcı sınıfını, yalnızca biriyle uğraşmak isteseler bile tüm soyut yöntemleri bildirme acısından kurtarırlar. Dolayısıyla arayüzdeki varsayılan yöntemler, bir şekilde adaptör sınıfları kavramının yerini almıştır.

Bununla birlikte, soyut sınıftaki yöntemlerin, herhangi bir alt sınıfın yalnızca ortak bir işlevselliği geçersiz kılmak için gerektiğinde geçersiz kılması gereken anlamlı bir uygulama sağlaması beklenir.


0

Diğer cevaplarda belirtildiği gibi, Koleksiyonlar çerçevesinde geriye dönük uyumluluk sağlamak için bir arayüze uygulama ekleme yeteneği eklendi. Geriye dönük uyumluluk sağlamanın, bir arabirime uygulama eklemek için potansiyel olarak tek iyi neden olduğunu iddia ediyorum.

Aksi takdirde, bir arabirime uygulama eklerseniz, arabirimlerin neden ilk başta eklendiğine ilişkin temel yasayı ihlal edersiniz. Java, çoklu kalıtım sağlayan C ++ 'dan farklı olarak tek bir kalıtım dilidir. Arabirimler, çoklu kalıtımla gelen sorunları ortaya çıkarmadan çoklu kalıtımın desteklendiği bir dil ile gelen yazma avantajları sağlar.

Daha spesifik olarak, Java bir uygulamanın yalnızca tek mirasına izin verir, ancak arabirimlerin birden çok mirasına izin verir. Örneğin, aşağıdakiler geçerli Java kodudur:

class MyObject extends String implements Runnable, Comparable { ... }

MyObject yalnızca bir uygulamayı miras alır, ancak üç sözleşmeyi miras alır.

Java, uygulamanın birden fazla mirasını iletti, çünkü uygulamanın birden fazla mirası bu cevabın kapsamı dışında olan bir dizi dikenli sorunla birlikte geliyor. Çoklu uygulama devralma problemleri olmadan sözleşmelerin çoklu mirasına (diğer bir deyişle arayüzler) izin vermek için arayüzler eklendi.

Demek istediğim, Ken Arnold ve James Gosling'in Java Programlama Dili, 4. baskı kitabından bir alıntı :

Tek miras bazı kullanışlı ve doğru tasarımları engeller. Çoklu kalıtım problemleri, uygulamanın çoklu kalıtımından kaynaklanır, ancak birçok durumda, birçok soyut sözleşmeyi ve belki de somut bir uygulamayı devralmak için çoklu kalıtım kullanılır. Bir uygulamayı devralmadan soyut bir sözleşmeyi devralmak için bir yol sağlamak, birden çok uygulama mirası sorunu olmadan birden fazla mirasın yazım avantajlarına izin verir. Soyut bir sözleşmenin mirasına arabirim mirası denir . Java programlama dili, bir interfacetür bildirmenize izin vererek arabirim mirasını destekler


-1

Lütfen önce açık / kapalı prensibini düşünün. Arayüzlerdeki varsayılan yöntemler ÇEVİRİR. Bu Java'daki kötü bir özelliktir. Kötü tasarımı, kötü mimariyi, düşük yazılım kalitesini teşvik eder. Varsayılan yöntemleri tamamen kullanmaktan kaçınmayı öneririm.

Kendinize birkaç soru sorun: Neden yöntemlerinizi soyut sınıfa koyamıyorsunuz? O zaman birden fazla soyut sınıfa mı ihtiyacınız var? O zaman sınıfınızın neden sorumlu olduğunu düşünün. Tek sınıfa koyacağınız tüm yöntemlerin aynı amacı yerine getirdiğinden emin misiniz? Belki birkaç amacı ayırt edeceksiniz ve daha sonra sınıfınızı her amaç için kendi sınıfına ayıracaksınız.

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.