Dziedziczenie Java - problem poczatkujacego

Dziedziczenie Java - problem poczatkujacego
WR
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:1
0

Hej. To będzie mój pierwszy post na forum więc witam wszystkich i przejdę od razu do rzeczy.

Otóż uczę się od miesiąca Javy (chcę zostać testerem automatyzującym) i natrafiłam na problem przy dziedziczeniu. Otóż:
Załóżmy, że mamy klasę Animal i dziedziczacą po niej klasę Dog.
Jaka jest różnica pomiędzy deklaracją nowego obiektu
1.Animal reks = new Animal(); a
2.Animal fuks = new Dog(); ?

3.Dog max = new Dog(); w miarę ogarniam.

Jak tworzę takie klasy w IntelliJ, to przy tworzeniu obiektu w drugi sposób cała psowosc mojego Fuksa gdzieś znika i nie widzę różnicy w dostępie do metod i pól w przypadkach 1 i 2. Oba mają dostęp do pól i metod wyłącznie w obrębie klasy Animal. Dlaczego?
Czy zapis drugi został stworzony wyłącznie pod klasy abstrakcyjne (bo jakimś cudem jak zrobię abstract Animal, to deklaracja obiektu 2 jest prawidłowa, chociaż jak rozumiem typ Fuksa to będzie Animal, a przecież nie można tworzyć obiektu typu klasy abstrakcyjnej. Pewnie umknął mi jakiś niuans, ale przyznam że im więcej się nad tym zastanawiam i szukam, tym mniej rozumiem.
Wiem, że deklaracja 1 w wypadku abstrakcyjności Animal oczywiście odpada). Czy metoda 2 ma jeszcze jakieś inne zastosowanie (poza utworzeniem obiektu typu Animal którego normalnie nie możnaby było utworzyć przy klasie abstrakcyjnej?).
Bo w momencie kiedy wszystkie klasy są konkretne, to nie widzę ZADNEJ różnicy pomiędzy reksem a fuksem (a na pewno jakaś jest).

Dzięki z góry

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
2

new Animal(); NIE powinno się skompilować, zaś osiąga się to poprzez public abstract class Animal ...
Jeżeli klasa Dog nie ma żadnej własnej metody (nie liczymy @Override oraz Konstruktora) to:
zapisy:

  • Animal fuks = new Dog();
  • Dog max = new Dog();
    niczym się nie różnią.
    Natomiast jeżeli ma własne metody to:
  • max.frisk();
  • ((Dog)fuks).frisk();
    Zawsze możesz sprawdzić if(fuks instanceof Dog)
    Rozważ Animal[] array=new Animal[] {new Dog(), new Cat(), new Cow()};

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 2x, ostatnio: _13th_Dragon
WR
Ok. Dzięki. Trochę mi to wyjaśniło. Ale dalej nie rozumiem różnicy pomiędzy reksem, a fuksem. Fuks ma dostęp tylko do metod klasy Animal (tak samo jak reks). Co się stało z klasą Dog? Co zostało z psowosci fuksa?
_13th_Dragon
reks nie ma prawa zaistnieć ponieważ ty jako człowiek https://pl.wikipedia.org/wiki/Cz%C5%82owiek_(rodzaj) też należysz do Animal czyli Animal wieronika=new Human(); natomiast Animal x=Animal(); co to ma być? Czy istnieje coś po prosty zewrzecie? Jak mam odpowiedzieć na pytanie czym się różni wieronika od x? Może tym że wieronika istnieje (no przynajmniej jest wielka szansa że wciąż tak) zaś x na 100% jakiś abstrakcyjny wymyśl rodem z "since fiction".
WR
No jakbyś zrobił np grę, w której są oprócz zwierząt np. elementy terenu, które się nie poruszają w odróżnieniu od zwierząt, to deklaracja Animal x = new Animal już jak najbardziej wg mnie miałaby sens. A ja jako Human od Animal różnie się tym np, że mam metodę speak, której Animals nie mają. Czyli hierarchia byłaby taka: klasy: Alive i notAlive rozszerzają Obiekt, Animal rozszerzaj isAlive, Human rozszerza Animal itd. Ale to może przyklad był nietrafiony. Chodzi mi ogólnie o zasadę, o to, że mój program tak samo traktuje Animal x = new Animal, jak i Animal y = new Dog.
_13th_Dragon
Nie powinien, Animal musi być być abstract, dopiero jak coś konkretnego opisuje klasa to już przestaje być abstrakcyjną.
WR
Ok. Dzięki za pomoc.
AS
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad rok
  • Postów:21
5

Tak przeczytałem od góry do dołu i zabrakło mi takich określeń jak "polimorfizm" oraz "wirtualne wywołanie metod". Tak w szybkim skrócie, w Twoim przypadku oznacza to, że możesz zadeklarować sobie metody w "typie referencji" a zostaną one zawsze wywołane na "typie obiektu". Dzięki temu możesz tworzyć obiekty dziedziczące pewne stany lub/i zachowania poprzez klasy abstrakcyjne albo tworzyć wielokrotne kontrakty oparte tylko o zachowania poprzez interfejsy. Wielokrotne w sensie, że klasa może implementować wiele interfejsów. Przy pomocy tych dwóch podejść (klasy albo interfejsy) możesz tworzyć kolekcje i wywoływać na nich metody opisane na typie referencji, nie martwić się że każda może być inaczej zaimplementowana a i tak dla każdej instancji wykonają się prawidłowo. Możesz też przekazywać takie abstrakcyjne typy jako parametry w metodach, konstruktorach a w momencie wywołania przekazać odpowiednią implementację. To działanie można jeszcze dodatkowo zawęzić dzięki typom generycznym oraz PECS (Producer Extends, Consumer Super) Dodatkowe rzeczy które wynikają z dziedziczenia to - klasa dziedzicząca nie ma bezpośredniego dostępu do prywatnych pól klasy nadrzędnej (trzeba używać getterów i setterów) oraz nie są dziedziczone konstruktory. Czasami trzeba ręcznie w klasie nadrzędnej dodać konstruktor bezparametrowy. Tak w skrócie, bo pisane na kolanie na telefonie...

edytowany 3x, ostatnio: Ashapola
_13th_Dragon
Nie ma się do czego doczepić oprócz jednego - tekst nie dla początkującego.
WR
Dzięki Ashapola. Tylko dwa pytania: w jakim sensie konstruktory nie są dziedziczone? Jeśli stworzę klasę Dog która rozszerza Animal i tworzę obiekt Dog x = new Dog(); , to wedle mojej wiedzy najpierw zostaje wywolany konstruktor klasy Animal, a następnie klasy Dog (czyli Dog odziedziczył konstruktor po Animal. Chyba, że to się nie nazywa dziedziczenie). I mimo wszystko nie rozumiem, jakiego typu jest obiekt, który stworzę w ten sposób: Animal y = new Dog();. Czy on jest typu Dog, czy typu Animal? Skoro ma dostęp tylko do pól i metod Animal, to stawiam że jest jest typu Ani
WR
No i urwało mi tekst. No więc kończąc: chyba y jest typu Animal (i to jest fajne, bo mogę sobie potem wrzucić takiego y do listy innych obiektów typu Animal, nawet jak to będzie lista Cats czy Fishes to skoro wszystkie są typu Animal to się Java nie przyczepi, jeśli dobrze rozumiem). Ale... Co się po drodze dzieje z psowoscia mojego psa, skoro mój y nie ma już dostępu do metod psa? To na cholerę tam ten pies?
_13th_Dragon
Jeżeli klasa Animal nie ma konstruktora domyślnego Animal() czyli np. Animal(String name) to nie dasz rady stworzyć klasy Dog pochodzącą od Animal bez jawnego wywołania konstruktora Animal w tym przypadku Dog(String name) { super(name); } Więc konstruktor nie jest dziedziczony. Jeżeli ciebie interesuje wyłącznie psowość twego psa to po kiego ci dziedziczenie? Tak jak mówiłem wcześniej zawsze możesz użyć instanceof aby sprawdzić czy ten Animal to Dog, Cat czy też Cow
piotrpo
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 dni
  • Postów:3277
4

Dobra, będzie kontrowersyjnie. Nie kopię zwierząt jak by co....

Klasa abstrakcyjna, określająca co można zrobić z sierściuchem

Kopiuj
public abstract class Animal{
  public abstract String kick();
}
Kopiuj
public class Dog extends Animal{
  @Override //wskazuje, że metoda z klasy nadrzędnej jest nadpisywana
  public String kick(){
    return "Wof Wof";//implementacja dla konkretnego zwierzaka
  }
}
Kopiuj
public class Cat extends Animal{
   @Override
   public String kick(){
    return "Meow!!!"; 
  }
}
Kopiuj
Animal animal1 = new Dog();
Animal animal2 = new Cat();

System.out.println(animal1.kick()); //tutaj wiemy tylko tyle, że animal1 to zwierzę, nie wiemy jakie konkretnie (pies, 
//kot, czy inny sebix) ale wiadomo jaką metodę można na nim wywołać
System.out.println(animal2.kick());

output:

Kopiuj
Wof Wof
Meow!!!
edytowany 1x, ostatnio: piotrpo
Zobacz pozostałe 6 komentarzy
_13th_Dragon
Ale zawsze można: public class Fish extends Animal{ @Override public String kick() { return ""; } }
piotrpo
Jest już późno, dzieci śpią, return null;
_13th_Dragon
System.out.println(new Fish().kick()); i okazuje się że gada?
piotrpo
Nie na darmo jest to "trillion dollar bug".
AS
@WeronikaR: zapoznaj się z definicją "Polimorfizm".... Książka "Myślenie obiektowe w programowaniu. Wydanie V" (najlepiej czytać w oryginale) rozwiąże 90% Twoich problemów.
AS
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad rok
  • Postów:21
1

Niby różnica pomiędzy klasami abstrakcyjnymi a interfejsami w najnowszej Javie (tak od 8) jest symboliczna jak to określił @piotrpo ale jest jednak znacząca. Klasa realizuje problem z "jest" a interfejs z "zachowuje się jak", np. "Ferrari" jest "pojazdem" - bo jest jest konkretną instancją (implementacją) klasy "samochód", która dziedziczy po ogólnej klasie abstrakcyjnej "pojazd". Natomiast "szybowiec" zachowuje się jak "ptak" bo lata ale zarówno szybowiec -> statek powietrzny -> pojazd ani sokół -> ptak drapieżny -> ptak -> zwięrzę nie mają wspólnego drzewa dziedziczenia ale można nimi zarządzać wspólnie poprzez wspólny interfejs z metodą void fly();

Dodatkowo w Javie nie ma wielodziedziczenia klas:

Kopiuj
public abstract class Glider{
  public void printInfo() {
      System.out.println("I am a glider");
  }
}

public abstract class Plane{
  public void printInfo() {
      System.out.println("I am a plane");
  }
}

// To nie zadziała, bo nie może:
public class MotorGlider extends Glider, Plane{
  @Override
  public void printInfo() {
      System.out.println("I am a motor glider");
  }
}

Natomiast ze względu na możliwość tworzenia od Javy 8 metod "default" mamy w Javie "problem diamentu":

Kopiuj
public interface Glider{
  default public void printInfo() {
      System.out.println("Glider");
  }
}

public interface Plane{
  default public void printInfo() {
      System.out.println("Plane");
  }
}

// To zadziała:
public class MotorGlider implements Glider, Plane{
  @Ovveride
  public void printInfo() {
      System.out.println("I am a motor glider");
      System.out.printf("I am a kind of like a %s and a kind of like a %s%n", Glider.super.printInfo(), Plane.super.printInfo());
  }
}
edytowany 4x, ostatnio: Ashapola
piotrpo
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 dni
  • Postów:3277
0

@Ashapola: Oczywiście masz rację, zwyczajnie to co napisała OP, skłoniło mnie do nie wnikania na tym etapie w te rozważania. Wydaje mi się, że sporą przeszkodą w ogarnięciu o co biega w programowaniu obiektowym, jest próba tłumaczenia początkującym jednych nie zrozumiałych pojęć, przy pomocy innych niezrozumiałych pojęć.

@WeronikaR: Wydaje mi się, że bardzo wiele wyjaśniają słowa kluczowe extends i implements. W każdym razie mi pozwoliły zrozumieć o co chodzi z tymi całymi dziedziczeniami, polimorfizmami itd.
Interface definiuje jakiś zbiór akcji, które można wywoływać na implementujących go klasach. Żeby zostawić już w spokoju te zwierzaki, masz w Javie interface collection. Jeżeli jakiś obiekt go implementuje, to można na nim wywołać każdą z metod i spodziewać się, że zwróci c.a. identyczny rezultat, bez zastanawiania się jak to zrobi. Masz pod spodem zbiory, listy (również interface'y 'Set', 'List'), które dokładają po parę metod w zależności od tego, czy porządek jest istotny i czy chcemy mieć możliwość przechowywania duplikatów. Oba interface'y mają ileś tam różnych implementacji, w zależności od tego, czy akurat zależy nam na wydajności podczas wrzucania czegoś do listy na jej końcu, czy wolimy użyć mniej pamięci itp.

Teraz o klasie abstrakcyjnej.
Słówko kluczowe abstract może występować przy nazwie klasy i przy metodzie.
Jeżeli jest ono przy nazwie klasy, to dzieją się 2 rzeczy:

  • Nie da się utworzyć instancji takiej klasy, bez względu na to co jest w reszcie kodu.

  • Da się napisać abstract przy nazwie metody.

    Jeżeli abstract jest napisany przy metodzie, to metoda nie może być zaimplementowana w rodzicu, ale musi być zaimplementowana w klasach rozszerzających (chyba, że też będą abstrakcyjne).

    I teraz odpowiedź na pytanie czym właściwie różni się klasa abstrakcyjna od interface'u (to co wyżej masz napisane to oczywiście prawda).
    Interface z natury może mieć tylko metody publiczne.
    Natomiast klasa abstrakcyjna, znowu wracam do zwierzaków, może mieć np. tak:

Kopiuj
public abstract class Animal{
protected int attitude = 0;
  public final String kick(){
    makeNoise();
    changeAttitude()
  }
  public abstract String makeNoise();
  public abstract void changeAttitude();
}

i implementacja:

Kopiuj
public class Dog extends Animal{
  @Override
  public String makeNoise(){
    return "Wof Wof";
  }

  @Override
  public void changeAttitude(){
    attitude -=100; //odwołanie do chronionego pola klasy nadrzędnej
  }
}
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)