Java'da devralmayı yasaklamak için iyi nedenler?


178

Java'da devralmayı yasaklamak için iyi nedenler vardır; örneğin, son sınıfları veya tek bir özel parametresiz kurucu kullanarak sınıfları kullanmak? Bir yöntemi kesinleştirmenin iyi nedenleri nelerdir?


1
Bilgiçlik: Kodunuza açıkça yazmazsanız, varsayılan kurucu sizin için Java derleyicisi tarafından oluşturulan yapıdır. Hiçbir argüman yapıcı demek istiyorsun.
John Topley

"Örtük varsayılan kurucu" değil mi?
cretzel

2
"Sınıfınız için herhangi bir kurucu sağlamanız gerekmez, ancak bunu yaparken dikkatli olmalısınız. Derleyici, kurucuları olmayan herhangi bir sınıf için otomatik olarak bağımsız değişken, varsayılan bir kurucu sağlar." java.sun.com/docs/books/tutorial/java/javaOO/constructors.html - John Topley
John Topley

Yanıtlar:


184

Buradaki en iyi referansınız, Joshua Bloch'un "Miras için tasarım ve belge veya başka bir şeyi yasaklaması" adlı mükemmel kitabı "Etkili Java" nın 19. Maddesidir. (İkinci baskıdaki 17. madde ve birinci baskıdaki 15. madde.) Gerçekten okumalısınız, ama özetleyeceğim.

Eğer atadan miras alınacak şekilde tasarlanmamışsa, kalıtsal sınıfların ebeveynleri ile etkileşimi şaşırtıcı ve tahmin edilemez olabilir.

Bu nedenle sınıflar iki çeşit olmalıdır:

  1. Genişletilmek üzere tasarlanmış sınıflar ve bunun nasıl yapılması gerektiğini açıklayan yeterli dokümantasyon

  2. Sınıflar final olarak işaretlendi

Tamamen dahili bir kod yazıyorsanız, bu biraz fazla olabilir. Ancak, bir sınıf dosyasına beş karakter eklemek için gereken ekstra çaba çok azdır. Sadece iç tüketim için yazıyorsanız, gelecekteki bir kodlayıcı her zaman 'final'i kaldırabilir - bunu "bu sınıf kalıtım göz önünde bulundurularak tasarlanmadı" diyen bir uyarı olarak düşünebilirsiniz.


6
Benim tecrübelerime göre, eğer benim dışımda kimse kodu kullanacaksa bu aşırıya kaçmaz. Java'nın neden varsayılan olarak geçersiz kılınan yöntemlere sahip olduğunu hiç anlamadım.
Eric Weilnau

9
Etkili Java'nın bazılarını anlamak için Josh'un tasarıma hakim olduğunu anlamanız gerekir. Sınıf arayüzlerini her zaman herkese açık bir API gibi tasarlamanız gerektiğini söylüyor. Bence çoğunluk bu yaklaşımın genellikle çok ağır ve esnek olmadığı yönündedir. (YAGNI, vb.)
Tom Hawtin - çakmak hattı

9
İyi cevap. (Ayrıca bir boşluk olduğu için altı karakter olduğuna işaret edemiyorum ... ;-)
joel.neely

36
Sınıfları final yapmak test yapmayı zorlaştırabilir (saplama veya alay etmek daha zor)
notnoop

10
Eğer son sınıf bir arayüze yazıyorsa, alay etmek sorun olmamalı.
Fred Haslam

26

Bir yöntemi sonlandırmak isteyebilirsiniz, böylece sınıfların geçersiz kılınması diğer yöntemlerde güvenilen davranışı değiştiremez. Yapıcılarda çağrılan yöntemler genellikle nihai olarak bildirilir, böylece nesne oluştururken hoş olmayan sürprizler alamazsınız.


Bu cevabı tam olarak anlamadım. Sakıncası yoksa, "sınıfları geçersiz kılmak diğer yöntemlerde sayılan davranışı değiştiremez" ile ne demek istediğinizi açıklayabilir misiniz? Öncelikle soruyorum çünkü cümleyi anlamadım, ikincisi Netbeans yapıcıdan aradığım fonksiyonlardan birinin olmasını tavsiye ediyor final.
Nav

1
@Nav Bir yöntemi sonlandırırsanız, geçersiz kılınan bir sınıf bu yöntemin kendi sürümüne sahip olamaz (veya derleme hatası olur). Bu şekilde, bu yöntemde uyguladığınız davranışın daha sonra başkası sınıfı genişlettiğinde değişmeyeceğini bilirsiniz.
Kertenkele Bill

19

Bir sınıfı final yapmanın bir nedeni, kompozisyonu kalıtım üzerinde zorlamak istemeniz olabilir. Bu genellikle sınıflar arasında sıkı bağlantıdan kaçınmak için arzu edilir.


15

Nihai yöntemler için gidebileceğiniz 3 kullanım durumu vardır.

  1. Türetilmiş sınıfın belirli bir temel sınıf işlevini geçersiz kılmasını önlemek için.
  2. Bu, temel sınıfın türetilmiş sınıfın onu değiştirmemesi gereken çerçevenin bazı önemli temel işlevlerini verdiği güvenlik amaçlıdır.
  3. Son ve özel yöntemler için sanal tablo kavramı kullanılmadığından, son yöntemler örnek yöntemlerden daha hızlıdır. Bu nedenle, herhangi bir olasılık olduğunda, son yöntemleri kullanmaya çalışın.

Bir dersi final yapma amacı:

Böylece hiçbir beden bu sınıfları genişletemez ve davranışlarını değiştiremez.

Örneğin: Wrapper sınıfı Integer son sınıftır. Bu sınıf son değilse, herhangi biri Integer'ı kendi sınıfına genişletebilir ve integer sınıfının temel davranışını değiştirebilir. Bundan kaçınmak için, java tüm sarmalayıcı sınıflarını son sınıf olarak yaptı.


Örneğinize pek ikna olmadınız. Eğer durum buysa, o zaman tüm sınıfı sonlandırmak yerine neden yöntemleri ve değişkenleri sonlandırmıyorsunuz? Cevabınızı ayrıntılarla düzenleyebilir misiniz?
Rajkiran

4
Tüm değişkenleri ve yöntemleri son olarak yapsanız bile, yine de diğerleri sınıfınızı devralabilir ve ekstra işlevsellik ekleyebilir ve sınıfınızın temel davranışını değiştirebilir.
user1923551

3
> Bu sınıf son değilse, herhangi biri Integer'ı kendi sınıfına genişletebilir ve integer sınıfının temel davranışını değiştirebilir. Buna izin verildiğini ve bu yeni sınıfın çağrıldığını DerivedInteger, hala orijinal Integer sınıfını değiştirmediğini ve kullananların DerivedIntegerbunu kendi riski altında yaptıklarını, bu yüzden neden bir sorun olduğunu anlamıyorum.
Siddhartha


7

Kalıtım bir testere gibidir - çok güçlü, ancak yanlış ellerde korkunç. Ya miras alınacak bir sınıf tasarlarsınız (esnekliği sınırlayabilir ve çok daha uzun sürebilir) ya da onu yasaklamanız gerekir.

Etkili Java 2nd edition öğeleri 16 ve 17 veya blog yazım "Veraset Vergisi" bölümüne bakın .


Blog gönderisine bağlantı kesildi. Ayrıca, bu konuda biraz daha ayrıntılı olabileceğinizi düşünüyor musunuz?

@MichaelT: Bağlantı düzeltildi, teşekkürler - daha fazla ayrıntı için takip edin :) (Şu anda eklemek için zamanım yok, korkuyorum.)
Jon Skeet

@JonSkeet, Blog gönderisine bağlantı kesildi.
Lucifer

@Kedarnath: Bana uyar ... o edildi codeblog.jonskeet.uk üzerinde bir süre kırık, ama şimdi doğru sayfaya işaret ediyor ...
Jon Skeet

4

Hmmm ... İki şey düşünebilirim:

Belirli güvenlik sorunlarıyla ilgilenen bir sınıfınız olabilir. Saldırgan, alt sınıfını ve alt sınıfını sisteminize besleyerek güvenlik kısıtlamalarını atlatabilir. Örneğin uygulamanız eklentileri destekleyebilir ve bir eklenti yalnızca güvenlikle ilgili sınıflarınızı alt sınıflara ayırabiliyorsa, bu hileyi bir şekilde alt sınıflı bir sürümünü yerine geçirmek için kullanabilir. Bununla birlikte, bu, Sun'ın küçük uygulamalar ve benzerleri ile uğraşması gereken bir şey, belki de gerçekçi bir durum değil.

Çok daha gerçekçi olanı bir nesnenin değişebilir hale gelmesini önlemektir. Örneğin, Dizeler değiştirilemez olduğundan, kodunuz ona referansları güvenle tutabilir

 String blah = someOtherString;

önce dizeyi kopyalamak yerine. Ancak, String'i alt sınıflandırabiliyorsanız, dize değerinin değiştirilmesine izin veren yöntemler ekleyebilirsiniz, şimdi dize yalnızca yukarıdaki gibi kopyalarsa dizenin aynı kalacağına artık hiçbir kod güvenmeyecektir, bunun yerine dize.


2

Ayrıca, ticari bir kapalı kaynak sınıfı yazıyorsanız, özellikle u için destek vermeniz gerekiyorsa ve insanlar yönteminizi geçersiz kıldıysa ve bunu çağırmaktan şikayet ediyorsa, insanların işlevsellikleri değiştirmesini istemeyebilirsiniz. beklenmedik sonuçlar.


2

Sınıfları ve yöntemleri son olarak işaretlerseniz, çalışma zamanının belirli bir nesneyi çağırmak için doğru sınıf yöntemini araması gerekmediğinden küçük bir performans artışı fark edebilirsiniz. Nihai olmayan yöntemler sanal olarak işaretlenir, böylece gerektiğinde uygun şekilde genişletilebilir, son yöntemler doğrudan sınıfta bağlanabilir veya satır içinde derlenebilir.


1
Bunun bir efsane olduğuna inanıyorum, çünkü mevcut nesil VM'ler gerektiği gibi optimize etmeli ve deoptimize etmelidir. Bunu bencharmking'i gördüğümü ve bir efsane olarak sınıflandırdığımı hatırlıyorum.
Miguel Ping

1
Kod oluşturmadaki fark bir efsane değil, belki de performans kazançlarıdır. Dediğim gibi, küçük bir kazanç, bir site% 3.5 performans kazancı, biraz daha yüksek, çoğu durumda kodunuzda tüm Nazi gitmeye değmez bahsetti.
seanalltogether

1
Bu bir efsane değil. Aslında, derleyici sadece satır içi V son yöntemlerini yapabilir. Herhangi bir JITs "satır içi" şeyler sanat çalışma zamanı olup olmadığından emin değilim ama daha sonra türetilmiş bir sınıf yüklüyor olabilir çünkü ben şüpheliyim.
Uri

1
Microbenchmark == tahıl-of-tuzu. Bununla birlikte, 10B döngü ısınmasından sonra, 10B'den fazla çağrı, dizüstü bilgisayarımdaki Eclipse altında hiçbir fark göstermiyor. String döndüren iki yöntem, final 6.9643us, final olmayan 6.9641us idi. Bu delta arkaplan gürültüsü IMHO.
joel.neely

1

İnsanların kendilerini ve başkalarını karıştırabilecek şeyler yapmalarını engellemek. Bazı sabitleri veya hesaplamaları tanımladığınız bir fizik kütüphanesi düşünün. Son anahtar kelimeyi kullanmadan, birisi gelip ASLA değişmemesi gereken temel hesaplamaları veya sabitleri yeniden tanımlayabilir.


2
Bu argüman hakkında anlamadığım bir şey var - eğer birisi değişmemesi gereken bu hesaplamaları / sabitleri değiştirdiyse - bu% 100 hatalarından dolayı herhangi bir hata değil mi? Başka bir deyişle, değişiklik herhangi bir şeye nasıl yarar sağlar?
matt b

Evet, teknik olarak "onların" hatasıdır, ancak kitaplık kullanan değişikliklerin farkına varılmamış bir başkasının kafa karışıklığına yol açtığını düşünün. Her zaman insanların temel işlevselliği değiştirmelerini engellemek için Java Base sınıflarının birçoğunun final olarak işaretlenmesinin nedeni olduğunu düşündüm.
Anson Smith

@matt b - Değişikliği yapan geliştiricinin bilerek yaptığını ya da bunun onların hatası olduğunu umurduğunu varsayıyorsunuz. Eğer benim dışımda biri kodumu kullanacaksa, değiştirilmesi istenmedikçe son olarak işaretleyeceğim.
Eric Weilnau

Bu aslında 'final'in sorulmasından farklı bir kullanımdır.
DJClayworth

Bu cevap, münferit alanların mirasını ve değişebilirliğini bir alt sınıf yazma yeteneği ile karıştırmaktadır. Devralmayı yasaklamadan tek tek alanları ve yöntemleri son olarak (yeniden tanımlanmasını önleyerek) son olarak işaretleyebilirsiniz.
joel.neely

0

Sınıfları geçersiz kılmanın davranışını değiştirmemesi için bir yöntemi sonlandırmak istiyorsunuz. Davranışı değiştirmek istediğinizde yöntemi herkese açık hale getirin. Genel bir yöntemi geçersiz kıldığınızda değiştirilebilir.

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.