เหตุใดจึงไม่มีการสืบทอดหลายรายการใน Java แต่อนุญาตให้ใช้หลายอินเตอร์เฟสได้


153

Java ไม่อนุญาตให้มีการสืบทอดหลายรายการ แต่อนุญาตให้ใช้หลายอินเตอร์เฟสได้ ทำไม?


1
ฉันแก้ไขชื่อคำถามเพื่อให้มีความหมายมากขึ้น
Bozho

4
ที่น่าสนใจใน JDK 8 นั้นจะมีวิธีการขยายซึ่งจะช่วยให้คำจำกัดความของการใช้วิธีการอินเตอร์เฟซ กฎจะถูกกำหนดให้อยู่ในการปกครองมรดกหลายพฤติกรรม แต่ไม่ได้ของรัฐ (ซึ่งผมเข้าใจมากขึ้นปัญหา .
เอ็ดวิน Dalorzo

1
ก่อนที่พวกคุณจะเสียเวลากับคำตอบที่บอกคุณว่า "วิธีการที่ java ได้รับมรดกมาหลายวิธี" ... ฉันแนะนำให้คุณไปที่ @Prabal Srivastava คำตอบที่มีเหตุผลจะทำให้คุณเกิดสิ่งที่เกิดขึ้นภายใน . และอนุญาตเฉพาะส่วนต่อประสานทางด้านขวาเพื่อให้สามารถรับมรดกได้หลายอย่าง
Yo Apps

คำตอบ:


228

เพราะอินเตอร์เฟซที่ระบุเพียงแค่สิ่งที่ชั้นจะทำไม่ว่ามันจะทำมัน

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


8
ฉันเคยทำ C ++ และพบปัญหาเดียวกันนี้ค่อนข้างบ่อยครั้ง เมื่อเร็ว ๆ นี้ฉันอ่านเกี่ยวกับสกาล่าว่ามี "ลักษณะ" ที่สำหรับฉันดูเหมือนบางสิ่งระหว่างทาง "C ++" และวิธี "Java" ในการทำสิ่งต่าง ๆ
Niels Basjes

2
ด้วยวิธีนี้คุณจะหลีกเลี่ยง "ปัญหาเพชร": en.wikipedia.org/wiki/Diamond_problem#The_diamond_problem
Nick L.

6
ตั้งแต่ Java 8 คุณสามารถกำหนดวิธีการเริ่มต้นที่เหมือนกันสองวิธีหนึ่งในแต่ละอินเตอร์เฟส หากคุณจะใช้อินเทอร์เฟซทั้งสองในชั้นเรียนของคุณคุณจะต้องแทนที่วิธีนี้ในชั้นเรียนของตัวเองดู: docs.oracle.com/javase/tutorial/java/IandI/…
bobbel

6
คำตอบนี้ไม่ถูกต้อง ปัญหาไม่ได้ระบุวิธีการและ Java 8 อยู่ที่นั่นเพื่อพิสูจน์ ใน Java 8 ซูเปอร์อินเทอร์เฟซสองรายการสามารถประกาศวิธีการเดียวกันกับการใช้งานที่แตกต่างกัน แต่นั่นไม่ใช่ปัญหาเนื่องจากวิธีการอินเทอร์เฟซเสมือนจริงดังนั้นคุณจึงสามารถเขียนทับพวกเขาและแก้ปัญหาได้ ปัญหาที่แท้จริงนั้นเกี่ยวกับความกำกวมในแอตทริบิวต์เนื่องจากคุณไม่สามารถแก้ปัญหาความคลุมเครือนี้ได้เนื่องจากคุณลักษณะ (ไม่ใช่แอตทริบิวต์)
Alex Oliveira

1
"แอตทริบิวต์" หมายความว่าอย่างไร
Bozho

96

อาจารย์คนหนึ่งในวิทยาลัยของฉันอธิบายให้ฉันฟังแบบนี้

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

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

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


5
ขอบคุณ NomeN แหล่งอ้างอิงเป็นนักศึกษาปริญญาเอกชื่อเบรนแดนเบิร์นส์ซึ่งในตอนนั้นยังเป็นผู้ดูแลแหล่งเก็บข้อมูลโอเพ่นซอร์ส Quake 2 ด้วย ไปคิด
วากยสัมพันธ์

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

20
หากมีคนตัดสินใจที่จะผสมระเบิดนิวเคลียร์และเครื่องปิ้งขนมปังเขาสมควรได้รับว่าระเบิดดังขึ้นในใบหน้าของเขา อ้างเป็นเหตุผลที่ผิดพลาด
masoud

1
ภาษาการเขียนโปรแกรมเดียวกันอนุญาตให้มีการสืบทอด "ส่วนต่อประสาน" หลายรายการดังนั้นจึงไม่มีการใช้เหตุผล
curiousguy

24

เนื่องจากการรับมรดกมากเกินไปแม้ว่าคุณจะไม่สามารถพูดว่า "เฮ้วิธีการนั้นดูมีประโยชน์ฉันจะขยายคลาสนั้นด้วย"

public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter, 
             AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...

คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงบอกว่ามรดกมีมากเกินไป? การสร้างคลาสพระเจ้านั้นเป็นสิ่งที่ฉันต้องการจะทำ! ฉันพบคนจำนวนมากที่ทำงานเกี่ยวกับการสืบทอดเดี่ยวโดยการสร้างคลาส "เครื่องมือ" ที่มีวิธีการคงที่
Duncan Calvert

9
@DuncanCalvert: ไม่คุณไม่ต้องการทำเช่นนั้นไม่ใช่ว่ารหัสนั้นจะต้องมีการบำรุงรักษา วิธีการแบบคงที่จำนวนมากพลาดจุด OO แต่การสืบทอดหลายครั้งที่มากเกินไปนั้นแย่กว่ามากเพราะคุณสูญเสียการติดตามรหัสที่ใช้ไปในที่ใดรวมถึงคลาสที่เป็นแนวคิด ทั้งคู่พยายามที่จะแก้ปัญหาของ "ฉันจะใช้รหัสนี้ในที่ที่ฉันต้องการได้อย่างไร" แต่นั่นเป็นปัญหาระยะสั้นง่ายๆ ปัญหาระยะยาวที่แก้ไขได้ยากขึ้นโดยการออกแบบ OO ที่เหมาะสมคือ "ฉันจะเปลี่ยนรหัสนี้ได้อย่างไรโดยไม่ทำให้โปรแกรมแตกในที่ต่างๆ 20 แห่งในรูปแบบที่ไม่สามารถคาดการณ์ได้?
Michael Borgwardt

2
@DuncanCalvert: และคุณแก้ปัญหาด้วยการมีชั้นเรียนที่มีการติดต่อกันสูงและการมีเพศสัมพันธ์ต่ำซึ่งหมายความว่าพวกเขามีชิ้นส่วนของข้อมูลและรหัสที่โต้ตอบกันอย่างเข้มข้น แต่โต้ตอบกับส่วนที่เหลือของโปรแกรมผ่าน API สาธารณะขนาดเล็กที่เรียบง่าย จากนั้นคุณสามารถคิดถึงพวกเขาในแง่ของ API นั้นแทนรายละเอียดภายในซึ่งเป็นสิ่งสำคัญเพราะผู้คนสามารถจดจำรายละเอียดจำนวน จำกัด ได้พร้อมกัน
Michael Borgwardt

18

คำตอบของคำถามนี้อยู่ในการทำงานภายในของ java compiler (constructor chaining) ถ้าเราเห็นการทำงานภายในของคอมไพเลอร์ java:

public class Bank {
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI extends Bank{
 public void printBankBalance(){
    System.out.println("20k");
  }
}

หลังจากรวบรวมลักษณะนี้:

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");
  }
}

เมื่อเราขยายคลาสและสร้างวัตถุของมันโซ่คอนสตรัคเตอร์หนึ่งจะทำงานจนถึงObjectคลาส

โค้ดด้านบนจะทำงานได้ดี แต่ถ้าเรามีคลาสอีกคลาสหนึ่งที่เรียกว่าคลาสCarที่ขยายBankและหนึ่งคลาสไฮบริด (หลายมรดก) เรียกว่า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");
  }
}

ในกรณีนี้ (SBICar) จะล้มเหลวในการสร้างตัวสร้างโซ่ ( รวบรวมความกำกวมเวลา )

สำหรับส่วนต่อประสานสิ่งนี้ได้รับอนุญาตเพราะเราไม่สามารถสร้างวัตถุของมันได้

สำหรับแนวคิดใหม่ของการdefaultและstaticวิธีการกรุณาดูค่าเริ่มต้นในอินเตอร์เฟซ

หวังว่านี่จะแก้ปัญหาการค้นหาของคุณ ขอบคุณ


7

การใช้หลายอินเตอร์เฟสนั้นมีประโยชน์มากและไม่ก่อให้เกิดปัญหากับผู้ใช้ภาษาหรือโปรแกรมเมอร์ ดังนั้นจึงได้รับอนุญาต การสืบทอดหลายอย่างพร้อมกันยังมีประโยชน์สามารถทำให้เกิดปัญหาร้ายแรงกับผู้ใช้ ( เพชรแห่งความตายที่น่ากลัว) และสิ่งที่คุณทำกับการสืบทอดหลายอย่างสามารถทำได้โดยการจัดองค์ประกอบหรือใช้คลาสภายใน ดังนั้นจึงห้ามมิให้มีการรับมรดกหลายครั้งเนื่องจากทำให้เกิดปัญหามากกว่าผลกำไร


ปัญหาของ "เพชรแห่งความตาย" คืออะไร?
curiousguy

2
@currguy มีมากกว่าหนึ่ง subobject ของคลาสฐานเดียวกันความกำกวม (แทนที่จากการใช้คลาสฐาน) กฎที่ซับซ้อนของการแก้ไขความคลุมเครือดังกล่าว
Tadeusz Kopec

@curiousguy: หากกรอบงานให้การคัดเลือกวัตถุที่อ้างอิงไปยังการอ้างอิงประเภทฐานจะเป็นการรักษาตัวตนจากนั้นวัตถุทุกอินสแตนซ์จะต้องมีการใช้งานอย่างใดอย่างหนึ่งของวิธีการใด ๆ ในระดับฐาน ถ้าToyotaCarและHybridCarทั้งสองอย่างนั้นได้มาจากCarและล้มล้างCar.Driveและถ้าPriusCarสืบทอดทั้งสองอย่างแต่ไม่ได้แทนที่Driveระบบจะไม่มีวิธีระบุสิ่งที่เสมือนCar.Driveควรทำ การเชื่อมต่อหลีกเลี่ยงปัญหานี้โดยหลีกเลี่ยงเงื่อนไขที่เอียงด้านบน
supercat

1
@supercat "ระบบจะไม่มีวิธีระบุสิ่งที่ Car.Drive เสมือนควรทำ" <- หรืออาจให้ข้อผิดพลาดในการคอมไพล์และทำให้คุณเลือกอย่างชัดเจนเช่น C ++
Chris Middleton

1
@ChrisMiddleton: วิธีการvoid UseCar(Car &foo); ไม่สามารถคาดหวังได้ว่าจะรวมถึงความไม่ลงรอยกันระหว่างToyotaCar::DriveและHybridCar::Drive(เนื่องจากบ่อยครั้งที่ไม่ควรทราบหรือไม่สนใจว่ามีประเภทอื่น ๆอยู่ ) ภาษาสามารถทำได้เช่นเดียวกับ C ++ ต้องการรหัสที่ToyotaCar &myCarต้องการส่งไปยังUseCarต้องส่งไปยังHybridCarหรืออย่างใดอย่างหนึ่งToyotaCarแต่เนื่องจาก ((รถยนต์) (HybridCar) myCar) .Drive` และ((Car)(ToyotaCar)myCar).Drive จะทำสิ่งต่าง ๆ นั่นหมายความว่า upcasts ไม่ได้รักษาตัวตน
supercat

6

คุณสามารถค้นหาคำตอบที่ถูกต้องสำหรับแบบสอบถามนี้ได้ในหน้าเอกสารของ oracle เกี่ยวกับการสืบทอดหลายแบบ

  1. การสืบทอดหลายสถานะ: ความสามารถในการรับช่วงจากหลายคลาส

    เหตุผลหนึ่งที่ภาษาจาวาไม่อนุญาตให้คุณขยายมากกว่าหนึ่งคลาสคือการหลีกเลี่ยงปัญหาการสืบทอดหลายสถานะซึ่งเป็นความสามารถในการรับช่วงจากหลายคลาส

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

    1. จะเกิดอะไรขึ้นถ้าเมธอดหรือตัวสร้างจากคลาสพิเศษต่างกันยกตัวอย่างฟิลด์เดียวกัน
    2. วิธีการหรือตัวสร้างจะมีความสำคัญ?
  2. การสืบทอดหลายแบบของการใช้งาน:ความสามารถในการสืบทอดการกำหนดวิธีการจากหลายคลาส

    ปัญหาด้วยวิธีนี้: ชื่อ conflic TS และความคลุมเครือ หากคลาสย่อยและซูเปอร์คลาสมีชื่อวิธีการเดียวกัน (และลายเซ็น) คอมไพเลอร์ไม่สามารถกำหนดเวอร์ชันที่จะเรียกใช้

    แต่จาวาสนับสนุนการสืบทอดหลายประเภทนี้ด้วยวิธีการเริ่มต้นซึ่งได้รับการแนะนำตั้งแต่การเปิดตัว Java 8 คอมไพเลอร์ Java มีกฎบางอย่างเพื่อกำหนดวิธีการเริ่มต้นที่คลาสใดใช้

    อ้างถึง SE post ด้านล่างสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการแก้ไขปัญหาเพชร:

    อะไรคือความแตกต่างระหว่างคลาสนามธรรมและอินเตอร์เฟสใน Java 8?

  3. การสืบทอดหลายประเภท: ความสามารถของคลาสที่จะใช้มากกว่าหนึ่งอินเตอร์เฟส

    เนื่องจากส่วนต่อประสานไม่มีฟิลด์ที่ไม่แน่นอนคุณจึงไม่ต้องกังวลกับปัญหาที่เกิดจากการสืบทอดหลายสถานะที่นี่



4

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

ไม่รองรับการสืบทอดหลายรายการเนื่องจากจะนำไปสู่ปัญหาเพชรร้ายแรง อย่างไรก็ตามมันสามารถแก้ไขได้ แต่มันนำไปสู่ระบบที่ซับซ้อนดังนั้นผู้ก่อตั้ง Java จึงถูกสืบทอดหลายมรดก

ในกระดาษสีขาวที่ชื่อว่า“ Java: ภาพรวม” โดย James Gosling ในเดือนกุมภาพันธ์ 1995 ( ลิงก์ ) ให้แนวคิดว่าทำไมมรดกหลายรายการไม่ได้รับการสนับสนุนใน Java

ตามที่กอสลิง:

"JAVA ละเว้นจำนวนมากที่ไม่ค่อยได้ใช้, เข้าใจไม่ดี, ทำให้เกิดความสับสนในคุณลักษณะของ C ++ ซึ่งในประสบการณ์ของเรานั้นทำให้เกิดความเศร้าโศกมากกว่าประโยชน์ซึ่งส่วนใหญ่ประกอบด้วยตัวดำเนินการโอเวอร์โหลด (แม้ว่าจะมีวิธีการโอเวอร์โหลด)


ลิงก์ไม่สามารถเข้าถึงได้ กรุณาตรวจสอบและอัปเดต
MashukKhan

3

ด้วยเหตุผลเดียวกัน C # ไม่อนุญาตให้สืบทอดหลาย ๆ อัน แต่อนุญาตให้คุณใช้หลายอินเตอร์เฟส

บทเรียนที่เรียนรู้จาก C ++ w / การสืบทอดหลายอย่างคือมันนำไปสู่ปัญหามากกว่าที่มันคุ้มค่า

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

การอนุญาตให้ส่วนต่อประสานหลายชุดช่วยให้คุณใช้รูปแบบการออกแบบ (เช่นตัวปรับต่อ) เพื่อแก้ไขปัญหาประเภทเดียวกันกับที่คุณสามารถแก้ไขได้โดยใช้การสืบทอดหลายตัว แต่ในลักษณะที่เชื่อถือได้และคาดการณ์ได้มากขึ้น


10
C # ไม่มีการสืบทอดหลายอย่างแม่นยำเนื่องจาก Java ไม่อนุญาต มันถูกออกแบบมาช้ากว่า Java ปัญหาหลักของการสืบทอดหลายอย่างที่ฉันคิดคือวิธีที่ผู้คนได้รับการสอนให้ใช้มันทั้งซ้ายและขวา แนวคิดที่ว่าการมอบอำนาจในกรณีส่วนใหญ่เป็นทางเลือกที่ดีกว่ามากไม่ได้อยู่ในช่วงต้นและกลางทศวรรษ ดังนั้นฉันจำตัวอย่างในตำราเมื่อรถยนต์เป็นล้อและประตูและกระจกหน้ารถกับรถมีล้อประตูและกระจกหน้ารถ ดังนั้นการสืบทอดเพียงครั้งเดียวในชวาจึงเป็นการตอบสนองต่อความเป็นจริงนั้น
Alexander Pogrebnyak

2
@AlexanderPogrebnyak: เลือกสองในสามข้อต่อไปนี้: (1) อนุญาตให้ใช้การป้องกันเอกลักษณ์จากการอ้างอิงย่อยเพื่อการอ้างอิง supertype; (2) อนุญาตให้คลาสเพิ่มสมาชิกสาธารณะเสมือนโดยไม่ต้องคอมไพล์คลาสที่ได้รับซ้ำ (3) อนุญาตให้คลาสเพื่อสืบทอดสมาชิกเสมือนโดยนัยจากคลาสฐานหลายคลาสโดยไม่ต้องระบุอย่างชัดเจน ฉันไม่เชื่อว่าเป็นไปได้สำหรับภาษาใด ๆ ที่จะจัดการทั้งสามข้อข้างต้น Java เลือกใช้ # 1 และ # 2 และเหมาะสมกับ C # ฉันเชื่อว่า C ++ ใช้เฉพาะ # 3 เท่านั้น โดยส่วนตัวฉันคิดว่า # 1 และ # 2 มีประโยชน์มากกว่า # 3 แต่คนอื่นอาจแตกต่างกัน
supercat

@supercat "ฉันไม่เชื่อว่าเป็นไปได้สำหรับภาษาใด ๆ ในการจัดการทั้งสามข้อด้านบน" - ถ้าออฟเซ็ตข้อมูลสมาชิกถูกกำหนดที่รันไทม์แทนที่จะรวบรวมเวลา ") และหรือต่อคลาส (เช่นแต่ละคลาสคอนกรีตมีตารางออฟเซ็ตสมาชิกของตัวเอง) จากนั้นฉันคิดว่าเป้าหมายทั้ง 3 สามารถทำได้
ครัวซองต์ Paramagnetic

@TheParamagneticCroissant: ปัญหาความหมายที่สำคัญคือ # 1 ถ้าD1และD2ทั้งสองสืบทอดมาจากBและแต่ละฟังก์ชั่นแทนที่fและถ้าobjเป็นตัวอย่างของประเภทSที่สืบทอดมาจากทั้งสองD1และD2แต่ไม่แทนที่fแล้วหล่ออ้างอิงSถึงD1ควรจะให้สิ่งที่มีการfใช้D1แทนที่และการหล่อBไม่ควร เปลี่ยนที่ ในทำนองเดียวกันการอ้างอิงSถึงD2ควรให้สิ่งที่มีการfใช้การD2แทนที่และการส่งไปยังBไม่ควรเปลี่ยนสิ่งนั้น หากภาษาไม่จำเป็นต้องอนุญาตให้มีการเพิ่มสมาชิกเสมือน ...
supercat

1
@ TheParamagneticCroissant: ทำได้และรายการตัวเลือกของฉันค่อนข้างง่ายเกินไปที่จะแสดงความคิดเห็น แต่การชะลอเวลาทำงานทำให้ผู้เขียน D1, D2 หรือ S รู้ว่าการเปลี่ยนแปลงใดที่พวกเขาสามารถทำได้โดยไม่ทำลาย ผู้บริโภคในชั้นเรียนของพวกเขา
supercat

2

เนื่องจากหัวข้อนี้ไม่ปิดฉันจะโพสต์คำตอบนี้ฉันหวังว่านี่จะช่วยให้ใครบางคนเข้าใจว่าทำไม java ไม่อนุญาตให้สืบทอดหลายรายการ

พิจารณาคลาสต่อไปนี้:

public class Abc{

    public void doSomething(){

    }

}

ในกรณีนี้คลาส Abc ไม่ขยายสิ่งที่ถูกต้องหรือไม่ ไม่เร็วนักโดยนัยคลาสนี้ขยายคลาส Object คลาสพื้นฐานที่อนุญาตให้ทุกอย่างทำงานใน java ทุกอย่างเป็นวัตถุ

ถ้าคุณพยายามที่จะใช้ชั้นบนคุณจะเห็นว่าคุณ IDE ช่วยให้คุณสามารถใช้วิธีการที่ชอบ: equals(Object o), toString()ฯลฯ แต่คุณไม่ได้ประกาศวิธีการที่พวกเขามาจากชั้นฐานObject

คุณสามารถลอง:

public class Abc extends String{

    public void doSomething(){

    }

}

นี่เป็นเรื่องปกติเพราะชั้นเรียนของคุณจะไม่ขยายโดยนัยObjectแต่จะขยายออกStringเพราะคุณพูดว่า พิจารณาการเปลี่ยนแปลงต่อไปนี้:

public class Abc{

    public void doSomething(){

    }

    @Override
    public String toString(){
        return "hello";
    }

}

ตอนนี้ชั้นเรียนของคุณจะกลับมา "สวัสดี" ถ้าคุณเรียก toString ()

ทีนี้ลองนึกภาพชั้นเรียนต่อไปนี้:

public class Flyer{

    public void makeFly(){

    }

}

public class Bird extends Abc, Flyer{

    public void doAnotherThing(){

    }

}

อีกครั้งระดับFlyerนัยขยายวัตถุซึ่งมีวิธีการที่toString()ชั้นใด ๆ ที่จะมีวิธีการนี้มาตั้งแต่พวกเขาทั้งหมดขยายObjectทางอ้อมดังนั้นถ้าคุณโทรtoString()จากBirdที่toString()Java จะมีการใช้งาน? จากAbcหรือFlyer? นี้จะเกิดขึ้นกับการเรียนใด ๆ ที่พยายามที่จะขยายสองคนหรือมากกว่าชั้นเรียนเพื่อหลีกเลี่ยงชนิดของ "วิธีการปะทะกัน" นี้พวกเขาสร้างความคิดของอินเตอร์เฟซโดยทั่วไปคุณอาจคิดว่าพวกเขาเป็นระดับนามธรรมที่ไม่ขยายวัตถุทางอ้อม เนื่องจากเป็นนามธรรมพวกเขาจะต้องดำเนินการโดยชั้นซึ่งเป็นวัตถุ (คุณไม่สามารถทำการเชื่อมต่ออินเทอร์เฟซคนเดียวได้พวกมันจะต้องถูกนำไปใช้โดยคลาส) ดังนั้นทุกอย่างจะยังคงทำงานได้ดี

เพื่อแยกคลาสต่าง ๆ จากส่วนต่อประสานคำสำคัญที่ใช้ถูกสงวนไว้สำหรับส่วนต่อประสานเท่านั้น

คุณสามารถใช้อินเทอร์เฟซใด ๆ ที่คุณชอบในคลาสเดียวกันได้เนื่องจากพวกเขาไม่ได้ขยายสิ่งใดเป็นค่าเริ่มต้น (แต่คุณสามารถสร้างอินเทอร์เฟซที่ขยายอินเทอร์เฟซอื่น แต่อีกครั้งอินเทอร์เฟซ "พ่อ" จะไม่ขยายวัตถุ ") ดังนั้น เพียงแค่อินเทอร์เฟซและพวกเขาจะไม่ต้องทนทุกข์ทรมานจาก " วิธีการรวบรวมลายเซ็นต์วิธีการ " หากพวกเขาทำคอมไพเลอร์จะส่งคำเตือนให้คุณและคุณจะต้องเปลี่ยนวิธีการลงนามเพื่อแก้ไขมัน (Signature = method name + params + return type) .

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

}

1

เนื่องจากอินเทอร์เฟซเป็นเพียงสัญญา และคลาสจริงๆแล้วเป็นที่เก็บข้อมูล


ปัญหาพื้นฐานที่มีหลายมรดกคือความเป็นไปได้ที่คลาสอาจสืบทอดสมาชิกผ่านหลายพา ธ ซึ่งใช้มันแตกต่างกัน การสืบทอดส่วนต่อประสานจะหลีกเลี่ยงสิ่งนี้ได้เนื่องจากสมาชิกส่วนติดต่อกับสถานที่เท่านั้นที่สามารถนำไปใช้งานได้ในชั้นเรียนซึ่งลูกหลานจะถูก จำกัด การสืบทอดแบบครั้งเดียว
supercat

1

ตัวอย่างเช่นสองคลาส A, B มีวิธีการเดียวกัน m1 () และคลาส C ขยายทั้ง A, B

 class C extends A, B // for explaining purpose.

ตอนนี้คลาส C จะค้นหาคำจำกัดความของ m1 อันดับแรกจะค้นหาในชั้นเรียนหากไม่พบจากนั้นจะตรวจสอบชั้นผู้ปกครอง ทั้ง A, B มีคำจำกัดความดังนั้นที่นี่ความกำกวมเกิดขึ้นซึ่งคำนิยามที่ควรเลือก ดังนั้น JAVA ไม่สนับสนุนการใช้งานหลายอย่างในตัว


วิธีการเกี่ยวกับการทำคอมไพเลอร์จาวาคอมไพเลอร์จะช่วยให้ถ้าวิธีการเดียวกันหรือตัวแปรที่กำหนดไว้ทั้งในระดับผู้ปกครองเพื่อให้เราสามารถเปลี่ยนรหัส ...
siluveru kiran kumar

1

Java ไม่รองรับการสืบทอดหลายรายการเนื่องจากเหตุผลสองประการ:

  1. ใน java ทุกคลาสเป็นลูกของObjectคลาส เมื่อได้รับมรดกจากซุปเปอร์คลาสมากกว่าหนึ่งคลาสย่อยจะได้รับความกำกวมในการได้มาซึ่งคุณสมบัติของคลาสอ็อบเจ็กต์
  2. ใน java ทุกคลาสมีคอนสตรัคเตอร์ถ้าเราเขียนอย่างชัดเจนหรือไม่เลย คำสั่งแรกคือการเรียกร้องsuper()ให้สร้างคลาสอาหารมื้อเย็น ถ้าชั้นเรียนมีมากกว่าหนึ่งคลาสชั้นยอดก็จะสับสน

ดังนั้นเมื่อคลาสหนึ่งขยายจากซุปเปอร์คลาสมากกว่าหนึ่งเราจะได้รับข้อผิดพลาดในการคอมไพล์เวลา


0

ยกตัวอย่างเช่นกรณีที่คลาส A มีเมธอด getSomething และคลาส B มีเมธอด getSomething และคลาส C ขยาย A และ B จะเกิดอะไรขึ้นถ้ามีคนเรียก C.getSomething ไม่มีวิธีในการกำหนดวิธีการโทร

โดยทั่วไปอินเตอร์เฟสจะระบุว่าเมธอดใดที่คลาสการใช้งานจำเป็นต้องมี คลาสที่ใช้หลายอินเตอร์เฟสนั้นหมายถึงคลาสนั้นต้องใช้เมธอดจากอินเตอร์เฟสทั้งหมด Whci จะไม่นำไปสู่ปัญหาใด ๆ ตามที่อธิบายไว้ข้างต้น


2
" จะเกิดอะไรขึ้นถ้ามีคนเรียก C.getSomething " เป็นข้อผิดพลาดใน C ++ แก้ไขปัญหา.
curiousguy

นั่นคือประเด็น ... นั่นเป็นตัวอย่างที่เคาน์เตอร์ที่ฉันคิดว่าชัดเจน ฉันชี้ให้เห็นว่าไม่มีวิธีใดที่จะตัดสินว่าควรใช้วิธีใดในการรับ คำถามที่เกี่ยวข้องกับ java ไม่ใช่ c ++
John Kane

ฉันขอโทษที่ฉันไม่เข้าใจว่าประเด็นของคุณคืออะไร เห็นได้ชัดว่ามีความเคลือบแคลงในบางกรณีด้วย MI นั่นเป็นตัวอย่างตัวนับอย่างไร ใครอ้างว่า MI ไม่เคยส่งผลให้เกิดความกำกวม? " นอกจากนี้ยังเป็นที่ทราบด้านคำถามที่เกี่ยวข้องกับ Java ไม่ C ++ " ดังนั้น?
curiousguy

ฉันแค่พยายามแสดงให้เห็นว่าทำไมความคลุมเครือนั้นมีอยู่และทำไมมันถึงไม่มีส่วนต่อประสาน
John Kane

ใช่ MI สามารถทำให้เกิดการโทรที่ไม่ชัดเจน ดังนั้นสามารถบรรทุกเกินพิกัด ดังนั้น Java ควรลบมากไปหรือไม่
curiousguy

0

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


0

Java ไม่รองรับการสืบทอดหลายแบบ, การสืบทอดหลายหน้าและแบบไฮบริดเนื่องจากปัญหาความกำกวม:

 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.

หลายมรดก

ลิงค์อ้างอิง: https://plus.google.com/u/0/communities/102217496457095083679


0

ในลักษณะที่เรียบง่ายที่เราทุกคนรู้เราสามารถสืบทอด (ขยาย) หนึ่งคลาส แต่เราสามารถใช้อินเทอร์เฟซมากมาย .. นั่นเป็นเพราะในอินเทอร์เฟซที่เราไม่ให้การใช้งานเพียงแค่พูดฟังก์ชั่น สมมติว่าถ้าจาวาสามารถขยายชั้นเรียนจำนวนมากและผู้ที่มีวิธีการเดียวกัน .. ในจุดนี้ถ้าเราพยายามที่จะเรียกวิธีการเรียนในชั้นเรียนซุปเปอร์ย่อยสิ่งที่วิธีการคิดว่าจะทำงาน ??, คอมไพเลอร์รับสับสน ตัวอย่างเช่น: - พยายามที่จะขยายหลาย แต่ใน อินเตอร์เฟซวิธีการเหล่านั้นไม่ได้มีร่างกายที่เราควรใช้ผู้ที่อยู่ในชั้นย่อย .. พยายามที่จะดำเนินการหลาย ๆ ดังนั้นไม่ต้องกังวล ..


1
คุณสามารถโพสต์ตัวอย่างเหล่านี้ได้ที่นี่ หากไม่มีสิ่งนี้ดูเหมือนว่ากลุ่มข้อความที่มีคำถามอายุ 9 ปีตอบร้อยครั้ง
Pochmurnik

-1

* นี่เป็นคำตอบง่ายๆตั้งแต่ฉันเป็นผู้เริ่มต้นใน Java *

พิจารณามีสามชั้นX, และYZ

ดังนั้นเรากำลังสืบทอดเช่นX extends Y, Z และทั้งสองYและZมีวิธีการที่alphabet()มีประเภทผลตอบแทนและข้อโต้แย้งเดียวกัน วิธีการนี้alphabet()ในการYพูดกับแสดงตัวอักษรแรกและวิธีการในอักษรZกล่าวว่าการแสดงผลอักษรสุดท้าย ดังนั้นที่นี่มาคลุมเครือเมื่อถูกเรียกโดยalphabet() Xไม่ว่าจะเป็นการแสดงตัวอักษรตัวแรกหรือตัวสุดท้าย ??? ดังนั้นจาวาไม่สนับสนุนการสืบทอดหลายอย่าง ในกรณีของอินเทอร์เฟซให้พิจารณาYและใช้Zเป็นอินเตอร์เฟส ดังนั้นทั้งสองจะมีการประกาศของวิธีการalphabet()แต่ไม่ได้นิยาม มันจะไม่บอกว่าจะแสดงตัวอักษรตัวแรกหรือตัวอักษรตัวสุดท้ายหรืออะไร แต่จะประกาศวิธีการalphabet(). ดังนั้นจึงไม่มีเหตุผลที่จะเพิ่มความกำกวม Xเราสามารถกำหนดวิธีการกับสิ่งที่เราต้องการระดับภายใน

ดังนั้นในคำนิยามในอินเทอร์เฟซจะทำหลังจากการใช้งานจึงไม่มีความสับสน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.