Java หลายมรดก


168

ในความพยายามที่จะเข้าใจวิธีแก้ปัญหาการสืบทอดหลายอย่างของ Java อย่างสมบูรณ์ฉันมีคำถามแบบคลาสสิกที่ฉันต้องการชี้แจง

ให้บอกว่าฉันมีคลาสAnimalนี้มีคลาสย่อยBirdและHorseและฉันจำเป็นต้องทำคลาสPegasusที่ขยายจากBirdและHorseเนื่องจากPegasusเป็นทั้งนกและม้า

ฉันคิดว่านี่เป็นปัญหาเพชรแบบดั้งเดิม จากสิ่งที่ฉันสามารถเข้าใจวิธีที่คลาสสิกที่จะแก้ปัญหานี้คือการทำให้Animal, BirdและHorseการเชื่อมต่อการเรียนและการดำเนินการPegasusจากพวกเขา

ฉันสงสัยว่ามีวิธีอื่นในการแก้ปัญหาที่ฉันยังสามารถสร้างวัตถุสำหรับนกและม้าได้หรือไม่ หากมีวิธีที่จะสามารถสร้างสัตว์ก็จะดี แต่ไม่จำเป็น


6
ฉันคิดว่าคุณสามารถสร้างชั้นเรียนด้วยตนเองและเก็บไว้เป็นสมาชิก (องค์ประกอบแทนการสืบทอด) ด้วยพร็อกซี ( docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html ) คลาสนี้อาจเป็นตัวเลือกแม้ว่าคุณจะต้องใช้อินเทอร์เฟซเช่นกัน
Gábor Bakos

4
@RAM แล้วมันไม่ควรขยาย Bird ค่อนข้างจะมีพฤติกรรมที่จะทำให้สามารถบินได้ : แก้ปัญหา D
Yogesh

11
เผง วิธีการเกี่ยวกับอินเตอร์เฟซ CanFly :-)
โคโคนัทเซอร์ไพร์ส

28
ฉันคิดว่านี่เป็นวิธีที่ผิด คุณมีสัตว์ - ม้านก และคุณมีคุณสมบัติ - Flying, Carnivore เพกาซัสไม่ใช่ม้า แต่เป็นม้าที่บินได้ คุณสมบัติควรอยู่ในอินเตอร์เฟส public class Pegasus extends Horse implements Flyingดังนั้น
Boris the Spider

12
ฉันเข้าใจว่าทำไมคุณถึงคิดว่ามันผิดและไม่ปฏิบัติตามกฎของชีววิทยาและชื่นชมความกังวลของคุณ แต่ในส่วนที่เกี่ยวกับโปรแกรมที่ฉันต้องสร้างซึ่งจริง ๆ แล้วเกี่ยวข้องกับธนาคารนี่เป็นวิธีที่ดีที่สุดสำหรับฉัน เนื่องจากฉันไม่ต้องการโพสต์ปัญหาที่แท้จริงของฉันเนื่องจากจะขัดกับกฎฉันเปลี่ยนตัวอย่างเล็กน้อย ขอบคุณแม้ว่า ...
Sheli

คำตอบ:


115

คุณสามารถสร้างอินเทอร์เฟซสำหรับคลาสสัตว์ (คลาสในความหมายทางชีวภาพ) เช่นpublic interface Equidaeสำหรับม้าและpublic interface Avialaeนก (ฉันไม่ใช่นักชีววิทยาดังนั้นคำศัพท์อาจผิด)

จากนั้นคุณยังสามารถสร้าง

public class Bird implements Avialae {
}

และ

public class Horse implements Equidae {}

และนอกจากนี้ยังมี

public class Pegasus implements Avialae, Equidae {}

เพิ่มจากความคิดเห็น:

เพื่อลดรหัสที่ซ้ำกันคุณสามารถสร้างคลาสนามธรรมที่มีรหัสทั่วไปของสัตว์ที่คุณต้องการนำไปใช้

public abstract class AbstractHorse implements Equidae {}

public class Horse extends AbstractHorse {}

public class Pegasus extends AbstractHorse implements Avialae {}

ปรับปรุง

ฉันต้องการเพิ่มรายละเอียดอีกหนึ่งรายการ ขณะที่ไบรอันพูดนี้เป็นสิ่งที่ OP รู้อยู่แล้ว

อย่างไรก็ตามฉันต้องการเน้นย้ำว่าฉันขอแนะนำให้หลีกเลี่ยงปัญหา "การสืบทอดหลายอย่าง" กับส่วนต่อประสานและฉันไม่แนะนำให้ใช้ส่วนต่อประสานที่แสดงประเภทที่เป็นรูปธรรมอยู่แล้ว (เช่น Bird) แต่มีพฤติกรรมมากกว่า พิมพ์ดีดเป็ดซึ่งก็ดีเหมือนกัน แต่ฉันหมายถึงเพียงแค่: นกชีววิทยาระดับ Avialae) ฉันไม่แนะนำให้ใช้ชื่ออินเทอร์เฟซที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่ 'I' เช่นIBirdซึ่งเพิ่งบอกอะไรเกี่ยวกับสาเหตุที่คุณต้องการอินเทอร์เฟซ นั่นคือความแตกต่างของคำถาม: สร้างลำดับชั้นการสืบทอดโดยใช้อินเตอร์เฟสใช้คลาสนามธรรมเมื่อมีประโยชน์ใช้คลาสคอนกรีตที่จำเป็นและใช้การมอบหมายหากเหมาะสม


9
ซึ่ง ... คือสิ่งที่ OP บอกว่าพวกเขารู้ว่าคุณสามารถทำได้ในคำถาม
Brian Roach

4
เนื่องจาก Pegasus เป็นม้าอยู่แล้ว (นั่นคือแมลงวัน) ฉันคิดว่าคุณสามารถใช้รหัสเพิ่มเติมได้ถ้าขยาย Horse และใช้ Avialae
Pablo Lozano

8
ฉันไม่แน่ใจฉันยังไม่เคยเห็น Pegasus เลย อย่างไรก็ตามในกรณีนี้ฉันต้องการใช้ a AbstractHorseซึ่งสามารถใช้สร้าง Zebras หรือสัตว์ที่เหมือนม้าอื่น ๆ ได้
Moritz Petersen

5
@MoritzPetersen ถ้าคุณต้องการที่จะเข้าไปในการนำแนวคิดและให้ชื่อมีความหมายจริงๆอาจจะเหมาะสมกว่าAbstractEquidae AbstractHorseมันคงจะแปลกที่การมีม้าลายขยายม้าที่เป็นนามธรรม คำตอบที่ดีโดยวิธีการ
afsantos

3
การใช้การพิมพ์เป็ดจะเกี่ยวข้องกับการDuckใช้คลาสAvialaeด้วยหรือไม่
mgarciaisaia

88

มีสองวิธีพื้นฐานในการรวมวัตถุเข้าด้วยกัน:

  • ครั้งแรกเป็นมรดก ตามที่คุณระบุข้อ จำกัด ของการสืบทอดหมายความว่าคุณไม่สามารถทำสิ่งที่คุณต้องการได้ที่นี่
  • ประการที่สองคือองค์ประกอบ เนื่องจากการสืบทอดล้มเหลวคุณต้องใช้การแต่งเพลง

วิธีการทำงานนี้คือคุณมีวัตถุสัตว์ ภายในวัตถุนั้นคุณจะเพิ่มวัตถุเพิ่มเติมที่ให้คุณสมบัติและพฤติกรรมที่คุณต้องการ

ตัวอย่างเช่น:

  • นกขยายการใช้สัตว์IFlier
  • ม้าขยายการดำเนินการสัตว์IHerbivore, IQuadruped
  • เพกาซัสขยายการดำเนินการสัตว์IHerbivore, IQuadruped, IFlier

ตอนนี้IFlierดูเหมือนว่า:

 interface IFlier {
     Flier getFlier();
 }

ดังนั้นBirdดูเหมือนว่านี้:

 class Bird extends Animal implements IFlier {
      Flier flier = new Flier();
      public Flier getFlier() { return flier; }
 }

ตอนนี้คุณมีข้อดีทั้งหมดของการสืบทอด คุณสามารถใช้รหัสซ้ำได้ คุณสามารถรวบรวม IFliers และสามารถใช้ข้อดีอื่น ๆ ของ polymorphism เป็นต้น

อย่างไรก็ตามคุณยังมีความยืดหยุ่นทั้งหมดจากองค์ประกอบ คุณสามารถใช้อินเทอร์เฟซที่แตกต่างกันและคลาสการสำรองข้อมูลคอมโพสิตเท่าที่คุณต้องการกับแต่ละประเภทAnimal- ด้วยการควบคุมมากเท่าที่คุณต้องการในการตั้งค่าบิตแต่ละบิต

รูปแบบกลยุทธ์วิธีการทางเลือกในการจัดองค์ประกอบ

อีกวิธีหนึ่งที่ขึ้นอยู่กับว่าคุณกำลังทำอะไรและทำอย่างไรเพื่อให้Animalคลาสพื้นฐานมีคอลเลกชันภายในเพื่อเก็บรายการพฤติกรรมที่แตกต่าง ในกรณีนี้คุณจะใช้สิ่งที่ใกล้เคียงกับรูปแบบกลยุทธ์ สิ่งนี้จะให้ประโยชน์ในแง่ของการทำให้โค้ดง่ายขึ้น (ตัวอย่างเช่นHorseไม่จำเป็นต้องรู้อะไรเกี่ยวกับQuadrupedหรือHerbivore) แต่ถ้าคุณไม่ทำเช่นนั้นด้วยอินเตอร์เฟสที่ทำให้คุณเสียข้อดีมากมายของ polymorphism เป็นต้น


ทางเลือกที่คล้ายกันอาจใช้คลาสประเภทแม้ว่าจะไม่เป็นธรรมชาติที่จะใช้ใน Java (คุณต้องใช้วิธีการแปลงเป็นต้น) คำนำนี้อาจมีประโยชน์ในการรับแนวคิด: typeclassopedia.bitbucket.org
Gábor Bakos

1
นี่เป็นวิธีที่ดีกว่ามากแล้ววิธีที่แนะนำในคำตอบที่ยอมรับได้ ตัวอย่างเช่นหากภายหลังฉันต้องการเพิ่ม "beak color" ในอินเทอร์เฟซ Bird ฉันมีปัญหา เพกาซัสเป็นส่วนประกอบประกอบจากม้าและนก แต่ไม่ได้ม้าและนกอย่างเต็มที่ การใช้องค์ประกอบทำให้รู้สึกที่สมบูรณ์แบบในสถานการณ์นี้
JDB ยังจำโมนิก้าได้

แต่getFlier()จะต้องมีการนำมาใช้ใหม่สำหรับนกทุกชนิด
SMUsamaShah

1
@ LifeH2O แยกพวกมันออกเป็นบล็อกของฟังก์ชั่นการแบ่งปันแล้วบอกพวกเขาว่า เช่นคุณอาจมีMathsTeacherและEnglishTeacherทั้งสืบทอดTeacher, ChemicalEngineer, MaterialsEngineerฯลฯ Engineerสืบทอด TeacherและEngineerใช้งานทั้งสองComponentอย่าง Personแล้วก็มีรายชื่อของComponents และคุณสามารถให้พวกเขาที่เหมาะสมสำหรับการที่Component Personเช่นperson.getComponent(Teacher.class), person.getComponent(MathsTeacher.class)ฯลฯ
Tim B

1
นี่คือคำตอบที่ดีที่สุด ตามกฎของหัวแม่มือการสืบทอดนั้นแทน "is" และอินเตอร์เฟสแทน "can" เพกาซัสเป็นสัตว์ที่สามารถบินได้และเดินได้ นกเป็นสัตว์ที่สามารถบินได้ ม้าเป็นสัตว์ที่สามารถเดินได้
Robear

43

ฉันมีความคิดที่โง่:

public class Pegasus {
    private Horse horseFeatures; 
    private Bird birdFeatures; 

   public Pegasus(Horse horse, Bird bird) {
     this.horseFeatures = horse;
     this.birdFeatures = bird;
   }

  public void jump() {
    horseFeatures.jump();
  }

  public void fly() {
    birdFeatures.fly();
  }
}

24
มันจะได้ผล แต่ฉันไม่ชอบวิธีการแบบนั้น (แรปเปอร์เพอร์) เพราะดูเหมือนว่าเพกาซัสจะมีม้าแทนที่จะเป็นม้า
Pablo Lozano

ขอบคุณ. ฉันไม่สามารถช่วยตัวเองโพสต์ความคิด ฉันรู้ว่ามันโง่ แต่ฉันไม่เห็นว่าทำไมมันถึงโง่ ...
Pavel Janicek

3
มันเกือบจะเหมือนวิธีเรียงความเวอร์ชั่นที่ไม่มีข้อ จำกัด จากคำตอบของ Tim B
เตฟาน

1
นี่เป็นวิธีที่ได้รับการยอมรับมากกว่าหรือน้อยกว่าแม้ว่าคุณควรจะมีบางอย่างเช่น IJumps ด้วยวิธี "กระโดด" ที่นำมาใช้โดย Horse และ Pegasus และ IFlies ด้วยวิธี "บิน" ที่ดำเนินการโดย Bird และ Pegasus
MatsT

2
@Pablo no, Pegasus HAS มีคุณสมบัติของม้าและ HAS birdFeatures +1 สำหรับคำตอบเพราะมันช่วยให้โค้ดง่าย ๆ ซึ่งแตกต่างจาก clunkier, class-spawning, Java solution ที่เหมาะสม
JaneGoodall

25

ฉันขอแนะนำแนวคิดของการพิมพ์เป็ดได้ไหม

ส่วนใหญ่มีแนวโน้มที่คุณจะมีแนวโน้มที่จะทำให้เพกาซัสขยายนกและอินเตอร์เฟซ แต่เป็ดม้าพิมพ์จริงแสดงให้เห็นว่าคุณควรจะค่อนข้างสืบทอดพฤติกรรม ตามที่ระบุไว้แล้วในความคิดเห็นเพกาซัสไม่ได้เป็นนก แต่มันสามารถบินได้ ดังนั้นแรงบันดาลใจของคุณจึงควรสืบทอดFlyable-interface และให้พูดว่าGallopable-interface

ชนิดของแนวคิดนี้ถูกนำมาใช้ในรูปแบบกลยุทธ์ ตัวอย่างที่ได้รับจริงแสดงให้คุณเห็นวิธีการที่สืบทอดเป็ดFlyBehaviourและQuackBehaviourและยังคงสามารถมีได้เป็ดเช่นRubberDuckซึ่งไม่สามารถบินได้ พวกเขาจะได้ทำยังDuckขยายBird-class แต่แล้วพวกเขาก็จะได้รับค่าความยืดหยุ่นบางเพราะทุกจะสามารถที่จะบินแม้จะยากจนDuckRubberDuck


19

ในทางเทคนิคการพูดคุณสามารถขยายคลาสได้ครั้งละหนึ่งคลาสและใช้หลายอินเตอร์เฟส แต่เมื่อวางมือบนวิศวกรรมซอฟต์แวร์ฉันอยากจะแนะนำวิธีแก้ปัญหาเฉพาะที่ไม่สามารถตอบได้โดยทั่วไป โดยวิธีการมันเป็นวิธีปฏิบัติที่ดี OO ไม่ต้องขยายชั้นเรียนที่เป็นรูปธรรม / ขยายชั้นเรียนที่เป็นนามธรรมเท่านั้นเพื่อป้องกันพฤติกรรมการรับมรดกที่ไม่พึงประสงค์ - ไม่มีสิ่งเช่น "สัตว์" และไม่มีการใช้วัตถุสัตว์ แต่สัตว์คอนกรีตเท่านั้น


13

ใน Java 8 ซึ่งยังอยู่ในช่วงการพัฒนา ณ เดือนกุมภาพันธ์ 2014 คุณสามารถใช้วิธีการเริ่มต้นเพื่อให้ได้ C ++ เหมือนมรดกหลาย ๆ อย่าง คุณสามารถดูบทช่วยสอนนี้ซึ่งแสดงตัวอย่างบางส่วนที่ควรเริ่มทำงานได้ง่ายกว่าเอกสารคู่มืออย่างเป็นทางการ


1
โปรดทราบว่าหากเบิร์ดและฮอร์สของคุณมีวิธีการเริ่มต้นคุณจะยังคงพบกับปัญหาเพชรและจะต้องนำไปใช้แยกต่างหากในคลาสเพกาซัสของคุณ (หรือรับข้อผิดพลาดคอมไพเลอร์)
Mikkel Løkke

@Mikkel Løkke: คลาส Pegasus มีการกำหนด overrides สำหรับวิธีการที่ไม่ชัดเจน แต่สามารถนำไปใช้ได้โดยเพียงมอบหมายวิธี super (หรือทั้งสองอย่างในลำดับที่เลือก)
Holger

12

มันปลอดภัยที่จะเก็บม้าไว้ในคอกม้าที่มีประตูครึ่งเนื่องจากม้าไม่สามารถผ่านได้ครึ่งประตู ดังนั้นฉันจึงตั้งค่าบริการที่อยู่อาศัยของม้าที่รับสิ่งของประเภทม้าและทำให้มันอยู่ในที่มั่นคงด้วยครึ่งประตู

ดังนั้นม้าเหมือนสัตว์ที่สามารถบินได้แม้กระทั่งม้าหรือไม่?

ฉันเคยคิดมากเกี่ยวกับมรดกหลายอย่าง แต่ตอนนี้ฉันได้เขียนโปรแกรมมานานกว่า 15 ปีแล้วฉันไม่สนใจที่จะใช้มรดกหลายอย่างอีกต่อไป

บ่อยครั้งกว่าเมื่อฉันพยายามรับมือกับการออกแบบที่ชี้ไปที่การสืบทอดหลายแบบฉันได้มาเผยแพร่ในภายหลังว่าฉันพลาดเข้าใจโดเมนปัญหา

หรือ

หากดูเหมือนเป็ดและ quacks เช่นเป็ด แต่จะต้องแบตเตอรี่คุณอาจจะมีสิ่งที่เป็นนามธรรมที่ไม่ถูกต้อง


ถ้าฉันอ่านการเปรียบเทียบของคุณถูกต้องคุณกำลังบอกว่าในหน้าจอเริ่มต้นนั้นดูยอดเยี่ยมเพราะพวกเขาให้คุณแก้ปัญหาการออกแบบเช่นคุณสามารถใช้ API ของคนอื่นโดยบังคับให้คลาสของคุณผ่านอินเทอร์เฟซของพวกเขา แต่หลังจากผ่านไปหลายปีคุณก็รู้ว่าปัญหาคือการออกแบบที่ไม่ดีตั้งแต่แรก
CS

8

Java ไม่มีปัญหาการสืบทอดหลายอย่างเนื่องจากไม่มีหลายการสืบทอด นี่คือโดยการออกแบบเพื่อที่จะแก้ปัญหาการสืบทอดหลายจริง (ปัญหาเพชร)

มีกลยุทธ์ที่แตกต่างกันในการบรรเทาปัญหา สิ่งที่สามารถทำได้ในทันทีคือวัตถุคอมโพสิตที่พาเวลเสนอ (โดยพื้นฐานแล้ววิธีการจัดการ C ++) ฉันไม่รู้ว่าการสืบทอดหลายทางผ่านการจัดวางเชิงเส้น C3 (หรือคล้ายกัน) อยู่บนการ์ดสำหรับอนาคตของ Java หรือไม่ แต่ฉันสงสัย

หากคำถามของคุณเป็นวิชาการการแก้ปัญหาที่ถูกต้องก็คือเบิร์ดแอนด์ฮอร์มีรูปธรรมมากขึ้นและเป็นเรื่องผิดถ้าคิดว่าเพกาซัสเป็นเพียงการรวมกันระหว่างนกกับม้า มันจะถูกต้องมากขึ้นถ้าจะพูดว่า Pegasus มีคุณสมบัติบางอย่างที่เหมือนกันกับนกและม้า (นั่นคือพวกมันอาจมีบรรพบุรุษร่วมกัน) นี่เป็นรูปแบบที่เพียงพอเมื่อคำตอบของ Moritz ชี้ให้เห็น


6

ฉันคิดว่ามันขึ้นอยู่กับความต้องการของคุณเป็นอย่างมากและวิธีการเรียนสัตว์ของคุณในรหัสของคุณ

หากคุณต้องการที่จะใช้ประโยชน์จากวิธีการและคุณสมบัติของการใช้งาน Horse และ Bird ในคลาส Pegasus ของคุณคุณสามารถใช้ Pegasus เป็นองค์ประกอบของ Bird and a Horse:

public class Animals {

    public interface Animal{
        public int getNumberOfLegs();
        public boolean canFly();
        public boolean canBeRidden();
    }

    public interface Bird extends Animal{
        public void doSomeBirdThing();
    }
    public interface Horse extends Animal{
        public void doSomeHorseThing();
    }
    public interface Pegasus extends Bird,Horse{

    }

    public abstract class AnimalImpl implements Animal{
        private final int numberOfLegs;

        public AnimalImpl(int numberOfLegs) {
            super();
            this.numberOfLegs = numberOfLegs;
        }

        @Override
        public int getNumberOfLegs() {
            return numberOfLegs;
        }
    }

    public class BirdImpl extends AnimalImpl implements Bird{

        public BirdImpl() {
            super(2);
        }

        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return false;
        }

        @Override
        public void doSomeBirdThing() {
            System.out.println("doing some bird thing...");
        }

    }

    public class HorseImpl extends AnimalImpl implements Horse{

        public HorseImpl() {
            super(4);
        }

        @Override
        public boolean canFly() {
            return false;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }

        @Override
        public void doSomeHorseThing() {
            System.out.println("doing some horse thing...");
        }

    }

    public class PegasusImpl implements Pegasus{

        private final Horse horse = new HorseImpl();
        private final Bird bird = new BirdImpl();


        @Override
        public void doSomeBirdThing() {
            bird.doSomeBirdThing();
        }

        @Override
        public int getNumberOfLegs() {
            return horse.getNumberOfLegs();
        }

        @Override
        public void doSomeHorseThing() {
            horse.doSomeHorseThing();
        }


        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }
    }
}

ความเป็นไปได้อีกอย่างหนึ่งคือการใช้วิธีการเอ็นทิตี้ - ส่วนประกอบ - ระบบแทนการสืบทอดเพื่อกำหนดสัตว์ของคุณ หลักสูตรนี้หมายความว่าคุณจะไม่ได้มีคลาส Java ของสัตว์แต่ละตัว แต่จะถูกกำหนดโดยส่วนประกอบของสัตว์เหล่านั้นเท่านั้น

โค้ดหลอกบางอย่างสำหรับแนวทาง Entity-Component-System อาจมีลักษณะเช่นนี้:

public void createHorse(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 4);
    entity.setComponent(CAN_FLY, false);
    entity.setComponent(CAN_BE_RIDDEN, true);
    entity.setComponent(SOME_HORSE_FUNCTIONALITY, new HorseFunction());
}

public void createBird(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 2);
    entity.setComponent(CAN_FLY, true);
    entity.setComponent(CAN_BE_RIDDEN, false);
    entity.setComponent(SOME_BIRD_FUNCTIONALITY, new BirdFunction());
}

public void createPegasus(Entity entity){
    createHorse(entity);
    createBird(entity);
    entity.setComponent(CAN_BE_RIDDEN, true);
}

4

คุณสามารถมีลำดับชั้นของอินเทอร์เฟซแล้วขยายคลาสของคุณจากอินเทอร์เฟซที่เลือก:

public interface IAnimal {
}

public interface IBird implements IAnimal {
}

public  interface IHorse implements IAnimal {
}

public interface IPegasus implements IBird,IHorse{
}

จากนั้นกำหนดคลาสของคุณตามต้องการโดยขยายอินเทอร์เฟซเฉพาะ:

public class Bird implements IBird {
}

public class Horse implements IHorse{
}

public class Pegasus implements IPegasus {
}

1
หรือเขาทำได้เพียง: คลาสสาธารณะเพกาซัสขยาย Animal Implements Horse, Bird
Batman

OP ได้รับทราบถึงวิธีนี้แล้วเขากำลังมองหาวิธีอื่นในการทำเช่นนั้น
Yogesh

@Batman แน่นอนเขา canm แต่ถ้าเขาต้องการที่จะขยายลำดับชั้นของเขาจะต้องทำตามวิธีนี้
richardtz

IBirdและIHorseควรใช้งานIAnimalแทนAnimal
oliholz

@Yogesh คุณพูดถูก ฉันมองข้ามสถานที่ซึ่งเขากล่าวไว้ ในฐานะ "มือใหม่" ฉันควรทำอย่างไรตอนนี้ลบคำตอบหรือทิ้งไว้ที่นั่นขอบคุณ
richardtz

4

Ehm คลาสของคุณสามารถเป็นคลาสย่อยได้เพียง 1 คลาสเท่านั้น แต่ยังคงคุณสามารถใช้อินเทอร์เฟซได้มากเท่าที่คุณต้องการ

เพกาซัสเป็นม้าจริง (เป็นกรณีพิเศษของม้า) ซึ่งสามารถบินได้ (ซึ่งเป็น "ทักษะ" ของม้าพิเศษนี้) จากอีกด้านหนึ่งคุณสามารถพูดได้ว่า Pegasus เป็นนกที่สามารถเดินได้และถูก 4legged - ทั้งหมดขึ้นอยู่กับว่ามันง่ายสำหรับคุณที่จะเขียนรหัสอย่างไร

เช่นเดียวกับในกรณีของคุณคุณสามารถพูดได้:

abstract class Animal {
   private Integer hp = 0; 
   public void eat() { 
      hp++; 
   }
}
interface AirCompatible { 
   public void fly(); 
}
class Bird extends Animal implements AirCompatible { 
   @Override
   public void fly() {  
       //Do something useful
   }
} 
class Horse extends Animal {
   @Override
   public void eat() { 
      hp+=2; 
   }

}
class Pegasus extends Horse implements AirCompatible {
   //now every time when your Pegasus eats, will receive +2 hp  
   @Override
   public void fly() {  
       //Do something useful
   }
}

3

การเชื่อมต่อไม่ได้จำลองการสืบทอดหลายรายการ ผู้สร้าง Java พิจารณาว่าการสืบทอดหลายอย่างไม่ถูกต้องดังนั้นจึงไม่มีสิ่งดังกล่าวใน Java

หากคุณต้องการรวมการทำงานของสองคลาสเข้าด้วยกัน - ใช้การจัดองค์ประกอบวัตถุ กล่าวคือ

public class Main {
    private Component1 component1 = new Component1();    
    private Component2 component2 = new Component2();
}

และหากคุณต้องการแสดงวิธีการบางอย่างให้กำหนดวิธีการนั้นและให้พวกเขามอบหมายการเรียกไปยังตัวควบคุมที่เกี่ยวข้อง

อินเทอร์เฟซอาจมีประโยชน์ - ถ้าComponent1ใช้อินเทอร์เฟซInterface1และComponent2ใช้งานInterface2คุณสามารถกำหนดได้

class Main implements Interface1, Interface2

เพื่อให้คุณสามารถใช้วัตถุแทนกันโดยที่บริบทอนุญาต

ในมุมมองของฉันคุณไม่สามารถเข้าใจปัญหาเพชรได้


มันไม่ผิดในลักษณะเดียวกับที่ตัวชี้หน่วยความจำโดยตรงประเภทที่ไม่ได้ลงนามและการใช้งานมากเกินไปจะไม่ผิด ไม่จำเป็นต้องทำให้งานเสร็จ Java ได้รับการออกแบบให้เป็นภาษาที่ง่ายต่อการหยิบ อย่าทำอะไรและหวังให้ดีที่สุดนี่คือความรู้ไม่ใช่การเดา
Gimby

3

ดังที่คุณจะได้รับทราบการสืบทอดหลายคลาสใน Java นั้นเป็นไปไม่ได้ แต่เป็นไปได้ด้วยอินเตอร์เฟส คุณอาจต้องการพิจารณาใช้รูปแบบการออกแบบองค์ประกอบ

ฉันเขียนบทความที่ครอบคลุมมากเกี่ยวกับการเขียนเรียงความเมื่อไม่กี่ปีที่ผ่านมา ...

/codereview/14542/multiple-inheritance-and-composition-with-java-and-c-updated


3
  1. กำหนดอินเตอร์เฟสสำหรับการกำหนดความสามารถ คุณสามารถกำหนดหลายอินเตอร์เฟสสำหรับความสามารถหลายอย่าง ความสามารถเหล่านี้สามารถนำไปใช้ได้โดยสัตว์หรือนกเฉพาะ
  2. ใช้การสืบทอดเพื่อสร้างความสัมพันธ์ระหว่างคลาสโดยการแบ่งปันข้อมูล / วิธีการที่ไม่คงที่และไม่เป็นสาธารณะ
  3. ใช้Decorator_patternเพื่อเพิ่มความสามารถแบบไดนามิก สิ่งนี้จะช่วยให้คุณลดจำนวนคลาสและชุดค่าผสมที่สืบทอดได้

ดูตัวอย่างด้านล่างเพื่อความเข้าใจที่ดีขึ้น

เมื่อใดที่จะใช้รูปแบบมัณฑนากร?


2

เพื่อลดความซับซ้อนและลดความซับซ้อนของภาษาการสืบทอดหลายรายการไม่ได้รับการสนับสนุนในภาษาจาวา

พิจารณาสถานการณ์ที่ A, B และ C เป็นสามคลาส คลาส C สืบทอดคลาส A และ B ถ้าคลาส A และ B มีเมธอดเดียวกันและคุณเรียกมันจากอ็อบเจ็กต์คลาส child จะมีความกำกวมในการเรียกเมธอดของคลาส A หรือ B

เนื่องจากข้อผิดพลาดในการคอมไพล์เวลานั้นดีกว่าข้อผิดพลาดแบบรันไทม์, จาวาแสดงผลข้อผิดพลาดในการคอมไพล์เวลาถ้าคุณสืบทอด 2 คลาส ดังนั้นไม่ว่าคุณจะมีวิธีการเดียวกันหรือแตกต่างกันจะมีข้อผิดพลาดในการรวบรวมเวลาในขณะนี้

class A {  
    void msg() {
        System.out.println("From A");
    }  
}

class B {  
    void msg() {
        System.out.println("From B");
    }  
}

class C extends A,B { // suppose if this was possible
    public static void main(String[] args) {  
        C obj = new C();  
        obj.msg(); // which msg() method would be invoked?  
    }
} 

2

เพื่อแก้ปัญหาการสืบทอด mutiple ใน Java →อินเตอร์เฟส

J2EE (core JAVA) หมายเหตุโดย Mr. KVR หน้า 51

วันที่ - 27

  1. อินเตอร์เฟสใช้โดยทั่วไปในการพัฒนาชนิดข้อมูลที่ผู้ใช้กำหนด
  2. ด้วยความเคารพต่ออินเตอร์เฟสเราสามารถบรรลุแนวคิดของการสืบทอดหลาย ๆ อย่าง
  3. ด้วยอินเตอร์เฟสเราสามารถบรรลุแนวคิดของ polymorphism การเชื่อมโยงแบบไดนามิกและด้วยเหตุนี้เราสามารถปรับปรุงประสิทธิภาพของโปรแกรม JAVA ในการเปลี่ยนพื้นที่หน่วยความจำและเวลาดำเนินการ

อินเทอร์เฟซเป็นโครงสร้างที่ประกอบด้วยคอลเลกชันของวิธีที่ไม่ได้กำหนดอย่างหมดจดหรืออินเทอร์เฟซคือชุดของวิธีนามธรรมอย่างหมดจด

[ ... ]

วันที่ - 28:

ไวยากรณ์ -1 สำหรับการนำคุณสมบัติของอินเตอร์เฟสไปใช้กับคลาสอีกครั้ง:

[abstract] class <clsname> implements <intf 1>,<intf 2>.........<intf n>
{
    variable declaration;
    method definition or declaration;
};

ใน clsname ไวยากรณ์ข้างต้นแสดงถึงชื่อของคลาสซึ่งสืบทอดคุณสมบัติจากจำนวนอินเตอร์เฟส 'n' 'การดำเนินการ' เป็นคำหลักที่ใช้ในการสืบทอดคุณสมบัติของส่วนต่อประสานไปยังคลาสที่ได้รับ

[ ... ]

Syntax-2 ที่รับจำนวนอินเทอร์เฟซ 'n' ไปยังอินเตอร์เฟสอื่น:

interface <intf 0 name> extends <intf 1>,<intf 2>.........<intf n>
{     
    variable declaration cum initialization;
    method declaration;
};

[ ... ]

ไวยากรณ์ที่ 3:

[abstract] class <derived class name> extends <base class name> implements <intf 1>,<intf 2>.........<intf n>
{
  variable declaration;
  method definition or declaration;
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.