Dlaczego enum nie może rozszerzać innych class a zwykłe klasy mogą?

0

Witam,

Dręczy mnie jedno pytanie związane z enumem i dziedziczeniem. W dokumentacji przeczytałem, że enum może implementować interfejs ale nie może dziedziczyć po innych klasach ponieważ każdy enum rozszerza klasę java.lang.Enum a jako że w Javie nie ma wielekrotnego dziedziczenia to nie może niczego rozszerzać. Ale przecież każda klasa w Javie dziedziczy po klasie Object i nie stoi to na przeszkodzie, żeby mogły dziedziczyć.

pozdrawiam,

0

Jeśli A dziedziczy po B a B dziedziczy po C to A też dziedziczy po C.

0

Dlaczego enum nie może rozszerzać innych class a zwykłe klasy mogą?

Bo tak zdecydowano.

0

Źle mnie zrozumiano. Nie chce dyskutować na temat dlaczego zdecydowano tak a nie inaczej tylko jak to jest że enum nie może dziedziczyć ponieważ już rozszerza klasę java.lang.Enum a każda klasa mimo że również na starcie rozszerza klasę Object to mimo to można rozszerzyć inną klasę.

0

Z prostej przyczyny - enum - poza wieloma innymi rzeczami - dodaje "final class" na starcie.

Jeśli zrobisz:

public final class SomeClass {}
public class AnotherClass extends SomeClass // błąd kompilacji

też się wyłoży

3

Ponieważ pod spodem enum dziedziczy po Enum:

public enum WhyEnumNotExtends {

	A, B;

}

jest kompilowany do:

// javap.exe WhyEnumNotExtends.class
// Compiled from "WhyEnumNotExtends.java"
public final class pl.koziolekweb._4p.WhyEnumNotExtends extends java.lang.Enum<pl.koziolekweb._4p.WhyEnumNotExtends> {
  public static final pl.koziolekweb._4p.WhyEnumNotExtends A;
  public static final pl.koziolekweb._4p.WhyEnumNotExtends B;
  public static pl.koziolekweb._4p.WhyEnumNotExtends[] values();
  public static pl.koziolekweb._4p.WhyEnumNotExtends valueOf(java.lang.String);
  static {};
}
0

@Koziołek: aha dzięki teraz rozumiem, czyli w przypadku enum mamy do czynienia z jawnym dziedziczeniem? A jak wygląda niejawne dziedziczenie w przypadku klasy Object? Każda klasa dziedziczy niejawnie po Object, ale nie nic nie stoi na przeszkodzie żeby rozszerzyć inną klasę, więc domyślam się że w przypadku niejawnego kompilator nie dodaje extends skompilowanej klasy? Więc jak to działa, że klasy posiadają metody z klasy Object?

0

Tutaj też masz niejawne dziedziczenie, bo ten kawałek extends java.lang.Enum<WhyEnumNotExtends> jest dopisywany przez kompilator. Podobnie jak w przypadku zwykłych klas. Jedyna różnica polega na tym, że w przypadku zwykłych klas javap pominie ten zapis, ale jak już dasz mu przełącznik -v, to będziesz widział:

  Last modified 2017-05-11; size 1807 bytes
  MD5 checksum 88c5f074ba51c76e85becba99ab982c7
  Compiled from "Collataz.java"
public class pl.koziolekweb._4p.Collataz extends java.lang.Object implements java.lang.Iterable<java.lang.Integer>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
0

Aha czyli rozumiem, że różnica polega na tym że kompilator w przypadku zwykłych klas ignoruje extends Object i dlatego można dołożyć jeszcze jedno dziedziczenie? Bo w sumie jak jakaś klasa dziedziczy po innej klasie to rozszerza dwie klasy, ponieważ jeszcze Object prawda?

2

Nie, Object i tak i tak jest w łańcuchu dziedziczenia więc nie rozszerzasz w ten sposób 2 klas

0

Jeśli rozszerzasz klasę Zwierzę, to nie musisz pisać, że rozszerzasz klasę Object, ponieważ Zwierzę rozszerza Object. Czyli wystarczy, że napiszesz, że rozszerzasz Zwierzę. I nie jest to wielokrotne dziedziczenie, możesz rozszerzać w tym sensie np. 3 klasy, jeśli Zwierzę będzie jeszcze po czymś dziedziczyć. W rzeczywistości rozszerzasz tylko klasę Zwierzę.

3

OK, rozszerzyliśmy:

enum Answer {
    YES,
    NO
}
 
enum NuancedAnswer extends Answer {
    MAYBE
}

Teraz co tu ma się stać?

void doSomething(Answer answer) {
    Answer copy = answer;
}

doSomething(NuancedAnswer.MAYBE);

Jakiego typu jest copy? Ono nie może już być zadeklarowanego typu Answer, bo Answer nie zawiera wartości MAYBE. Polimorfizm się tu załamuje.

Animal animal = new Dog() to jest co innego, ponieważ Dog może zawierać dodatkową metodę np. bark, ale nie przeszkadza to w patrzeniu na niego jako na Animal. Nic, co ma Dog, nie wyklucza go z tego, by mieścić się w przestrzeni możliwych wartości Animal. Po prostu części charakterystyk obiektu Dog nie będziemy widzieć, póki nie nałożymy specjalnych okularów rzutujących.

Dog nie może jednak wykluczyć niczego z zakresu Animal - "wyprzeć się" rodzica. Np. rozszerzając klasę Animal nie możesz "skasować" żadnej metody rodzica. Nie ma w Javie czegoś takiego:

public class Dog extends Animal {
    public void bark() {
        // arff!
    }

    // a tu USUWAMY metodę, którą posiadał rodzic - w Javie to niemożliwe, i też popsułoby polimorfizm, jak i zaburzyło działanie psa
    delete void takeShit();
}

MAYBE to co całkiem innego. MAYBE to nie jest ani YES, ani NO. Ani trochę, w żadnym stopniu. Nie ma z nimi wspólnego mianownika. To są wartości które - inaczej niż Dog i Animal - wykluczają się. Wynika to z faktu, że enum nie jest tak naprawdę (w sensie koncepcyjnym) klasą, tylko multitonem.

Gdybyśmy mieli go "zasymulować" (np. w języku, w którym nie ma takich enumów jak w Javie - jak C#), wyglądałoby to tak:

public class EmulatedEnum {
    static final Value YES = new Yes();
    static final Value NO = new No();

    static class Value {
    }

    static final class Yes extends Value {
    }

    static final class No extends Value {
    }
}

Inaczej mówiąc, YES nie jest (znowu, w sensie koncepcyjnym) wartością typu EmulatedEnum. EmulatedEnum to tylko nazwa wiadra na wartości. Dlatego idee dziedziczenia nie do końca się tu moim zdaniem sprawdzają.

1 użytkowników online, w tym zalogowanych: 0, gości: 1