ขยายจากสองคลาส


150

ฉันจะทำสิ่งนี้ได้อย่างไร:

public class Main extends ListActivity , ControlMenu 

นอกจากนี้ฉันอยากจะรู้ว่าวิธีนี้ก็โอเคที่ฉันได้ทำเมนูในชั้นเรียนซึ่งเป็น ControlMenu และฉันได้ขยายกิจกรรมที่เหลือ


4
คุณไม่สามารถขยายสองคลาสขึ้นไปในครั้งเดียว ไม่อนุญาตให้สืบทอดหลายรายการใน java
yogsma

คำตอบ:


156

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

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

 public class CustomActivity extends Activity {

     private AnotherClass mClass;

     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mClass = new AnotherClass(this);
     }

     //Implement each method you want to use.
     public String getInfoFromOtherClass()
     {
        return mClass.getInfoFromOtherClass();
     }
 }

นี่เป็นทางออกที่ดีที่สุดที่ฉันคิดไว้ คุณสามารถรับฟังก์ชั่นการใช้งานจากทั้งคลาสและยังคงเป็นประเภทคลาสเดียวเท่านั้น

ข้อเสียเปรียบคือคุณไม่สามารถบรรจุลงใน Mold of the Internal class โดยใช้การร่าย


2
คุณได้เร็ว :) ฉันจะออกจากคำตอบของฉันเป็นบางส่วนเดินเตร่นามธรรมเปรียบเทียบกับตัวอย่างที่เป็นรูปธรรมของคุณ;)
Nicolas78

1
นี่เป็นทางเลือกที่ดีมากและให้ความยืดหยุ่นมากขึ้นในอนาคต
David Souther

1
ดูโซลูชัน @SCB สำหรับโซลูชัน OOP เพิ่มเติม
idish

ฉันสามารถใช้ตัวอย่างที่แม่นยำยิ่งขึ้นได้ที่นี่ ไม่แน่ใจว่าฉันเข้าใจ
Neo42

54

ทำไมไม่ใช้ชั้นใน (ทำรัง)

class A extends B {
    private class C extends D {
        //Classes A , B , C , D accessible here 
    }
}

4
Intersting approach ผู้เชี่ยวชาญคิดอย่างไรกับเทคนิคนี้ ฉันต้องการขุดลงไป
TechNyquist

1
ตกลงกับการประชุมหรือไม่?
Totten

2
Fragmentไม่สามารถรับระดับชั้นในการทำงานใน กิจกรรมของฉันต้องขยายออกไปFragmentซึ่งหมายความว่าฉันต้องการอย่างยิ่งgetView()ซึ่งจะไม่ทำงานถ้ามันอยู่ในชั้นในและรหัสในที่นั่นคือที่ที่ฉันต้องใช้รหัสจากListActivityดังนั้นฉันจึงไม่สามารถขยายได้สองครั้งหรือทำชั้นใน ในตัวอย่างนี้
Azurespot

นี่เป็นทางออกที่ดีที่สุดสำหรับการขยายคลาสนามธรรมที่คุณต้องการเข้าถึงตัวแปรของคลาสที่มีการบังคับใช้
tak3shi

45

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

สมมติว่าคุณมีclass Fooและที่คุณทั้งสองต้องการที่จะลองขยายเป็นclass Bar class FooBarแน่นอนอย่างที่คุณพูดคุณไม่สามารถทำได้:

public class FooBar extends Foo, Bar

ผู้คนได้เข้าไปหาเหตุผลของสิ่งนี้ในระดับหนึ่งแล้ว ให้เขียนinterfacesทั้งสองวิธีFooและBarครอบคลุมวิธีการสาธารณะทั้งหมด เช่น

public interface FooInterface {

    public void methodA();

    public int methodB();

    //...
} 

public interface BarInterface {

    public int methodC(int i);

    //...
}

และตอนนี้ทำFooและBarใช้อินเทอร์เฟซแบบสัมพันธ์:

public class Foo implements FooInterface { /*...*/ }

public class Bar implements BarInterface { /*...*/ }

ขณะนี้class FooBarคุณสามารถใช้ทั้งสองFooInterfaceและBarInterfaceในขณะที่รักษาFooและBarวัตถุและเพียงผ่านวิธีการผ่าน:

public class FooBar implements FooInterface, BarInterface {

    Foo myFoo;
    Bar myBar;

    // You can have the FooBar constructor require the arguments for both
    //  the Foo and the Bar constructors
    public FooBar(int x, int y, int z){
        myFoo = new Foo(x);
        myBar = new Bar(y, z);
    }

    // Or have the Foo and Bar objects passed right in
    public FooBar(Foo newFoo, Bar newBar){
        myFoo = newFoo;
        myBar = newBar;
    }

    public void methodA(){
        myFoo.methodA();
    }

    public int methodB(){
        return myFoo.methodB();
    }

    public int methodC(int i){
        return myBar.methodC(i);
    }

    //...

}

โบนัสสำหรับวิธีนี้คือว่าFooBarวัตถุที่เหมาะกับแม่พิมพ์ของทั้งสองและFooInterface BarInterfaceนั่นหมายความว่านี่เป็นเรื่องปกติอย่างสมบูรณ์แบบ:

FooInterface testFoo;
testFoo = new FooBar(a, b, c);
testFoo = new Foo(a);

BarInterface testBar;
testBar = new FooBar(a, b, c);
testBar = new Bar(b, c);

หวังว่านี่จะอธิบายวิธีใช้อินเทอร์เฟซแทนส่วนขยายหลายรายการ แม้ว่าฉันจะไม่กี่ปีที่ผ่านมา


6
นี่คือคำตอบที่ดีที่สุด
user3290180

นี่เป็นคำตอบที่ดีมาก แต่ฉันเห็นปัญหาสองสามข้อที่ต้องแก้ไขในรายละเอียดเพิ่มเติม ครั้งแรกที่ขยายจากสองคลาสที่มีอยู่จะมีปัญหาเพิ่มขึ้นเล็กน้อยและจะมีอุปสรรคเพิ่มขึ้นเป็นจำนวนมากสิ่งที่สองคือถ้าอินเตอร์เฟส Foo และ Bar ทั้งคู่มีฟังก์ชันเดียวกันคุณจะต้องมีลำดับความสำคัญก่อน การขยายชั้นเรียนของคุณอย่างชัดเจนจะบังคับให้คุณตัดสินใจล่วงหน้า ยังเป็นตัวเลือกที่ยอดเยี่ยม :) +1
The Lazy Coder

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

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

37

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

abstract class A {
 abstract string foo();
}

class B extends A {
 string foo () { return "bar"; }
}

class C extends A  {
 string foo() {return "baz"; }
}

class D extends B, C {
 string foo() { return super.foo(); } //What do I do? Which method should I call?
}

C ++ และอื่น ๆ มีวิธีแก้ไขสองสามวิธีเช่น

string foo() { return B::foo(); }

แต่ Java ใช้อินเตอร์เฟสเท่านั้น

Java Trails มีการแนะนำที่ยอดเยี่ยมเกี่ยวกับส่วนต่อประสาน: http://download.oracle.com/javase/tutorial/java/concepts/interface.html คุณอาจต้องการติดตามสิ่งนั้นก่อนที่จะดำดิ่งสู่ความแตกต่างใน Android API


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

1
สำหรับคอมไพเลอร์ที่ฉลาดพอมันไม่ใช่ แก้มันในระดับคอมไพเลอร์ที่เป็นไปได้และแน่นอน c ++ virtual classไม่ได้กับ การทำเช่นนั้นมาพร้อมกับคำเตือนและคำเตือนมากมายทั้งสำหรับคอมไพเลอร์และนักพัฒนา
David Souther

1
วิธีการเกี่ยวกับวิธีการเริ่มต้นเดียวกันในสองอินเตอร์เฟส? นั่นเป็นอีกตัวอย่างของปัญหาเพชรซึ่งจาวาสามารถจัดการได้
Lajos Meszaros

1
@ MészárosLajos แต่คุณไม่ได้รับสายsuperจากวิธีการสืบทอด ดีคุณสามารถ แต่คุณต้องระบุวิธีการอินเตอร์เฟซที่จะเรียกใช้ (และมันจะต้องใช้คำหลักdefaultในการดำเนินการอินเตอร์เฟซ) ตัวอย่างคือ: MyIFace.super.foo()โดยที่ MyIFace เป็นอินเตอร์เฟส ในขณะที่คุณสามารถเห็นวิธีการอินเทอร์เฟซในการดำเนินการที่กำหนดไว้และหลีกเลี่ยงปัญหาเพชรทั้งหมด หากคุณขยายMyClass1และMyClass2ทั้งสองคลาสมี a foo()และเรียกsuper.foo()คอมไพเลอร์จะถูกโยนโดยปัญหาเพชร
JDSweetBeat

คุณไม่สามารถกลับมา"bar"หรือมีประเภทวิธีการ"baz" voidอย่างไรก็ตามคำอธิบายที่ดี xD
xdevs23

22

ใช่อย่างที่ทุกคนเขียนไว้คุณไม่สามารถทำมรดกหลายอย่างใน Java หากคุณมีสองคลาสที่คุณต้องการใช้รหัสคุณมักจะเป็นคลาสย่อย (พูดคลาสA) สำหรับการเรียนBคุณนามธรรมวิธีการที่สำคัญของมันมีอินเตอร์เฟซBInterface(ชื่อน่าเกลียด แต่คุณได้รับความคิด) Main extends A implements BInterfaceแล้วบอกว่า ภายในคุณสามารถยกตัวอย่างวัตถุของคลาสBและใช้วิธีการทั้งหมดBInterfaceโดยการเรียกฟังก์ชันที่เกี่ยวข้องของBโดยการเรียกฟังก์ชั่นที่สอดคล้องกันของ

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


นี่คือคำอธิบายที่ชัดเจนและเข้าใจง่ายที่สุด กรุณาเพิ่มขึ้นอีก
Algorini

12

สร้างอินเตอร์เฟส Java ไม่มีมรดกหลายอย่าง

http://csis.pace.edu/~bergin/patterns/multipleinheritance.html


9
aww ทำไม downvote? ฉันไม่ได้หมายถึงฉันแค่คิดว่าเขาสามารถที่จะทำการอ่านและคิดเกี่ยวกับปัญหาของเขาเองก่อนที่เราจะสร้างชั้นเรียนให้เขา
slandau

6

Java ไม่สนับสนุนการสืบทอดหลายรายการ แต่คุณสามารถลองใช้สองอินเตอร์เฟสขึ้นไป


4

ใช่. slandau ถูกต้อง Java ไม่อนุญาตให้ขยายจากหลายคลาส

สิ่งที่คุณต้องการน่าจะเป็น public class Main extends ListActivity implements ControlMenuสิ่งที่คุณต้องการอาจจะเป็นฉันเดาว่าคุณกำลังพยายามทำรายการ

หวังว่าจะช่วย


3

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

ตัวอย่างเช่นคุณสามารถสร้างคลาสนามธรรมและอินเทอร์เฟซ:

public abstract class FatherClass {

    abstract void methodInherit() {
        //... do something
    }
}

public interface InterfaceWithDefaultsMethods {
    default void anotherMethod() {
        //... do something
        //... maybe a method with a callable for call another function.
    }
}

ดังนั้นหลังจากนั้นคุณสามารถขยายและใช้คลาสทั้งสองและใช้ทั้งสองวิธี

public class extends FatherClass implements InterfaceWithDefaultsMethods {

    void methode() {
        methodInherit();
        anotherMethod();
    }
}

หวังว่านี่จะช่วยคุณ ...



2

มันเป็นไปได้

public class ParallaxViewController<T extends View & Parallaxor> extends ParallaxController<T> implements AbsListView.OnScrollListener {

//blah
}

ParallaxorและParallaxControllerไม่เป็นที่รู้จักดังนั้นฉันคิดว่ามันไม่ใช่ SDK- คลาส ดังนั้นนี่ใช้ไม่ได้สำหรับฉัน (และคำตอบนี้แสดงข้อผิดพลาด) ParallaxController คืออะไร นี่เป็นการพึ่งพาที่เฉพาะเจาะจงหรือไม่? โปรดอธิบายอย่างละเอียด
โซอี้

1

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

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


0

คุณไม่สามารถทำหลายมรดกใน java พิจารณาใช้อินเตอร์เฟส:

interface I1 {}
interface I2 {}
class C implements I1, I2 {}

หรือคลาสภายใน:

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