Mở rộng từ hai lớp


150

Tôi có thể làm cái này như thế nào:

public class Main extends ListActivity , ControlMenu 

Ngoài ra, tôi muốn biết rằng cách tiếp cận này là ổn vì tôi đã tạo các menu trong lớp là ControlMothy và tôi sẽ mở rộng trong các hoạt động còn lại.


4
Bạn không thể mở rộng hai hoặc nhiều lớp cùng một lúc. Nhiều kế thừa không được phép trong java.
Yogsma

Câu trả lời:


156

Bạn chỉ có thể mở rộng một lớp duy nhất. Và thực hiện Giao diện từ nhiều nguồn.

Mở rộng nhiều lớp không có sẵn. Giải pháp duy nhất tôi có thể nghĩ đến là không kế thừa một trong hai lớp mà thay vào đó là có một biến nội bộ của mỗi lớp và thực hiện nhiều proxy hơn bằng cách chuyển hướng các yêu cầu đến đối tượng của bạn đến đối tượng mà bạn muốn chúng đến.

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

đây là giải pháp tốt nhất tôi nghĩ ra Bạn có thể nhận được chức năng từ cả hai lớp và Vẫn chỉ thực sự là một loại lớp.

Hạn chế là bạn không thể phù hợp với Khuôn mẫu của lớp Nội bộ bằng cách sử dụng ép kiểu.


2
bạn đã nhanh hơn :) Tôi sẽ để lại câu trả lời của mình như một vài câu lan man trừu tượng để so sánh với ví dụ cụ thể của bạn;)
Nicolas78

1
Đây là một thay thế rất tốt đẹp, và cung cấp sự linh hoạt hơn rất nhiều trong tương lai.
David Souther

1
Xem giải pháp @SCB để biết thêm giải pháp OOP
nhàn rỗi

Tôi có thể sử dụng một ví dụ chính xác hơn ở đây. Không chắc là tôi hiểu.
Neo42

54

Tại sao không sử dụng Lớp bên trong (Làm tổ)

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

4
Tiếp cận xen kẽ, các chuyên gia nghĩ gì về kỹ thuật này? Tôi muốn tìm hiểu sâu về nó.
TechNyquist

1
Có ổn với các quy ước?
totten

2
Không thể có được lớp bên trong để làm việc trong một Fragment. Hoạt động của tôi phải mở rộng Fragment, điều đó có nghĩa là tôi hoàn toàn cần getView(), sẽ không hoạt động nếu nó ở bên trong lớp bên trong và mã ở đó là nơi tôi cần sử dụng mã từ đó ListActivity, vì vậy tôi không thể gia hạn hai lần, HOẶC làm một lớp bên trong trong ví dụ này
Azurespot 13/03/2015

Đây là giải pháp tốt nhất để mở rộng các lớp trừu tượng nơi bạn cần truy cập vào các biến của lớp tạp chất.
tak3shi

45

Như mọi người khác đã nói. Không, bạn không thể. Tuy nhiên, mặc dù mọi người đã nói nhiều lần trong nhiều năm qua rằng bạn nên sử dụng nhiều giao diện mà họ chưa thực sự đi vào. Hy vọng điều này sẽ giúp.

Nói rằng bạn có class Fooclass Barcả hai bạn muốn thử mở rộng thành một class FooBar. Tất nhiên, như bạn đã nói, bạn không thể làm:

public class FooBar extends Foo, Bar

Mọi người đã đi vào lý do cho điều này đến một mức độ nào đó. Thay vào đó, viết interfacescho cả hai FooBarbao gồm tất cả các phương pháp công khai của họ. Ví dụ

public interface FooInterface {

    public void methodA();

    public int methodB();

    //...
} 

public interface BarInterface {

    public int methodC(int i);

    //...
}

Và bây giờ thực hiện FooBarthực hiện các giao diện tương đối:

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

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

Bây giờ, với class FooBar, bạn có thể thực hiện cả hai FooInterfaceBarInterfacetrong khi giữ một FooBarđối tượng và chỉ cần truyền thẳng các phương thức:

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

    //...

}

Phần thưởng cho phương pháp này, là FooBarđối tượng phù hợp với khuôn của cả hai FooInterfaceBarInterface. Điều đó có nghĩa là điều này là hoàn toàn tốt:

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

Hy vọng điều này làm rõ cách sử dụng giao diện thay vì nhiều tiện ích mở rộng. Ngay cả khi tôi trễ vài năm.


6
đây là câu trả lời hay nhất
user3290180

Đây là một câu trả lời rất tốt, tuy nhiên tôi thấy một vài vấn đề với nó sẽ cần được giải quyết chi tiết hơn. Đầu tiên mở rộng từ hai lớp hiện tại sẽ có một chút vấn đề hơn và sẽ có một số trở ngại thêm phải trải qua, Thứ hai là nếu giao diện Foo và Bar đều có cùng chức năng, bạn sẽ cần phải có thứ tự ưu tiên. Rõ ràng việc mở rộng lớp học của bạn sẽ buộc bạn phải đưa ra những quyết định đó. Tuy nhiên, tùy chọn tuyệt vời :) +1
Bộ giải mã lười biếng

Điều gì xảy ra nếu chúng ta phải tạo các lớp mới "thường xuyên" và làm cho mỗi lớp đó thực hiện cả hai giao diện? Sau đó, chúng ta phải lặp lại các triển khai soạn sẵn này trong mỗi lớp. Có cách nào tốt hơn để làm cho một lớp "thực hiện" hai bộ hành vi không?
MasterJoe2

1
@ MasterJoe2 Thành thật mà nói, tôi đã không sử dụng Java nhiều trong một thời gian (tôi đã dừng lại khoảng thời gian tôi viết câu trả lời này). Một trong những lý do tôi tránh nó ngay bây giờ là vấn đề bạn nêu ra. Một linh cảm cho một hướng bạn có thể điều tra, đang viết một lớp trừu tượng thực hiện cả hai giao diện, và mở rộng điều đó. Tuy nhiên tôi không biết liệu đó có phải là Java hợp lệ hay không và tôi cảm thấy rằng bạn sẽ chỉ gặp cùng một vấn đề ban đầu khi bạn muốn thay đổi lớp triển khai trong tương lai. Xin lỗi tôi không thể giúp đỡ nhiều hơn.
SCB

37

Bạn sẽ muốn sử dụng giao diện. Nói chung, nhiều kế thừa là xấu vì vấn đề Kim cương:

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 ++ và những người khác có một vài cách để giải quyết điều này, vd

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

nhưng Java chỉ sử dụng các giao diện.

Java Trails có phần giới thiệu tuyệt vời về các giao diện: http://doad.oracle.com/javase/tutorial/java/con accept / inter.html.html Bạn có thể muốn theo dõi điều đó trước khi đi sâu vào các sắc thái trong API Android.


24
Tôi không bao giờ hiểu tại sao vấn đề kim cương thực sự là một vấn đề ngăn chặn nhiều sự kế thừa. Tại sao trình biên dịch không thể phàn nàn nếu có các phương thức xung đột có cùng tên?
pete

1
Đối với trình biên dịch đủ thông minh, thì không. Việc giải quyết nó ở cấp độ trình biên dịch là có thể, và thực sự C ++ thực hiện nó với virtual class. Làm như vậy đi kèm với nhiều cảnh báo và cảnh báo, cho cả trình biên dịch và nhà phát triển.
David Souther

1
Làm thế nào về các phương thức mặc định giống nhau trong hai giao diện? Đó là một ví dụ khác về vấn đề kim cương, mà java có thể xử lý.
Lajos Meszaros

1
@ MészárosLajos Nhưng bạn không gọi supertừ trong một phương thức kế thừa. Vâng, bạn có thể, nhưng bạn phải chỉ định phương thức giao diện để gọi (và nó phải sử dụng từ khóa defaulttrong việc thực hiện giao diện) . Một ví dụ là: MyIFace.super.foo()trong đó MyIFace là một giao diện. Như bạn có thể thấy phương thức giao diện để thực thi được xác định và tránh hoàn toàn Vấn đề Kim cương. Nếu bạn mở rộng MyClass1MyClass2, cả hai lớp đều có một foo()và gọi super.foo()thì trình biên dịch sẽ bị ném bởi Bài toán kim cương.
JDSweetBeat

Bạn không thể trả về "bar"hoặc "baz"với loại phương thức void. Dù sao, giải thích tốt xD
xdevs23

22

Phải, như mọi người khác đã viết, bạn không thể thực hiện nhiều kế thừa trong Java. Nếu bạn có hai lớp mà bạn muốn sử dụng mã, thông thường bạn chỉ cần lớp con một (lớp nói A). Đối với lớp B, bạn trừu tượng hóa các phương thức quan trọng của nó với một giao diện BInterface(tên xấu, nhưng bạn có ý tưởng), sau đó nói Main extends A implements BInterface. Bên trong, bạn có thể khởi tạo một đối tượng của lớp Bvà thực hiện tất cả các phương thức BInterfacebằng cách gọi các hàm tương ứng của B.

Điều này thay đổi mối quan hệ "is-a" thành mối quan hệ "has-a" vì Mainhiện tại bạn là một A, nhưng có một B. Tùy thuộc vào trường hợp sử dụng của bạn, bạn thậm chí có thể thực hiện thay đổi đó rõ ràng bằng cách xóa BInterfacekhỏi Alớp của mình và thay vào đó cung cấp một phương thức để truy cập trực tiếp vào đối tượng B của bạn.


Đây là lời giải thích dễ đọc và dễ hiểu nhất. Xin vui lòng thêm nhiều người ủng hộ.
Algorini


6

Java không hỗ trợ nhiều kế thừa, nhưng bạn có thể thử triển khai hai hoặc nhiều giao diện.


4

Đúng. slandau là đúng. Java không cho phép mở rộng từ một số lớp.

Những gì bạn muốn có lẽ là public class Main extends ListActivity implements ControlMenu. Tôi đoán bạn đang cố gắng lập một danh sách.

Mong rằng sẽ giúp.


3

Giống như một phương án khác, có thể bạn có thể sử dụng một giao diện với cách triển khai mặc định của một phương thức. Điều đó phụ thuộc vào tất nhiên những gì bạn muốn làm.

Ví dụ: bạn có thể tạo một lớp trừu tượng và giao diện:

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.
    }
}

Vì vậy, sau đó, bạn có thể mở rộng và thực hiện cả hai lớp và sử dụng cả hai phương thức.

public class extends FatherClass implements InterfaceWithDefaultsMethods {

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

Hy vọng điều này sẽ giúp bạn ...



2

điều đó là có thể

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

//blah
}

ParallaxorParallaxControllerkhông xác định, vì vậy tôi cho rằng chúng không phải là các lớp SDK. Vì vậy, điều này không làm việc cho tôi (và do đó câu trả lời này cho thấy lỗi). ParallaxControll là gì? Đây có phải là một phụ thuộc cụ thể? Xin hãy giải thích
Zoe

1

Những người tạo ra java đã quyết định rằng các vấn đề của nhiều kế thừa lớn hơn các lợi ích, vì vậy chúng không bao gồm nhiều kế thừa. Bạn có thể đọc về một trong những vấn đề lớn nhất của đa thừa kế (vấn đề kim cương kép) tại đây .

Hai khái niệm tương tự nhất là triển khai giao diện và bao gồm các đối tượng của các lớp khác là thành viên của lớp hiện tại. Sử dụng các phương thức mặc định trong các giao diện gần như giống hệt như nhiều kế thừa, tuy nhiên việc sử dụng một giao diện chỉ có các phương thức mặc định được coi là thực tế xấu.


0

bạn không thể thực hiện nhiều kế thừa trong java. xem xét sử dụng các giao diện:

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

hoặc các lớp bên trong:

class Outer {
    class Inner1 extends Class1 {}
    class Inner2 extends Class2 {}
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.