Dziedziczenie i słówko extends

0

Od dłuższego czasu mam problem ze zrozumieniem dziedziczenia w javie.
Rozumiem założenia hierarchizacji ale nie wiem jak to ma wyglądać w praktyce.
Napisałem dwie klasy:

public class Zwierze 
{

	private String imie;
	private String waga;
	
	public String getImie()
	{
		return imie;
	}
	
	void glos()
	{
		System.out.println("odglos");
	}
	
	
} 
public class KotTest 
{

	public static void main(String[] args) 
	{

		Zwierze zwierze = new Zwierze();
		zwierze.glos();
		System.out.println(zwierze.getImie());
	}

} 
  1. Czy to jest już dziedziczenie, gdzie używam pól i metod w dwóch różnych klasach?
  2. Po co używa się słówka extends, co ono daje?
0

extends "daje" właśnie to dziedziczenie.
A extends B oznacza, że klasa A dziedziczy po klasie B.

To, że masz 2 niepowiązane ze sobą klasy, które maja jakieś metody i pola z dziedziczeniem nie ma nic wspólnego.

0

Ale jeżeli mogę używać pól i metod z innej klasy bez słówka extends to po co mi one skoro nie daje mi ono nowych możliwości?
Tego właśnie nie rozumiem, samo założenie dziedziczenia jest dla mnie jasne ale już wykorzystanie go nie, tym bardziej słówko extends...

0

Nie rozumiem pytania. Mam na przykład 3 klasy, z jakąśtam implementacją. Klasa bazowa ma "wspólne" właściwości takie jak ścieżka do pliku, otwieranie pliku i wczytywanie surowych danych. Dziedziczące klasy dodatkowo wykonują parsowanie danych zgodnie ze swoim typem (czyli CSV albo XML).

class GenericInputFileReader{}
class XMLInputFileReader extends GenericInputFileReader{}
class CSVInputFileReader extends GenericInputFileReader{}

Mam teraz jakiś kod w programie:

GenericInputFileReader fileReader = getFileReader();
FileContents contents = fileReader.readFile(ścieżka);

Zauważ że w tym kodzie metoda getFileReader() może nam zwrócić obiekt dowolnej z tych 3 klas, ale kodu nie będę musiał nigdzie zmieniać. Co wiecej mogę jutro dopisać sobie nową klasę, która czyta jeszcze inne pliki, a następnie będę musiał tą nową klasę uwzględnić tylko w metodzie getFileReader bo cała reszta kodu pozostaje bez zmian.

Dziedziczenie pozwala mi tu również na nie powtarzanie tego samego kodu 3 razy. Bo otwarcie pliku i wczytanie zawartości w każdej sytuacji wygląda identycznie, jedyna różnica jest taka że "parsowanie" danych wykonuje jakieś transformacje albo po prostu wypluwa treść pliku. Wygodniej więc zamknąć tą "wspólną" logikę w jednej klasie. Szczególnie że może się okazać że coś jeszcze musimy zrobić w trakcie czytania pliku i wtedy musimy to dopisać tylko w 1 miejscu a nie w 3 (albo 50).

0

Zacznijmy od tego co zrobiłeś:

  • utworzyłeś klasę Zwierze,

  • utworzyłeś obiekt o nazwie zwierze (chociaż równie dobrze mógłbyś to nazwać kot, pies itd.),

  • wywołałeś metodę .glos() dla utworzonego obiektu,

  • nie zastosowałeś dziedziczenia.
    Co powinieneś zrobić:

  • utworzyć klasę Zwierze,

  • utworzyć inną klasę np. Kot, Pies, Zolw,

  • przy klasie np. Kot zastosować public class Kot extends Zwierze {...
    Co to daje i po co te dziedziczenie:

  • każde zwierze je prawie tak samo czyli w klasie Zwierze możesz utworzyć metodę typu jedz i w każdej kolejnej klasie typu Kot, Pies nie musisz się powtarzać,

  • zrobiłeś metodę void glos() więc niby jest ok, ale przecież każde zwierze ma inny głos więc powinieneś albo w klasie zwierze zrezygnować z tej metody albo w każdej dziedziczącej klasie ją nadpisać przez co zwierzęta, które maja nadpisaną wersję dadzą swój głos, a tam gdzie nie nadpiszesz będzie do "odglos"

  • acha i na koniec, słowo klucz extends oznacza, że rozszerzasz jakąś klasę o nowe elementy, czyli nowo utworzona klasa (trzymając się przykładów wyżej) Kot to klasa Zwierze + dodatkowe metody + dodatkowe składowe

0
n4lik napisał(a):
  • każde zwierze je prawie tak samo czyli w klasie Zwierze możesz utworzyć metodę typu jedz i w każdej kolejnej klasie typu Kot, Pies nie musisz się powtarzać,

Ale w klasie Kot, Pies, ... mogę użyć metody z klasy Zwierze bez używania słówka extends - tylko tego nie rozumiem, jak pisałem wcześniej rozumiem całe założenie dziedziczenia ale nie rozumiem po co w podklasie mam używać słówka extends skoro nie daje mi ono żadnej nowej funkcjonalności...

0

Ale w klasie Kot, Pies, ... mogę użyć metody z klasy Zwierze bez używania słówka extends

O RLY? Pics or it didn't happen.

0

po co w podklasie mam używać słówka extends skoro nie daje mi ono żadnej nowej funkcjonalności

Skoro tak uważasz, to nic nie rozumiesz z dziedziczenia.

class Zwierze{
     public void dajGlos(){
         System.out.println("");
     }
}
class Osmiornica extends Zwierze{
}
class Test{
     public static void main(String[] args)
     {
          Osmiornica o = new Osmiornica();
          o.dajGlos();
     } 
}

Spróbuj skompilować powyższe klasy po wyrzuceniu słowa extends.

0

Troche nie na temat... ale naprawde polecam odchodzić od używania polskich nazw zmiennych w kodzie.

0
bogdans napisał(a):

po co w podklasie mam używać słówka extends skoro nie daje mi ono żadnej nowej funkcjonalności

Skoro tak uważasz, to nic nie rozumiesz z dziedziczenia.

class Zwierze{
     public void dajGlos(){
         System.out.println("");
     }
}
class Osmiornica extends Zwierze{
}
class Test{
     public static void main(String[] args)
     {
          Osmiornica o = new Osmiornica();
          o.dajGlos();
     } 
}

Spróbuj skompilować powyższe klasy po wyrzuceniu słowa extends.

Ale jak zrobię obiekt klasy Zwierze w klasie Osmiornica to działa... czemu tak się dzieje?

0
rubesom napisał(a):

Ale jak zrobię obiekt klasy Zwierze w klasie Osmiornica to działa... czemu tak się dzieje?

Bo wtedy ośmiornica sama nie umie dać głosu, ale ma w środku siebie(ble...) inne zwierze, które może dać głos.

1

@rubesom to sie nazywa "delegacja" i generalnie jest bardzo fajną rzeczą. Ale wymaga żeby ten obiekt który tworzysz "wewnątrz" był kompletny. Tzn żeby faktycznie potrafił wykonywać pewną rzecz samodzielnie, a ty tylko dodajesz jakieś operacje przed i po tym wywołaniu. Ale nie zawsze tak jest. Czasem masz pewną wspólną ale niekompletną logikę i nie da sie jej po prostu wywołać.

0

@autor to teraz operując na swoim kodzie stwórz kontener homogeniczny o nazwie schronisko i wrzuć do niego zwięrzta: obiekt klasy testkot i np. dopisz sobie jeszcze w analogiczny sposób testpies i dodaj go do kontenera.

Zasadniczo dziedziczenie niesie ze sobą informację, że dany typ wywodzi się z pewnego typu bazowego przez co może być wykorzystywany przez publiczny interfejs (odziedziczony) w sposób podobny jak obiekty typu bazowego, może również wykorzystywać pewne mechanizmy wewnętrzne typu bazowego niedostępne bezpośrednio przez interfejs publiczny.
Daje Ci to spore możliwości np. traktowania obiektów klas pochodnych w sposób uogólniony - na poziomie klasy bazowej - tak jak w przypadku kontenera schronisko. Zasadniczo nie istotne jest jakie zwierzę tam umieścisz, liczy się to, że jest to zwierzę. Gdyby klasa testkot czy testpies nie dziedziczyły po klasie zwierze, wówczas nie byłoby wiadome jakie rodzaju (typu) one są. To, że nazwiesz klasę kot i skopiujesz w niej interfejs analogiczny jaki jest w klasie zwierze nie czyni kota zwierzęciem do czasu gdy jawnie nie poinformujesz, że kot ma być tym zwierzęciem.

2

mówisz że rozumiesz idee dziedziczenia a w ogóle nic nie rozumiesz
najpierw zrozum co możesz zrobić bez dziedziczenia bo to co robisz nie ma nic wspólnego z dziedziczeniem

może warto żebyś poczytał co to polimorfizm to będzie Ci łatwiej zrozumieć na przykładach
samo dziedziczenie bez polimorfizmu nie daje żadnych specjalnych plusów poza lepszą strukturą kodu i zmniejszeniem jego ilości

1

Oj, marudzenie. Jakby się nie dało normalnie.

public class Zwierze {
	public void glos() {
		System.out.println("Glos zwierza");
	}

	public void krzyk() {
		System.out.println("Krzyk zwierza");
	}
}

public class Kot extends Zwierze {
	public void glos() {
		System.out.println("Glos kota");
	}
}

public class Test {
	public static void main(String[] args) {
		Zwierze z = new Zwierze();
		z.glos();
		z.krzyk();
		Kot k = new Kot();
		k.glos();
		k.krzyk();
	}
}

A teraz słuchaj uważnie, co się dzieje w metodzie main:

  1. Tworzymy obiekt klasy Zwierze
  2. Wywołujemy metody glos() i krzyk(), efekty zapewne znasz - są to Glos zwierza i Krzyk zwierza
  3. Tworzymy obiekt klasy Kot, która rozszerza klasę Zwierze
  4. Wywołujemy metodę glos(), która wyświetli w konsoli tekst Glos kota.
  5. Wywołujemy metodę krzyk(), ale, ale! W klasie Kot nie ma metody krzyk()! Ale ponieważ Kot rozszerza Zwierze, jeśli klasa Kot nie ma metody krzyk(), wywołana zostanie metoda krzyk() klasy bazowej, czyli Zwierze. W konsoli wyświetli się więc Krzyk zwierza.

Pamiętaj, że dziedziczenie to relacja "jest" - kot i pies są ssakami, a ssaki i ptaki to zwierzęta, więc możesz stworzyć klasę Zwierze, którą będą rozszerzały klasy Ssak i Ptak, a z kolei klasę Ssak mogą rozszerzać klasy Kot i Pies. W klasie Zwierze umieścisz wszystkie cechy wspólne wszystkich zwierząt, w klasie Ssak te funkcje, które mają ssaki, a w Kot te, które mają koty. Ponieważ Kot nadal jest Ssakiem i Zwierzem, dalej będziesz mógł wywoływać w obiekcie klasy Kot te metody, które zostały zadeklarowane w klasie Zwierze.

0

Przepraszam, że odpowiadam dopiero teraz ale miałem natłok pracy.
Gwoli wyjaśnienia, nie wiedziałem o czymś takim jak mechanizm delegacji i z tego co widzę nie miałem wiedzy o tym jak powinny być wywoływane obiekty przy zachowaniu dziedziczenia.
Dziękuję wszystkim za cierpliwe wyjaśnienie wielu kwestii, mi najlepiej wyjaśniło wszystko to co napisał @Shalom stąd jego odpowiedź zaakceptuję co by potomni mogli z tego skorzystać.
Dzięki

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