เหตุใดการสืบทอดหลายรายการจึงไม่อนุญาตใน Java หรือ C #


115

ฉันรู้ว่าไม่อนุญาตให้มีการสืบทอดหลายรายการใน Java และ C # หนังสือหลายเล่มบอกว่าไม่อนุญาตให้มีการสืบทอดหลาย ๆ แต่สามารถใช้งานได้โดยใช้อินเทอร์เฟซ ไม่มีการพูดคุยเกี่ยวกับสาเหตุที่ไม่อนุญาต ใครช่วยบอกทีว่าทำไมถึงไม่อนุญาต?


1
เช่นเดียวกับที่จะชี้ให้เห็นว่ามีเป็นกรอบออกมีที่ให้ MI-เช่นพฤติกรรมใน C # เรียน และแน่นอนว่าคุณมีตัวเลือกในการมีมิกซ์อินไร้สัญชาติ (โดยใช้วิธีการขยาย) - การไร้สัญชาติมันไม่มีประโยชน์มากนัก
Dmitri Nesteruk

1
อินเทอร์เฟซนั้นเกี่ยวข้องกับการสืบทอดเป็นภาพลวงตาที่สร้างขึ้นโดยไวยากรณ์ของภาษา การสืบทอดควรจะทำให้คุณร่ำรวยขึ้น ไม่มีอะไรเช่นนั้นสำหรับอินเทอร์เฟซคุณจะไม่สืบทอด squat และทำให้คุณแย่ลงอย่างมาก คุณได้รับหนี้การพนันของลุงแฮร์รี่คุณมีงานต้องทำมากขึ้นเพื่อใช้อินเทอร์เฟซ
Hans Passant

คำตอบ:


143

คำตอบสั้น ๆ คือเพราะนักออกแบบภาษาตัดสินใจที่จะไม่ทำ

โดยทั่วไปก็ดูเหมือนว่าทั้ง NET และนักออกแบบ Java ไม่อนุญาตให้มรดกหลายเพราะพวกเขาให้เหตุผลว่าการเพิ่ม MI เพิ่มความซับซ้อนมากเกินไปกับภาษาในขณะที่ให้ประโยชน์น้อยเกินไป

เพื่อความสนุกสนานและการอ่านเชิงลึกยิ่งขึ้นมีบทความบางส่วนในเว็บพร้อมบทสัมภาษณ์ของนักออกแบบภาษาบางคน ตัวอย่างเช่นสำหรับ. NET Chris Brumme (ซึ่งทำงานที่ MS บน CLR) ได้อธิบายถึงสาเหตุที่พวกเขาตัดสินใจที่จะไม่:

  1. ภาษาที่แตกต่างกันมีความคาดหวังที่แตกต่างกันสำหรับวิธีการทำงานของ MI ตัวอย่างเช่นวิธีแก้ไขความขัดแย้งและการรวมฐานที่ซ้ำกันหรือซ้ำซ้อน ก่อนที่เราจะนำ MI ไปใช้ใน CLR เราต้องทำการสำรวจภาษาทั้งหมดหาแนวคิดทั่วไปและตัดสินใจว่าจะแสดงออกอย่างไรในลักษณะที่เป็นกลางทางภาษา เราจะต้องตัดสินใจด้วยว่า MI อยู่ใน CLS หรือไม่และหมายความว่าอย่างไรสำหรับภาษาที่ไม่ต้องการแนวคิดนี้ (น่าจะเป็น VB.NET เป็นต้น) แน่นอนว่านั่นเป็นธุรกิจที่เราใช้ในฐานะรันไทม์ภาษาทั่วไป แต่เรายังไม่ได้ทำเพื่อ MI

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

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

คุณสามารถอ่านบทความฉบับเต็มได้ที่นี่

สำหรับ Java คุณสามารถอ่านบทความนี้ :

สาเหตุของการละเว้นการสืบทอดหลายรายการจากภาษา Java ส่วนใหญ่เกิดจากเป้าหมาย "เรียบง่ายเชิงวัตถุและคุ้นเคย" ในฐานะภาษาที่เรียบง่ายผู้สร้างของ Java ต้องการภาษาที่นักพัฒนาส่วนใหญ่สามารถเข้าใจได้โดยไม่ต้องมีการฝึกอบรมมากมาย ด้วยเหตุนี้พวกเขาจึงพยายามทำให้ภาษาใกล้เคียงกับ C ++ มากที่สุด (คุ้นเคย) โดยไม่ต้องใช้ความซับซ้อนที่ไม่จำเป็นของ C ++ (ง่าย ๆ )

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


10
การเปรียบเทียบที่ดี ค่อนข้างบ่งบอกถึงกระบวนการคิดที่เป็นรากฐานของทั้งสองแพลตฟอร์มที่ฉันคิด
CurtainDog

97

การสืบทอดการใช้งานหลายครั้งเป็นสิ่งที่ไม่ได้รับอนุญาต

ปัญหาคือคอมไพเลอร์ / รันไทม์ไม่สามารถคิดได้ว่าจะทำอย่างไรถ้าคุณมีคลาส Cowboy และ Artist ทั้งที่มีการใช้งานสำหรับเมธอด draw () จากนั้นคุณพยายามสร้างประเภท CowboyArtist ใหม่ จะเกิดอะไรขึ้นเมื่อคุณเรียกใช้เมธอด draw () มีคนนอนตายอยู่บนถนนหรือคุณมีสีน้ำน่ารัก ๆ ?

ฉันเชื่อว่ามันเรียกว่าปัญหามรดกเพชรคู่


80
คุณได้สีน้ำที่น่ารักของคนที่ตายบนถนน :-)
Dan F

นี่เป็นปัญหาเดียวหรือไม่? ฉันคิดว่าฉันไม่แน่ใจว่า C ++ แก้ปัญหานี้ด้วยคีย์เวิร์ดเสมือนจริงหรือไม่? ฉันไม่เก่ง C ++
Abdulsattar Mohammed

2
ใน C ++ คุณสามารถระบุฟังก์ชันคลาสพื้นฐานที่จะเรียกใช้ในการรับมาหรือปรับใช้ใหม่ด้วยตัวคุณเอง
Dmitry Risenberg

1
การออกแบบสมัยใหม่มีแนวโน้มที่จะชอบการจัดองค์ประกอบมากกว่าการถ่ายทอดทางพันธุกรรมในทุกกรณียกเว้นกรณีที่ง่ายที่สุด การสืบทอดหลาย ๆ ครั้งจะไม่นับเป็นกรณีง่ายๆ ด้วยการจัดองค์ประกอบคุณสามารถควบคุมสิ่งที่ชั้นเรียนของคุณทำได้อย่างแม่นยำเมื่อมีปัญหาเกี่ยวกับเพชรเช่นนี้ ...
Bill Michell

คุณต้องมีรายการลำดับความสำคัญ สิ่งนี้ใช้ในระบบวัตถุของ Common Lisp, CLOS คลาสทั้งหมดสร้างมรดกและวิธีการที่เรียกนั้นจะถูกกำหนดจากที่นั่น นอกจากนี้ CLOS ยังช่วยให้คุณกำหนดกฎต่างๆได้เช่น CowboyArtist.draw () อาจวาดคาวบอยก่อนแล้วจึงเป็นศิลปิน หรือสิ่งที่คุณเคยแต่งหน้า
Sebastian Krog

19

เหตุผล: Java เป็นที่นิยมมากและง่ายต่อการเขียนโค้ดเนื่องจากความเรียบง่าย

ดังนั้นสิ่งที่นักพัฒนา java รู้สึกยากและซับซ้อนในการทำความเข้าใจสำหรับโปรแกรมเมอร์พวกเขาพยายามหลีกเลี่ยง ทรัพย์สินประเภทหนึ่งคือมรดกหลายรายการ

  1. พวกเขาหลีกเลี่ยงตัวชี้
  2. พวกเขาหลีกเลี่ยงการสืบทอดหลายอย่าง

ปัญหาเกี่ยวกับการสืบทอดหลายรายการ:ปัญหาเพชร

ตัวอย่าง :

  1. สมมติว่าคลาส A กำลังสนุกกับเมธอด () คลาส B และคลาส C มาจากคลาส A
  2. และทั้งคลาส B และ C จะแทนที่เมธอด fun ()
  3. สมมติว่าคลาส D สืบทอดทั้งคลาส B และ C. (เพียงแค่สมมติฐาน)
  4. สร้างวัตถุสำหรับคลาส D
  5. D d = ใหม่ D ();
  6. และพยายามเข้าถึง d.fun (); => มันจะเรียกความสนุกของคลาส B () หรือความสนุกของคลาส C ()?

นี่คือความไม่ชัดเจนที่มีอยู่ในปัญหาเพชร

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

หมายเหตุ : แต่วิธีใดก็ตามที่คุณสามารถใช้การสืบทอดทางอ้อมโดยใช้อินเทอร์เฟซ


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

13

เนื่องจาก Java มีปรัชญาการออกแบบที่แตกต่างจาก C ++ อย่างมาก (ฉันจะไม่พูดถึง C # ที่นี่)

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

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

นอกจากนี้ Java ยังเป็นปฏิกิริยาจาก C ++ และ Smalltalk ซึ่งเป็นภาษา OO ที่รู้จักกันดีที่สุด มีภาษา OO อื่น ๆ มากมาย (Common Lisp เป็นภาษาแรกที่เป็นมาตรฐาน) โดยมีระบบ OO ต่างๆที่จัดการ MI ได้ดีกว่า

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

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


12

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


8

ในการสืบทอดหลาย ๆ C ++ เป็นอาการปวดหัวอย่างมากเมื่อใช้ไม่ถูกต้อง เพื่อหลีกเลี่ยงปัญหาการออกแบบที่เป็นที่นิยมเหล่านั้นอินเทอร์เฟซหลาย ๆ "การสืบทอด" ถูกบังคับให้ใช้ภาษาสมัยใหม่แทน (java, C #)


8

การสืบทอดหลายรายการคือ

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

ดังนั้นจึงถือได้ว่าเป็นทางเลือกที่ชาญฉลาดที่จะไม่รวมการสืบทอดหลายรายการลงในภาษา Java


3
ฉันไม่เห็นด้วยกับสิ่งที่กล่าวมาทั้งหมดโดยทำงานร่วมกับ Common Lisp Object System
David Thornley

2
ภาษาแบบไดนามิกไม่นับ ;-) ในระบบที่มีลักษณะคล้ายเสียงกระเพื่อมคุณมี REPL ที่ทำให้การดีบักค่อนข้างง่าย นอกจากนี้ CLOS (อย่างที่ฉันเข้าใจฉันไม่เคยใช้มันจริงๆอ่านเท่านั้น) เป็นระบบเมตา - ออบเจ็กต์ที่มีความยืดหยุ่นมากและมีทัศนคติที่เป็นตัวของคุณเอง แต่พิจารณาภาษาที่คอมไพล์แบบสแตติกเช่น C ++ ซึ่งคอมไพเลอร์สร้างการค้นหาวิธีการที่ซับซ้อนอย่างร้ายกาจโดยใช้ vtables หลายตัว (อาจทับซ้อนกัน): ในการใช้งานดังกล่าวการค้นหาว่าการนำเมธอดไปใช้งานใดที่เรียกใช้อาจเป็นงานที่ไม่สำคัญนัก
mfx

5

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


4

ย้อนกลับไปในสมัยก่อน (ยุค 70) เมื่อวิทยาการคอมพิวเตอร์เป็นวิทยาศาสตร์มากขึ้นและการผลิตจำนวนมากน้อยลงโปรแกรมเมอร์มีเวลาคิดเกี่ยวกับการออกแบบที่ดีและการนำไปใช้งานที่ดีและเป็นผลให้ผลิตภัณฑ์ (โปรแกรม) มีคุณภาพสูง (เช่นการออกแบบ TCP / IP และการนำไปใช้งาน) ทุกวันนี้เมื่อทุกคนกำลังเขียนโปรแกรมและผู้จัดการกำลังเปลี่ยนข้อกำหนดก่อนกำหนดเวลาปัญหาที่ละเอียดอ่อนเช่นที่อธิบายไว้ในลิงก์วิกิพีเดียจากโพสต์ของ Steve Haigh นั้นยากที่จะติดตาม ดังนั้น "การสืบทอดหลายรายการ" จึงถูก จำกัด โดยการออกแบบคอมไพเลอร์ หากคุณชอบคุณยังสามารถใช้ C ++ ได้ .... และมีอิสระทุกอย่างที่คุณต้องการ :)


4
... รวมถึงอิสระในการยิงด้วยเท้าตัวเองหลาย ๆ ครั้ง;)
Mario Ortegón

4

ฉันใช้คำพูดที่ว่า "ไม่อนุญาตให้มีการสืบทอดหลายรายการใน Java" ด้วยเกลือเล็กน้อย

การสืบทอดหลายรายการถูกกำหนดเมื่อ "ประเภท" สืบทอดมาจาก "ประเภท" มากกว่าหนึ่งรายการ และอินเทอร์เฟซยังจัดเป็นประเภทตามที่มีพฤติกรรม ดังนั้น Java จึงมีการสืบทอดหลายรายการ แค่นั้นก็ปลอดภัยกว่า


6
แต่คุณไม่ได้รับช่วงต่อจากอินเทอร์เฟซ อินเทอร์เฟซไม่ใช่คลาส
Blorgbeard ออก

2
ใช่ แต่ไม่เคยมีการกำหนดมรดกให้แคบลงยกเว้นในหนังสือเตรียมสอบบางเล่ม และฉันคิดว่าเราควรมองข้ามไวยากรณ์ของมัน ทั้งคู่ทำสิ่งเดียวกันและอินเทอร์เฟซถูก "คิดค้น" ขึ้นมาด้วยเหตุผลนั้นเท่านั้น
Rig Veda

ใช้อินเทอร์เฟซ Closeable: หนึ่งวิธีปิด () สมมติว่าคุณต้องการขยายเป็น Openable: มันมีเมธอด open () ซึ่งตั้งค่าฟิลด์บูลีน isOpen เป็น true การพยายามเปิด Openable ซึ่งเปิดอยู่แล้วจะทำให้เกิด Exception เช่นเดียวกับการพยายามปิด Openable ที่ไม่ได้เปิด ... ลำดับวงศ์ตระกูลที่น่าสนใจอีกหลายร้อยคลาสอาจต้องการใช้ฟังก์ชันดังกล่าว ... โดยไม่ต้องเลือกที่จะขยายแต่ละ เวลาจากคลาสเปิดได้ หาก Openable เป็นเพียงอินเทอร์เฟซก็ไม่สามารถให้ฟังก์ชันนี้ได้ QED: อินเทอร์เฟซไม่ได้ให้การสืบทอด!
หนูไมค์

4

การโหลดคลาสแบบไดนามิกทำให้การใช้งานการสืบทอดหลายรายการทำได้ยาก

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

ปัญหาเพชรของการสืบทอดหลายรายการ เรามีคลาส B และ C สองคลาสที่สืบทอดมาจาก A สมมติว่า B และ C กำลังลบล้างวิธีการที่สืบทอดมาและจัดเตรียมการนำไปใช้งานของตนเอง ตอนนี้ D รับช่วงจากทั้ง B และ C โดยทำการสืบทอดหลายรายการ D ควรสืบทอดวิธีการที่ถูกลบล้างนั้น jvm ไม่สามารถตัดสินใจได้ว่าจะใช้วิธีการลบล้างใด?

ในฟังก์ชันเสมือน c ++ ถูกใช้เพื่อจัดการและเราต้องทำอย่างชัดเจน

สิ่งนี้สามารถหลีกเลี่ยงได้โดยใช้อินเทอร์เฟซไม่มีเนื้อหาของวิธีการ อินเทอร์เฟซไม่สามารถสร้างอินสแตนซ์ได้ - สามารถใช้งานได้โดยคลาสหรือขยายโดยอินเทอร์เฟซอื่นเท่านั้น


3

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


2

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

หรือในขณะที่เรียก superclass constructor โดยsuper()super class constructor ตัวไหนจะถูกเรียก?

การตัดสินใจนี้เป็นไปไม่ได้ด้วยคุณสมบัติ java API ปัจจุบัน ดังนั้นจึงไม่อนุญาตให้มีการสืบทอดหลายรายการใน java


โดยพื้นฐานแล้วระบบประเภทของ Java จะถือว่าอินสแตนซ์อ็อบเจ็กต์ทุกตัวมีประเภทเดียวการหล่ออ็อบเจ็กต์ไปยังซูเปอร์ไทป์จะใช้งานได้และเป็นการสงวนข้อมูลอ้างอิงเสมอและการส่งการอ้างอิงไปยังประเภทย่อยจะเป็นการสงวนข้อมูลอ้างอิงหากอินสแตนซ์เป็นประเภทย่อยนั้นหรือ ประเภทย่อยดังกล่าว สมมติฐานดังกล่าวมีประโยชน์และฉันไม่คิดว่าเป็นไปได้ที่จะอนุญาตให้มีการสืบทอดหลาย ๆ แบบโดยทั่วไปในลักษณะที่สอดคล้องกับพวกเขา
supercat

1

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

เหตุผล :

การสืบทอดหลายรายการ:ทำให้เกิดความซับซ้อนและความคลุมเครือมากขึ้น

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

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

มายกตัวอย่างง่ายๆ

  1. สมมติว่ามีคลาสซูเปอร์คลาส 2 คลาส A และ B ที่มีชื่อเมธอดเหมือนกัน แต่ฟังก์ชันต่างกัน ผ่านรหัสต่อไปนี้ที่มีคำหลัก (ขยาย) การสืบทอดหลายคำเป็นไปไม่ได้

       public class A                               
         {
           void display()
             {
               System.out.println("Hello 'A' ");
             }
         }
    
       public class B                               
          {
            void display()
              {
                System.out.println("Hello 'B' ");
              }
          }
    
      public class C extends A, B    // which is not possible in java
        {
          public static void main(String args[])
            {
              C object = new C();
              object.display();  // Here there is confusion,which display() to call, method from A class or B class
            }
        }
  2. แต่ผ่านทางอินเทอร์เฟซโดยใช้คำหลัก (implements) การสืบทอดหลายคำเป็นไปได้

    interface A
        {
           // display()
        }
    
    
     interface B
        {
          //display()
        }
    
     class C implements A,B
        {
           //main()
           C object = new C();
           (A)object.display();     // call A's display
    
           (B)object.display(); //call B's display
        }
    }

0

ใครช่วยบอกทีว่าทำไมถึงไม่อนุญาต?

คุณสามารถหาคำตอบได้จากลิงค์เอกสารนี้

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

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

  1. จะเกิดอะไรขึ้นถ้าเมธอดหรือตัวสร้างจากซูเปอร์คลาสที่แตกต่างกันสร้างอินสแตนซ์ฟิลด์เดียวกัน

  2. วิธีการหรือตัวสร้างใดจะมีความสำคัญกว่า?

แม้ว่าตอนนี้จะอนุญาตให้มีการสืบทอดหลายสถานะได้ แต่คุณยังสามารถใช้

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

การสืบทอดการใช้งานจำนวนมาก (ผ่านวิธีการเริ่มต้นในอินเทอร์เฟซ): ความสามารถในการสืบทอดนิยามวิธีการจากหลายคลาส

อ้างถึงคำถาม SE ที่เกี่ยวข้องนี้สำหรับข้อมูลเพิ่มเติม:

ความคลุมเครือในการสืบทอดหลายรายการพร้อมอินเทอร์เฟซ


0

ใน C ++ คลาสสามารถสืบทอด (โดยตรงหรือโดยอ้อม) จากมากกว่าหนึ่งคลาสซึ่งเรียกว่าการ สืบทอดหลายรายการมรดกหลาย

อย่างไรก็ตาม C # และ Java จำกัด คลาสไว้ที่การสืบทอดเดียวแต่ละคลาสสืบทอดมาจากคลาสพาเรนต์เดียว

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

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

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

class A {
    protected:
    bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
    public:
    void setFlag( bool nflag ){
        flag = nflag; // ambiguous
    }
};

ในตัวอย่างนี้ข้อมูลสมาชิกถูกกำหนดโดยflag class Aแต่class Dลงมาจากclass B และclass Cซึ่งทั้งสองเป็นผลมาจากAดังนั้นในสาระสำคัญสองฉบับของflagที่มีอยู่เพราะทั้งสองกรณีของAอยู่ในD's ลำดับชั้น คุณต้องการตั้งค่าใด คอมไพเลอร์จะบ่นว่าการอ้างอิงถึงflagในDเป็นที่คลุมเครือ การแก้ไขอย่างหนึ่งคือการทำให้ข้อมูลอ้างอิงไม่ชัดเจน:

B::flag = nflag;

การแก้ไขอีกประการหนึ่งคือการประกาศ B และ C เป็นvirtual base classesซึ่งหมายความว่ามีสำเนา A เพียงชุดเดียวเท่านั้นที่สามารถมีอยู่ในลำดับชั้นเพื่อขจัดความคลุมเครือใด ๆ

ความซับซ้อนอื่น ๆ มีอยู่พร้อมกับการสืบทอดหลายรายการเช่นลำดับที่คลาสพื้นฐานถูกเตรียมใช้งานเมื่อสร้างอ็อบเจ็กต์ที่ได้รับหรือวิธีที่สมาชิกสามารถซ่อนโดยไม่ได้ตั้งใจจากคลาสที่ได้รับ เพื่อหลีกเลี่ยงความซับซ้อนเหล่านี้บางภาษา จำกัด ตัวเองให้อยู่ในรูปแบบการสืบทอดเดียวที่ง่ายกว่า

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


-1

ลองนึกภาพตัวอย่างนี้: ฉันมีชั้นเรียน Shape1

มีCalcualteAreaวิธีการ:

Class Shape1
{

 public void CalculateArea()

     {
       //
     }
}

มีคลาสอื่นShape2ที่มีวิธีการเดียวกัน

Class Shape2
{

 public void CalculateArea()

     {

     }
}

ตอนนี้ฉันมี Circle คลาสเด็กมันมาจากทั้ง Shape1 และ Shape2

public class Circle: Shape1, Shape2
{
}

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

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


นักออกแบบภาษาสามารถเรียกใช้วิธีการที่ชัดเจนได้เช่นเดียวกับในอินเทอร์เฟซ ไม่มีจุดนั้น! อีกเหตุผลหนึ่งคือตอนนี้คุณพิจารณาคลาสนามธรรมด้วยวิธีนามธรรมอย่างไร?
Nomi Ali
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.