Java birden fazla kalıtıma izin vermez, ancak birden fazla arabirimin uygulanmasına izin verir. Neden?
Java birden fazla kalıtıma izin vermez, ancak birden fazla arabirimin uygulanmasına izin verir. Neden?
Yanıtlar:
Arabirimler sadece belirtmek Çünkü neyi sınıf değildir, yapıyor nasıl bunu yapıyor.
Çoklu kalıtımla ilgili sorun, iki sınıfın aynı şeyi yapmanın farklı yollarını tanımlayabilmesidir ve alt sınıf hangisini seçeceğini seçemez.
Üniversite eğitmenlerimden biri bana şöyle açıkladı:
Bir Toaster olan bir sınıfım ve NuclearBomb olan başka bir sınıfım olduğunu varsayalım. İkisinin de bir "karanlık" ayarı olabilir. Her ikisinin de on () yöntemi vardır. (Birinin bir off () vardır, diğeri yoktur.) Bunların her ikisinin de bir alt sınıfı olan bir sınıf oluşturmak istersem ... gördüğünüz gibi, bu gerçekten yüzümde patlayabilecek bir sorun .
Yani ana konulardan biri, iki ana sınıfınız varsa, aynı özelliğin farklı uygulamalarına sahip olabilirler - veya eğitmenimin örneğindeki gibi aynı ada sahip iki farklı özelliğe sahip olabilirler. O zaman alt sınıfınızın hangisini kullanacağına karar vermek zorundasınız. Bunu ele almanın yolları var, kesinlikle - C ++ bunu yapıyor - ancak Java tasarımcıları bunun çok karmaşık olacağını düşünüyor.
Bununla birlikte, bir arayüzle, başka bir sınıfın bir şey yapma yöntemini ödünç almak yerine sınıfın yapabileceği bir şeyi açıklıyorsunuz. Birden çok arabirimin, çözülmesi gereken zor çakışmalara neden olması, birden çok üst sınıftan daha az olasıdır.
Kalıtım sen bile fazla kullanılırsa Çünkü olamaz demek "Hey, yöntem yararlıdır görünüyor, ben de bu sınıfı genişletir edeceğiz".
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
Bu sorunun cevabı, java derleyicisinin iç çalışmasında yatıyor (oluşturucu zinciri). Java derleyicisinin dahili çalışmasını görürsek:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
Bu derlemeden sonra şöyle görünür:
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
sınıfı genişletip onun bir nesnesini yarattığımızda, bir kurucu zincir Object
sınıfa kadar çalışır .
Yukarıdaki kod iyi çalışır. ancak, adı verilen başka bir sınıfımız ve Car
şu adla adlandırılan Bank
bir karma (çoklu kalıtım) sınıfı varsa SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
Bu durumda (SBICar) yapıcı zinciri oluşturamaz ( derleme zamanı belirsizliği ).
Arabirimler için buna izin verilir, çünkü onun bir nesnesini oluşturamayız.
Yeni konsept default
ve static
yöntem için lütfen arayüzde varsayılana bakın .
Umarım bu sorunuzu çözer. Teşekkürler.
Birden çok arayüz uygulamak çok faydalıdır ve dil uygulayıcılarına veya programcılara fazla sorun yaratmaz. Böylece izin verilir. Çoklu kalıtım da faydalı olsa da, kullanıcılara ciddi sorunlara neden olabilir (korkunç ölüm elması ). Ve çoklu kalıtımla yaptığınız çoğu şey kompozisyon veya iç sınıflar kullanılarak da yapılabilir. Dolayısıyla, birden fazla mirasın kazançlardan daha fazla sorun getirmesi yasaktır.
ToyotaCar
ve HybridCar
her ikisi de türetilmiş Car
ve geçersiz kılınmışsa ve her ikisi de miras alınmış ancak geçersiz kılmamışsa Car.Drive
, sistemin sanalın ne yapması gerektiğini belirleyemez. Arabirimler yukarıdaki italik durumdan kaçınarak bu sorunu önler. PriusCar
Drive
Car.Drive
void UseCar(Car &foo)
; arasındaki anlam ayrımı dahil etmek beklenemez ToyotaCar::Drive
ve HybridCar::Drive
(o diğer türleri bile genellikle ne bilmeli beri ne de bakımı var ). Bir dil, C ++ 'ın yaptığı gibi, ToyotaCar &myCar
onu geçmek isteyen kodun UseCar
önce HybridCar
veya birine yayınlanmasını gerektirebilir ToyotaCar
, ancak ((Araba) (HybridCar) myCar) .Drive` olduğundan ve ((Car)(ToyotaCar)myCar).Drive
farklı şeyler yapacağından kimlik koruyucu değildi.
Bu sorgu için doğru cevabı çoklu miras hakkındaki oracle dokümantasyon sayfasında bulabilirsiniz
Birden çok durum mirası: Alanları birden çok sınıftan devralma yeteneği
Java programlama dilinin birden fazla sınıfı genişletmenize izin vermemesinin bir nedeni, birden fazla sınıftan alan devralma yeteneği olan çoklu durum miras sorunlarından kaçınmaktır
Birden fazla devralmaya izin verilirse ve bu sınıfı örnekleyerek bir nesne oluşturduğunuzda, bu nesne sınıfın tüm üst sınıflarından alanlarını devralır. İki soruna neden olacaktır.
Birden çok uygulama devralma: Birden çok sınıftan yöntem tanımlarını devralma yeteneği
Bu yaklaşımla ilgili sorunlar: çatışmaları ve belirsizliği adlandırın . Bir alt sınıf ve üst sınıf aynı yöntem adını (ve imzasını) içeriyorsa, derleyici hangi sürümü çağıracağını belirleyemez.
Ancak java, Java 8 sürümünden beri tanıtılan varsayılan yöntemlerle bu tür çoklu kalıtım türlerini destekler . Java derleyicisi, belirli bir sınıfın hangi varsayılan yöntemi kullandığını belirlemek için bazı kurallar sağlar.
Elmas probleminin çözümü hakkında daha fazla bilgi için aşağıdaki SE yazısına bakın:
Java 8'deki soyut sınıflar ve arayüzler arasındaki farklar nelerdir?
Birden fazla tür mirası: Bir sınıfın birden fazla arabirim uygulama yeteneği.
Arayüz değiştirilebilir alanlar içermediğinden, burada çoklu durum mirasından kaynaklanan sorunlar hakkında endişelenmenize gerek yoktur.
Nesne durumunun, içindeki alanlara göre atıfta bulunduğu ve çok fazla sınıfın miras alınması durumunda belirsiz hale geleceği söylenir. Bağlantı burada
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Java, yalnızca arabirimler aracılığıyla birden fazla kalıtımı destekler. Sınıf herhangi bir sayıda arabirim uygulayabilir, ancak yalnızca bir sınıfı genişletebilir.
Ölümcül elmas sorununa yol açtığı için çoklu kalıtım desteklenmez. Bununla birlikte, çözülebilir, ancak karmaşık bir sisteme yol açar, bu nedenle Java kurucuları tarafından birden fazla miras bırakılır.
Şubat 1995'te James Gosling'in “Java: Genel Bakış” başlıklı beyaz kitabında ( link ), Java'da çoklu kalıtımın neden desteklenmediği hakkında bir fikir verilmektedir.
Gosling'e göre:
"JAVA, C ++ 'ın deneyimlerimize faydadan daha fazla keder getiren nadiren kullanılan, az anlaşılmış, kafa karıştırıcı birçok özelliği atlıyor. Bu öncelikle operatör aşırı yüklemesi (yöntem aşırı yüklemesine rağmen), çoklu kalıtım ve kapsamlı otomatik zorlamalardan oluşuyor."
Aynı nedenden ötürü C #, birden fazla kalıtsallığa izin vermez, ancak birden çok arabirim uygulamanızı sağlar.
C ++ 'dan çoklu kalıtımla alınan ders, değerinden daha fazla soruna yol açmasıydı.
Arayüz, sınıfınızın uygulamak zorunda olduğu şeylerin bir sözleşmesidir. Arayüzden herhangi bir işlevsellik kazanmazsınız. Miras, bir üst sınıfın işlevselliğini miras almanıza izin verir (ve çoklu mirasta, bu son derece kafa karıştırıcı olabilir).
Birden fazla arabirime izin vermek, birden fazla kalıtım kullanarak çözebileceğiniz aynı sorunları çözmek için Tasarım Desenlerini (Adaptör gibi) kullanmanıza izin verir, ancak çok daha güvenilir ve öngörülebilir bir şekilde.
D1
ve D2
hem miras B
ve her geçersiz kılma fonksiyonu f
ve eğer obj
bir tür bir örneği olan S
, hem devralır D1
ve D2
ancak geçersiz kılmaz f
, daha sonra bir referans döküm S
için D1
bir şey verim gereken f
kullanımlar D1
geçersiz kılma ve döküm B
olmamalıdır değiştir şunu. Aynı şekilde bir başvuru döküm S
için D2
olan bir şey vermelidir f
kullanımları D2
geçersiz kılma ve döküm B
olduğunu değiştirmemelidir. Bir dilin sanal üyelerin eklenmesine izin vermesi gerekmiyorsa ...
Bu konu yakın olmadığından bu yanıtı göndereceğim, umarım bu java'nın neden birden fazla kalıtıma izin vermediğini anlamasına yardımcı olur.
Aşağıdaki sınıfı düşünün:
public class Abc{
public void doSomething(){
}
}
Bu durumda Abc sınıfı hiçbir şeyi doğru uzatmıyor mu? Çok hızlı değil, bu sınıf örtük nesnesi sınıf genişletir, temel sınıf her şeyin java çalışmasına izin verir. Her şey bir nesnedir.
Eğer IDE gibi yöntemleri kullanmak için izin göreceksiniz yukarıdaki sınıfını kullanmayı denerseniz: equals(Object o)
, toString()
, vs, ama bu yöntemleri beyan etmedi, onlar temel sınıftan çıktıObject
Deneyebilirsiniz:
public class Abc extends String{
public void doSomething(){
}
}
Bu iyidir, çünkü sınıfınız örtük Object
olmayacak, ancak String
söylediğiniz için uzayacaktır . Aşağıdaki değişikliği düşünün:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
Şimdi toString () öğesini çağırırsanız sınıfınız her zaman "merhaba" döndürür.
Şimdi aşağıdaki sınıfı hayal edin:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
Yine sınıf Flyer
örtülü yöntemi vardır Nesne uzanır toString()
hepsi uzanır beri, herhangi bir sınıf bu yöntemi olacak Object
böylece, dolaylı olarak çağırırsanız, toString()
gelen Bird
hangi toString()
kullanım olurdu java? Kimden Abc
veya Flyer
? Bu, iki veya daha fazla sınıfı genişletmeye çalışan herhangi bir sınıfta gerçekleşecek, bu tür "yöntem çarpışması" ndan kaçınmak için arayüz fikrini inşa ettiler , temelde onları dolaylı olarak Nesneyi dolaylı olarak genişletmeyen soyut bir sınıf olarak düşünebilirsiniz . Bunlar olduğu soyut bunlar aşağıdakilerden biri olan bir sınıfı tarafından uygulanacak gerekecektir nesne (yalnızca bir arabirimi örnekleyemezsiniz, bunlar bir sınıf tarafından uygulanmalıdır), bu yüzden her şey iyi çalışmaya devam edecektir.
Arabirimlerden sınıfları farklı için, anahtar kelime uygular sadece arayüzleri için ayrıldı.
Varsayılan olarak hiçbir şeyi genişletmediği için aynı sınıfta istediğiniz herhangi bir arabirimi uygulayabilirsiniz (ancak başka bir arabirimi genişleten bir arabirim oluşturabilirsiniz, ancak yine "baba" arabirimi Nesneyi genişletmez), bu nedenle arabirim sadece bir arayüz ve onlar " yöntemleri imza sütunları " muzdarip olmaz , derleyici yaparsanız size bir uyarı atar ve sadece düzeltmek için yöntem imza değiştirmek zorunda kalacak (imza = yöntem adı + params + dönüş tipi) .
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
Çünkü bir arayüz sadece bir sözleşmedir. Ve bir sınıf aslında veri için bir kaptır.
Örneğin, m1 () yöntemine sahip iki A, B sınıfı. Ve C sınıfı hem A, B'yi uzatır.
class C extends A, B // for explaining purpose.
Şimdi, C sınıfı m1 tanımını arayacaktır. İlk olarak, bulamazsa sınıfta arayacak ve daha sonra ebeveyn sınıfına bakacaktır. Hem A, B tanımına sahiptir Yani burada tanımın seçilmesi gereken belirsizlik oluşur. Bu yüzden JAVA ÇOKLU MİRASI DESTEKLEMEZ.
Java, iki nedenden ötürü birden fazla kalıtım özelliğini desteklemez:
Object
. Birden fazla süper sınıftan miras alındığında, alt sınıf Object sınıfının özelliğini edinme belirsizliğini kazanır.super()
supper class yapıcısını çağırmaya çağırıyor. Sınıfın birden fazla süper sınıfı varsa, kafası karışır.Yani bir sınıf birden fazla süper sınıftan uzandığında derleme zamanı hatası alırız.
Örneğin, Sınıf A'nın bir getSomething yöntemine ve Sınıf B'nin bir getSomething yöntemine sahip olduğu ve sınıf C'nin A ve B'yi genişlettiği durumu ele alalım. Hangi yöntemi arayacağınızı belirlemenin bir yolu yoktur.
Arayüzler temel olarak sadece bir uygulayıcı sınıfın hangi yöntemleri içermesi gerektiğini belirtir. Birden fazla arabirim uygulayan bir sınıf, sınıfın tüm bu arabirimlerdeki yöntemleri uygulaması gerektiği anlamına gelir. Whci, yukarıda açıklanan herhangi bir soruna yol açmaz.
Test1, Test2 ve Test3'ün üç sınıf olduğu bir senaryo düşünün. Test3 sınıfı, Test2 ve Test1 sınıflarını devralır. Test1 ve Test2 sınıfları aynı yönteme sahipse ve bunu alt sınıf nesnesinden çağırırsanız, Test1 veya Test2 sınıfının yöntemini çağırmak için bir belirsizlik olacaktır, ancak arabirimde herhangi bir uygulama olmadığı için arabirimde böyle bir belirsizlik yoktur.
Java, belirsizlik sorunu nedeniyle çoklu kalıtım, çoklu yol ve karma kalıtım desteklemez:
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
Referans Bağlantısı: https://plus.google.com/u/0/communities/102217496457095083679
basit bir şekilde hepimizin bildiği gibi, bir sınıfı devralabilir (genişletebilir), ancak çok fazla arabirim uygulayabiliriz, çünkü arabirimlerde sadece bir işlevsellik söylemiyoruz. java çok sınıf genişletebilir ve bu aynı yöntemlere sahip varsayalım varsayalım .. bu noktada alt sınıf süper sınıf yöntemi çağırmak için çalışırsanız hangi yöntem çalıştırmak için varsayalım ??, derleyici karışık örnek olsun : - birden fazla genişletmeye çalışın ama arayüzler bu yöntemler alt sınıfta uygulamanız gereken organları yok .. birden fazla uygulama için deneyin endişe yok ..
* Bu, Java'da yeni başlayan biri olduğum için basit bir cevaptır *
Üç sınıf vardır düşünün X
, Y
ve Z
.
Bu yüzden X extends Y, Z
Ve her ikisi gibi miras alıyoruz Y
ve aynı dönüş türüne ve argümanlarına Z
sahip bir yöntem alphabet()
var. Bu yöntem alphabet()
de Y
diyor ilk alfabe görüntülemek ve yöntem alfabe Z
diyor ekran son alfabesi . İşte alphabet()
çağrıldığında belirsizlik geliyor X
. İlk veya son alfabeyi görüntüleyip görüntülemeyeceği ??? Dolayısıyla java birden fazla mirası desteklemiyor. Arayüzler söz konusu olduğunda, Y
ve Z
arayüz olarak düşünün . Yani her ikisi de yöntem bildirimini içerecek, alphabet()
ancak tanımı içermeyecektir . İlk alfabeyi mi, son alfabeyi mi yoksa herhangi bir şey mi görüntüleyeceğini söylemez, sadece bir yöntem beyan ederalphabet()
. Yani belirsizliği yükseltmek için bir sebep yok. Yöntemi sınıf içinde istediğimiz her şeyle tanımlayabiliriz X
.
Yani bir kelimeyle, Arayüzlerde tanım uygulamadan sonra yapılır, bu yüzden karışıklık olmaz.