Klasy dziedziczenie Programowanie w Javie - wykład 4 Treści prezentowane w wykładzie zostały oparte o: Barteczko, JAVA Programowanie praktyczne od podstaw, PWN, 2014 http://docs.oracle.com/javase/8/docs/ http://docs.oracle.com/javase/10/docs/ C. S. Horstmann, G. Cornell, Java. Podstawy, Helion, Gliwice 2013 Dorota Pylak C. S. Horstmann, Java, Techniki zaawansowane(wydanie10), Helion, Gliwice 2017
METODA tostring() - przykład 14.03.2019 2 //definicja klasy bazowej(nadklasy) public class Animal { private String name; //imie zwierzaka public Animal(){//konstruktor bezparametrowy public Animal(String name) { this.name = name; public String getname(){ return name; public void setname(string name){ this.name = name; @Override public String tostring(){ return "Animal: " + name;
METODA tostring() - przykład 14.03.2019 3 // definicja klasy pochodnej public class Cat extends Animal { private String owner;//wlasciciel kotka public Cat(){//konstruktor sam wykona super() public Cat(String name, String owner) { super(name); // wywołaj konstruktor nadklasy this.owner = owner; public String getowner(){ return owner; public void setowner(string owner){ this.owner = owner; @Override public String tostring(){ return super.tostring() + '\n' + "Owner: " + owner; //super.tostring()-wywołanie metody tostring z nadklasy
14.03.2019 4 DZIEDZICZENIE-referencyjna konwersja rozszerzająca Zauważmy, że obiekty klasy Cat są również obiektami klasy Animal (mają jej wszelkie właściwości). Zatem referencje do obiektów klasy Cat możemy przypisać zmiennym oznaczającym obiekty klasy Animal : Cat kitty = new Cat(...); Animal z = kitty; Nazywamy to referencyjną konwersją rozszerzającą (widening reference conversion). Konwersja jest rozszerzająca, bo przekształcamy typ pochodny(referencja do obiektu podklasy) do typu szerszego (referencja do obiektu nadklasy) Zdolność obiektów do stawania się obiektem swojej nadklasy jest użyteczna. PRZYKŁAD: Załóżmy, że mamy nadklasę NadKlasa, z której wyprowadziliśmy podklasy: PodKlasa1, PodKlasa2.
DZIEDZICZENIE konwersja rozszerzająca 14.03.2019 5 Załóżmy, że mamy zdefiniowaną metodę public typ metoda(nadklasa n1, NadKlasa n2){. Wówczas dzięki automatycznej konwersji referencyjnej możemy wywoływać ją dla różnych rodzajów par: NadKlasa nkl1 = new NadKlasa(...); NadKlasa nkl2 = new NadKlasa(...); PodKlasa1 p1 = new PodKlasa1(...); PodKlasa2 p2 = new PodKlasa2(...); typ zm; zm = metoda(nkl1,nkl2); zm = metoda(nkl1,p1); zm = metoda(p1,p2); Gdyby nie było obiektowych konwersji rozszerzających, to dla każdej możliwej kombinacji par musielibyśmy napisać inną wersję metody metoda().
DZIEDZICZENIE-referencyjna konwersja zwężająca Zauważmy, że w rozważanej powyżej metodzie public typ metoda(nadklasa n1, NadKlasa n2){ wobec parametrów n1, n2 możemy używać metod z NadKlasy, ale nie można używać metod z klas pochodnych (nawet, gdy n1, n2 wskazują na obiekty klas pochodnych) Referencyjna konwersja zwężająca polega na jawnym zastosowaniu operatora rzutowania do podklasy np..: void jakasmetoda(nadklasa n){ typ zm=((podklasa1)n).metodapodklasy1(); W przypadku, gdyby ktoś wywołał tę metodę dla obiektu podklasy PodKlasa2, zostałby wyrzucony wyjątek CastClassException i wykonanie programu zostałoby przerwane 14.03.2019 6
OPERATOR instanceof 14.03.2019 7 Wyrażenie ref instanceof T ma wartość true, jeśli referencja ref nie jest null i może być w fazie wykonania programu rzutowana do typu T bez zgłoszenia wyjątku CastClassException tzn. jeśli referencja ref jest typu T lub dowolnego podtypu T. Wówczas możemy napisać: void jakasmetoda (NadKlasa n){ typ zm; if(n instanceof PodKlasa1) zm = ((PodKlasa1)n).metodaPodKlasy1(); else if (n instanceof PodKlasa2) zm = ((PodKlasa2)n).metodaPodKlasy2();
14.03.2019 8 METODA getclass() Innym sposobem stwierdzania typu jest zastosowanie metody getclass(). Metoda ta zwraca faktyczny typ obiektu w postaci referencji do obiektu klasy Class. Obiekty tej klasy oznaczają klasy Cat k = new Cat( Mruczek ); Animal z = k; Class c = z.getclass(); //zmienna c będzie oznaczać klasę Cat Aby dowiedzieć się nazwy klasy -metoda getname() Class c = z.getclass(); String nazwa = c.getname(); Uwaga: Różnica pomiędzy instanceof, a getclass(): jeśli PodKlasa1 miałaby klasę pochodną PodPodKlasa1: PodPodKlasa1 kl = new PodPodKlasa1(...); to kl instanceof PodKlasa1 zwróci true (bo brane są pod uwagę podtypy), natomiast kl.getclass().getname() zwróci napis PodPodKlasa1
DZIEDZICZENIE -nadpisywanie metod w klasie pochodnej Metoda public String tostring(){ return "Animal: " + name; z nadklasy Animal nie jest zbyt przydatna w klasie Cat,bo informuje tylko o cechach dostępnych w nadklasie. Nadpiszmy ją zatem w klasie Cat: @Override public String tostring(){ return super.tostring() + '\n' + "Owner: " + owner; //super.tostring()-wywołanie metody tostring z nadklasy Ta metoda ma tę samą sygnaturę co metoda o tej samej nazwie w klasie bazowej i nadpisuje się nad tamtą metodę. Metoda nadpisująca metodę z klasy bazowej nie może mieć atrybutu dostępu bardziej restrykcyjnego niż metoda z klasy bazowej. 14.03.2019 9
TABLICE 13 Tablice są zestawami elementów (wartości) tego samego typu, ułożonych na określonych pozycjach. Do elementów tablicy odwołujemy się za pomocą nazwy tablicy oraz indeksu umieszczonego w nawiasach kwadratowych, np. tab[0], tab[1] W Javie tablice są obiektami, a nazwa tablicy jest nazwą zmiennej, będącej referencją do obiektu-tablicy. Obiekt-tablica zawiera elementy tego samego typu. Może to być dowolny z typów pierwotnych lub referencyjnych. Zatem, w szczególności elementami tablic mogą być referencje do innych tablic. Mamy wtedy do czynienia z odpowiednikiem tablic wielowymiarowych. Deklaracja tablicy składa się z: nazwy typu elementów tablicy, pewnej liczby par nawiasów kwadratowych (liczba par określa liczbę wymiarów tablicy), nazwy zmiennej, która identyfikuje tablicę.
TABLICE 14 Przykładowa deklaracja zmiennych tablicowych: String[] s; // jest deklaracją tablicy referencji do obiektów // klasy String, s jest referencją double[][] d; // jest deklaracją dwuwymiarowej tablicy liczb // rzeczywistych Taka deklaracja nie alokuje pamięci dla tablicy. Pamięć jest alokowana dynamicznie albo w wyniku inicjacji za pomocą nawiasów klamrowych albo w wyniku użycia wyrażenia new. Inicjacja tablicy za pomocą nawiasów klamrowych może wystąpić wyłącznie w wierszu deklaracji tablicy i ma postać: { element_1, element_2,... element_n
TABLICE - TWORZENIE 15 Np. int[] arr = { 1, 2, 7, 21 ; deklaruje tablicę o nazwie arr, tworzy ją i inicjuje jej elementy; kolejno: Wydzielana jest pamięć dla zmiennej arr, która będzie przechowywać referencję do obiektu-tablicy. Wydzielana jest pamięć (dynamicznie, na stercie) potrzebna do przechowania 4 liczb całkowitych (typu int), a następnie wartości 1,2,7,21 są zapisywane kolejno w tym obszarze pamięci. Adres tego obszaru (referencja) jest przypisywany zmiennej arr. Tworzenie tablicy za pomocą wyrażenia new ma postać new Typ[n]; gdzie: Typ - typ elementów tablicy n - rozmiar tablicy (liczba elementów tablicy) Np. int[] arr = new int[4];
TABLICE - INDEKSY 16 Rozmiar tablicy może być ustalony dynamicznie, w fazie wykonania programu. Np. int n; //... n uzyskuje wartość np. na skutek obliczeń //opartych na wprowadzonych przez użytkownika danych int[] tab = new int[n]; Ale po ustaleniu rozmiar nie może być zmieniony. Indeksy tablicy mogą być wyłącznie wartościami typu int. Mogą one być dowolnymi wyrażeniami, których wyliczenie daje wartość typu int. Ze względu na to, że wartości typu byte, char i short są w wyrażeniach "promowane" (przekształcane do typu int), to również wartości tych typów możemy używać przy indeksowaniu tablic. Niedopuszczalne natomiast jest użycie wartości typu long. Tablice zawsze indeksowane są poczynając od 0. Czyli pierwszy element n-elementowej tablicy ma indeks 0, a ostatni - indeks n-1.
TABLICE - INDEKSY 17 Odwołanie do i-go elementu tablicy o nazwie tab ma postać: tab[i] Ta konstrukcja składniowa traktowana jest jako zmienna, stanowi nazwę zmiennej - zatem możemy tej zmiennej przypisywać wartości innych wyrażeń oraz możemy używać jej wartości w innych wyrażeniach. Odwołania do elementów tablic są przez JVM sprawdzane w trakcie wykonania programu pod względem poprawności indeksów. Java nie dopuści do odwołania się do nieistniejącego elementu tablicy lub podania indeksu mniejszego od 0. Próba takiego odwołania spowoduje powstanie wyjątku ArrayIndexOutOfBoundsException, na skutek czego zostanie wyprowadzony odpowiedni komunikat i wykonanie programu zostanie przerwane (ew. taki wyjątek możemy obsłużyć). Informacje o rozmiarze (liczbie elementów) tablicy możemy uzyskać za pomocą odwołania: nazwa_tablicy.length W Javie length nie jest nazwą metody (lecz pola niejawnie stworzonej klasy, opisującej tablicę), dlatego NIE STAWIAMY po nim nawiasów okrągłych
TABLICE - PRZYPISANIA 18 Pętla przebiegająca przez wszystkie elementy tablicy tab: for (int i = 0; i < tab.length; i++)... tab[i]... ; Zmiennej tablicowej typu typa[] można przypisać wartość zmiennej tablicowej typu typb[] pod warunkiem, że dopuszczalne jest przypisanie wartości typb zmiennej typa. Każdej zmiennej tablicowej - jak każdej zmiennej zawierającej referencję - można przypisać wartość null Tak samo jak w przypadku innych obiektów - nie należy mylić przypisania zmiennych tablicowych (czyli referencji) z kopiowaniem zawartości tablic Na jedną tablicę może wskazywać kilka zmiennych tablicowych. Za pomocą każdej z nich (i operacji indeksowania) możemy zmieniać wartości elementów tej jednej tablicy, a odwołania do tej tablicy poprzez inne zmienne będą - oczywiście - uwzględniać te zmiany (np. pokazywać zmienione wartości).
TABLICE JAKO ARGUMENTY I WYNIKI METOD 19 Metody mogą działać na tablicach, do których referencje otrzymują w postaci parametrów i mogą zwracać wyniki - referencje do tablic. W nagłówku metod - parametry (które mają oznaczać tablice; są referencjami do tablic) deklarujemy za pomocą T[], gdzie T - nazwa typu elementów tablicy. Oczywiście, gdy wywołujemy metodę - to na liście argumentów podajemy nazwy zmiennych tablicowych (już bez nawiasów kwadratowych). Jeśli metoda zwraca wynik - referencję do tablicy, to typem wyniku jest również odpowiedni typ tablicowy T[]. Dla uproszczenia dalej będziemy mówić, że metoda otrzymuje jako argument - tablicę i zwraca jako wynik - tablicę. Jest to skrót myślowy: pamiętajmy, że zawsze chodzi o referencje do tablic Np. metoda o nazwie dblval, która zwraca referencję do nowo utworzonej tablicy liczb całkowitych, wartości elementów której są podwojonymi wartościami elementów tablicy liczb całkowitych, do której referencja przekazana została metodzie jako argument, może być zdefiniowana i użyta następująco
TABLICE JAKO ARGUMENTY I WYNIKI METOD public class Test { static int[] dblval(int[] tab) { int[] w = new int[tab.length];//tablica for (int i=0; i < w.length; i++) w[i] = tab[i]*2; return w; public static void main(string[] args) { int[] a = {1, 2, 3, 4 ; int[] wynik = dblval(a); //for (int i=0; i < wynik.length; i++) //System.out.print(" " + wynik[i]) for(int el : wynik) System.out.print(" " + el); 20
TABLICE OBIEKTÓW 21 Pamiętamy, że w Javie argumenty przekazywane są metodom przez wartość. Gdy argumentem jest zmienna tablicowa - przekazywana jest referencja do tablicy - i tej oczywiście w metodzie nie jesteśmy w stanie efektywnie zmienić. Nic jednak nie stoi na przeszkodzie, by zmienić elementy przekazanej tablicy. Elementami tablicy mogą być referencje do dowolnych obiektów, np. String[] miasta = {"Warszawa", "Poznań", "Kraków"; Stwórzmy tablicę obiektów np. klasy Para Para[] tabpar = new Para[10]; Samo stworzenie tablicy nie tworzy jednak obiektów, do których referencje są elementami tablicy. Zatem elementy tablicy na początku będą miały domyślne wartości null - a dopiero po stworzeniu obiektów i przypisaniu referencji (ich adresów) elementom tablicy będziemy mogli używać elementów tablicy w operacjach na obiektach.
TABLICE OBIEKTÓW 22 Np. public class TabPar { public static void main(string[] args) { Para[] tabpar = new Para[10]; for (int i=0; i < tabpar.length; i++) tabpar[i] = new Para(i+1, i+2); // for-each for (Para par : tabpar) System.out.println(par); Button tabb = new Button[10]; for (int i=0; i<tabb.length; i++) tabb[i] = new Button(); //lub równoważnie: //for (Button b : tabb) // b = new Button();
TABLICE OBIEKTÓW 23 W przypadku tablic referencji do obiektów możemy użyć inicjatorów klamrowych Para[] tabpara = {new Para(1,1),new Para(2,3), new Para(4,5) ; Button[] b = {new Button("A"),new Button("B") ; Przypomnijmy, że obiektowe konwersje rozszerzające pozwalają przypisywać zmiennym oznaczającym obiekty klasy bazowej referencje do obiektów klas pochodnych. Zatem elementy tablicy mogą zawierać referencje wskazujące na obiekty różnych klas, pod warunkiem, że klasy te mają tę samą klasę bazową- określającą ogólny, niejako wspólny dla wszystkich, typ elementów tablicy. Np. dla nadklasy Zwierze i podklasy Pies z poprzedniego wykładu: Zwierze[] tabz = { new Zwierze(..), new Pies(...), new Pies() ;
TABLICE OBIEKTÓW 24 Wygodną konstrukcją składniową Javy jest wyrażenie ad hoc tworzące i inicjujące tablicę referencji do obiektów: new klasaa[] { refb, refc,... Wyrażenie to tworzy tablicę typu klasaa[] i inicjuje ją referencjami podanymi w nawiasach klamrowych, przy czym każda z tych referencji może wskazywać obiekt klasy klasaa lub dowolnej klasy pochodnej od klasy klasaa. Wynikiem wyrażenia jest referencja do zmiennej tablicowej typu klasaa[]. Najczęściej wyrażenie to ma zastosowanie na liście argumentów wywołania metody, której parametrem jest tablica. W ten sposób możemy uzyskać efekt wywołania metody ze zmienną liczbą i (do pewnego stopnia) zmiennymi typami argumentów. Np., metodę xyz, której parametrem jest tablica zwierząt, możemy wywołać ze zmienną liczbą i typami zwierząt: xyz( new Zwierze[] {new Pies(...), new Pies(...), new Zwierze(...) ); xyz( new Zwierze[] {new Pies(...), new Pies(...) ); Pies p1 = new Pies(...),p2 = new Pies(...); xyz( new Zwierze[] { p1, p2 );
TABLICE WIELOWYMIAROWE 25 Tablice wielowymiarowe w Javie realizowane są jako tablice elementów, będących referencjami do tablic. Liczba wymiarów określana jest przez liczbę nawiasów [ ]. Przykładowe sposoby deklaracji i inicjacji: inicjalizacja w nawiasach klamrowych int[][] mac1 = { { 1, 2, 3, { 4, 5, 6, 7 ; dynamicznie int[][] mac2 = new int[n][m]; tablica składa się z wektorów o różnych rozmiarach, zadawanych przez tablicę w,np. dla int w[] = { 2, 3, 4 ; int n = 3; int[][] m3 = new int[n][]; //rozmiarami wierszy będą // zmienne dynamicznie for(int i = 0; i < m3.length; i++) { m3[i] = new int[w[i]]; Rozmiar 0-go wiersza tablicy m3 wynosi 2, 1-go wiersza 3, 2-go wiersza 4
TABLICE WIELOWYMIAROWE 26 public class MultiArr { public static void main(string[] arg) { int w[] = { 2, 3, 4 ; int n = 3; //rozmiary wierszy będą ustalane dynamicznie: int[][] m3 = new int[n][]; for(int i = 0; i < m3.length; i++) { m3[i] = new int[w[i]]; for (int j = 0; j < m3[i].length; j++) m3[i][j] = i + j; for (int i = 0; i < m3.length; i++) { System.out.println("Rozmiar " + i + "-go wiersza " + m3[i].length); String out = " "; for(int j = 0; j < m3[i].length; j++) out += " " + m3[i][j]; System.out.println(out);
varargs 27 Możemy również korzystać z konstrukcji zwanej "varargs" do przekazania dowolnej liczby wartości (parametrów aktualnych) do metody. Używamy "varargs", gdy nie wiemy jak wiele argumentów określonego typu będziemy przekazywać do metody. Jest to szybsze niż tworzenie tablic ręcznie. Aby użyć "varargs" należy po typie, a przed nazwą ostatniego parametru napisać trzy kropki " ". Taka metoda może być wywołana z dowolną liczbą parametrów, w tym bez żadnego parametru. public static void show(string... tab) { for (int i=0; i<tab.length; i++) System.out.println(tab[i]); Przykładowe wywołania funkcji show: show(); show("hello"); show("jak", "się", "masz"); show(new String[]{"A","B");
28 KLASA Arrays Klasa Arrays z pakietu java.util zawiera wiele użytecznych metod statycznych ułatwiających działania na tablicach wszelkich typów. Np. wypełnianie tablic pojedynczą wartością fill(tab, wartość) kopiowanie tablic copyof(tab, newlen) zwraca nową tablicę, przy czym jeżeli newlen==tab.length, mamy dokładną kopię tablicy, jeśli newlen>tab.length, to w nowej tablicy pojawią się na końcu dodatkowe elementy z wartościami zero ( 0, false lub null) jeśli newlen<tab.length, to zostanie skopiowane pierwsze newlen elem. tablicy tab kopiowania fragmentów tablic copyofrange(tab, fromindex, toindex) metoda tostring(tab), która wyprowadza napisową reprezentację tablicy