Rozszerzenie z dwóch klas

150

W jaki sposób mogę to zrobić:

public class Main extends ListActivity , ControlMenu 

Chciałbym również wiedzieć, czy to podejście jest w porządku, że stworzyłem menu w klasie ControlMenu i rozszerzam pozostałe czynności.

umar
źródło
4
Nie możesz jednocześnie przedłużyć dwóch lub więcej zajęć. Dziedziczenie wielokrotne nie jest dozwolone w Javie.
yogsma

Odpowiedzi:

156

Możesz rozszerzyć tylko jedną klasę. I implementuj interfejsy z wielu źródeł.

Przedłużanie wielu klas nie jest dostępne. Jedynym rozwiązaniem, które przychodzi mi do głowy, nie jest dziedziczenie żadnej z klas, ale zamiast tego posiadanie wewnętrznej zmiennej każdej klasy i robienie więcej z proxy, przekierowując żądania do obiektu do obiektu, do którego mają trafiać.

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

to najlepsze rozwiązanie, jakie wymyśliłem. Możesz uzyskać funkcjonalność z obu klas, a nadal może być tylko jednego typu.

Wadą jest to, że nie można dopasować się do klasy formy wewnętrznej za pomocą odlewu.

Leniwy koder
źródło
2
byłeś szybszy :) Zostawię moją odpowiedź jako abstrakcyjną wędrówkę do porównania z twoim konkretnym przykładem;)
Nicolas78
1
To bardzo fajna alternatywa, zapewniająca dużo większą elastyczność w przyszłości.
David Souther,
1
Zobacz rozwiązanie @SCB, aby uzyskać więcej rozwiązań OOP
idish
Mógłbym tu posłużyć się dokładniejszym przykładem. Nie jestem pewien, czy rozumiem.
Neo42,
54

Dlaczego nie użyć klasy wewnętrznej (zagnieżdżanie)

class A extends B {
    private class C extends D {
        //Classes A , B , C , D accessible here 
    }
}
Ifte
źródło
4
Ciekawe podejście, co eksperci sądzą o tej technice? Chciałbym się w to zagłębić.
TechNyquist
1
Czy to jest w porządku z konwencjami?
totten
2
Nie można zmusić klasy wewnętrznej do pracy w Fragment. Moja aktywność musi się rozszerzać Fragment, co oznacza, że ​​absolutnie potrzebuję getView(), co nie zadziała, jeśli znajduje się w klasie wewnętrznej, a kod tam jest, z którego muszę użyć kodu ListActivity, więc nie mogę dwukrotnie rozszerzyć LUB zrobić klasy wewnętrznej w tym przykładzie.
Azurespot
Jest to najlepsze rozwiązanie do rozszerzania klas abstrakcyjnych, w których potrzebny jest dostęp do zmiennych klasy implementującej.
tak3shi
45

Jak wszyscy powiedzieli. Nie, nie możesz. Jednak nawet jeśli ludzie wielokrotnie powtarzali przez lata, że ​​powinieneś używać wielu interfejsów, tak naprawdę nie wnikali w to, jak to zrobić. Mam nadzieję, że to pomoże.

Powiedzmy, że masz class Fooi class Baroboje chcecie spróbować rozszerzyć do class FooBar. Oczywiście, jak powiedziałeś, nie możesz:

public class FooBar extends Foo, Bar

Ludzie do pewnego stopnia już wnikali w przyczyny takiego stanu rzeczy. Zamiast tego napisz interfacesdla obu Fooi Baromów wszystkie ich publiczne metody. Na przykład

public interface FooInterface {

    public void methodA();

    public int methodB();

    //...
} 

public interface BarInterface {

    public int methodC(int i);

    //...
}

A teraz stwórz Fooi Barzaimplementuj odpowiednie interfejsy:

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

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

Teraz, z class FooBar, można realizować zarówno FooInterfacei BarInterfaceprzy zachowaniu Fooi Barobiekt i tylko przejazdem metody prosto przez:

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

    //...

}

Zaletą tej metody jest to, że FooBarprzedmiot pasuje do formy zarówno FooInterfacei BarInterface. Oznacza to, że wszystko jest w porządku:

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

Mam nadzieję, że to wyjaśnia, jak używać interfejsów zamiast wielu rozszerzeń. Nawet jeśli spóźnię się kilka lat.

SCB
źródło
6
to jest najlepsza odpowiedź
user3290180
To bardzo dobra odpowiedź, ale widzę kilka problemów, które wymagają bardziej szczegółowego rozwiązania. Po pierwsze rozszerzenie z dwóch istniejących klas byłoby nieco bardziej problematyczne i wymagałoby przejścia przez szereg dodatkowych przeszkód. Po drugie, gdyby interfejs Foo i Bar miały te same funkcje, musiałbyś mieć kolejność pierwszeństwa. Wyraźne rozszerzenie klasy zmusiłoby cię do podjęcia tych decyzji z góry. Mimo to doskonała opcja :) +1
The Lazy Coder
A jeśli będziemy musieli „często” tworzyć nowe klasy i sprawić, by każda z tych klas implementowała oba interfejsy? Następnie musielibyśmy powtórzyć te standardowe implementacje w każdej klasie. Czy istnieje lepszy sposób, aby klasa „zaimplementowała” dwa zestawy zachowań?
MasterJoe2,
1
@ MasterJoe2 Szczerze mówiąc, od jakiegoś czasu nie robiłem zbyt wiele z Javą (zatrzymałem się mniej więcej w czasie, gdy pisałem tę odpowiedź). Jednym z powodów, dla których teraz tego unikam, jest sam problem, który podnosisz. Przeczuciem w kierunku, który mógłbyś ewentualnie zbadać, jest napisanie klasy abstrakcyjnej, która implementuje oba interfejsy i rozszerza to. Nie mam jednak pojęcia, czy to poprawna Java i czuję, że po prostu skończysz z tym samym pierwotnym problemem, gdy będziesz chciał wprowadzić zmiany w klasie implementującej w przyszłości. Przepraszam, nie mogę być bardziej pomocny.
SCB
37

Będziesz chciał używać interfejsów. Generalnie dziedziczenie wielokrotne jest złe z powodu problemu diamentowego:

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 ++ i inne mają kilka sposobów rozwiązania tego problemu, np

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

ale Java używa tylko interfejsów.

Ścieżki Java mają świetne wprowadzenie do interfejsów: http://download.oracle.com/javase/tutorial/java/concepts/interface.html Prawdopodobnie zechcesz to prześledzić, zanim zagłębisz się w niuanse w Android API.

David Souther
źródło
24
Nigdy nie rozumiałem, dlaczego problem z diamentami jest w rzeczywistości problemem, który zapobiega wielokrotnemu dziedziczeniu. Dlaczego kompilator nie może po prostu narzekać, jeśli istnieją sprzeczne metody o tej samej nazwie?
pete
1
W przypadku wystarczająco inteligentnych kompilatorów tak nie jest. Rozwiązanie tego na poziomie kompilatora jest możliwe i rzeczywiście C ++ robi to z virtual class. Takie postępowanie wiąże się z wieloma ostrzeżeniami i zastrzeżeniami, zarówno dla kompilatora, jak i programisty.
David Souther,
1
A co z tymi samymi domyślnymi metodami w dwóch interfejsach? To kolejny przykład problemu z diamentami, z którym może sobie poradzić Java.
Lajos Meszaros
1
@ MészárosLajos Ale nie wywołujesz superfrom w dziedziczeniu metody. Cóż, możesz, ale musisz określić metodę interfejsu do wywołania (i musi ona użyć słowa kluczowego defaultw implementacji interfejsu) . Przykład: MyIFace.super.foo()gdzie MyIFace jest interfejsem. Jak widać, metoda interfejsu do wykonania jest zdefiniowana i całkowicie pozwala uniknąć problemu diamentów. Jeśli rozszerzysz MyClass1i MyClass2, obie klasy mają a foo(), i wywołasz, super.foo()kompilator zostanie zgłoszony przez Diamentowy Problem.
JDSweetBeat
Nie możesz zwrócić "bar"ani "baz"z typem metody void. W każdym razie, dobre wyjaśnienie xD
xdevs23
22

Tak, jak wszyscy pisali, w Javie nie można dziedziczyć wielokrotnego. Jeśli masz dwie klasy, z których chciałbyś użyć kodu, zwykle tworzysz podklasę jedną (powiedzmy class A). W przypadku klasy B, przenosisz ważne metody do interfejsu BInterface(brzydka nazwa, ale masz pomysł), a następnie mówisz Main extends A implements BInterface. Wewnątrz możesz utworzyć instancję obiektu klasy Bi zaimplementować wszystkie metody programu BInterface, wywołując odpowiednie funkcje B.

Zmienia to relację „jest-a” w relację „ma-a”, ponieważ twoje Mainteraz jest A, ale ma B. W zależności od przypadku użycia możesz nawet wprowadzić tę zmianę jawnie, usuwając element BInterfacez Aklasy i zamiast tego udostępnić metodę bezpośredniego dostępu do obiektu B.

Nicolas78
źródło
To najbardziej czytelne i łatwe do zrozumienia wyjaśnienie. Poproszę o więcej głosów za.
Algorini
6

Java nie obsługuje dziedziczenia wielokrotnego, ale możesz spróbować zaimplementować dwa lub więcej interfejsów.


źródło
4

Tak. slandau ma rację. Java nie pozwala na rozszerzanie z kilku klas.

Prawdopodobnie chcesz public class Main extends ListActivity implements ControlMenu. Domyślam się, że próbujesz zrobić listę.

Mam nadzieję, że to pomoże.

wir
źródło
3

Podobnie jak inna alternatywa, być może możesz użyć interfejsu z domyślną implementacją metody. To oczywiście zależy od tego, co chcesz zrobić.

Na przykład możesz utworzyć klasę abstrakcyjną i interfejs:

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

Następnie możesz rozszerzyć i zaimplementować obie klasy i użyć obu metod.

public class extends FatherClass implements InterfaceWithDefaultsMethods {

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

Mam nadzieję, że to ci pomoże ...

odciski palców
źródło
2

Rozszerzanie z wielu klas nie jest dozwolone w Javie, aby zapobiec Deadly Diamond of death !

akkig
źródło
2

to jest możliwe

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

//blah
}
Jonathan
źródło
Parallaxori ParallaxControllersą nieznane, więc zakładam, że nie są to klasy SDK. Więc to nie działa dla mnie (i dlatego ta odpowiedź pokazuje błędy). Co to jest ParallaxController? Czy to jest konkretna zależność? Proszę rozwinąć
Zoe
1

Twórcy javy zdecydowali, że problemy wielokrotnego dziedziczenia przeważają nad korzyściami, więc nie uwzględnili dziedziczenia wielokrotnego. Możesz przeczytać o jednym z największych problemów związanych z dziedziczeniem wielokrotnym (problem podwójnego diamentu) tutaj .

Dwie najbardziej podobne koncepcje to implementacja interfejsu i dołączanie obiektów innych klas jako elementów składowych bieżącej klasy. Używanie metod domyślnych w interfejsach jest prawie dokładnie tym samym, co dziedziczenie wielokrotne, jednak używanie interfejsu tylko z metodami domyślnymi jest uważane za złą praktykę.

Pika, czarodziej wielorybów
źródło
0

nie możesz zrobić wielokrotnego dziedziczenia w java. rozważ użycie interfejsów:

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

lub klasy wewnętrzne:

class Outer {
    class Inner1 extends Class1 {}
    class Inner2 extends Class2 {}
}

źródło