Przygotował: Jacek Sroka

1

Programowanie obiektowe

Wykład 4 – Java podstawy c.d.

(w tym pakiety, polimorfizm, modyfikatory, klasy abstrakcyjne, interfejsy)

Przygotował: Jacek Sroka

2

Kolokwium planujemy na 10 kwietnia w trakcie wykładu!!!

Przygotował: Jacek Sroka

Kapsułkowanie przypomnienie/zagadka ●

Czy w poniższym zawsze left == right/3 class Foo { public int left = 12; public int right = 4; public void setLeft(int l) { left = l; right = l/3; } // reszta skomplikowanego kodu }

3

Przygotował: Jacek Sroka

Zasięg deklaracji lokalnych Pracownik prac1 = new Pracownik("Jacek", 10000f); //tylko prac1 { Pracownik prac2 = new Pracownik("Placek", 8000f); //prac1 i prac2 //zmiennych lokalnych nie wolno przesłaniać //źle: Pracownik prac1 = new Pracownik("Jacek", 10000d);

} //tylko prac1

4

Przygotował: Jacek Sroka

5

Zagadka public class ScopeTest { int x; void test1() { x = x + 2; int x = 1; x = x++; System.out.println(x); } void test2(int x) { System.out.println(x); }

}

System.out.println(this.x);

System.out.println(this.x);

public static void main (String[] args) { ScopeTest st = new ScopeTest(); st.test1(); st.test2(1); }

Przygotował: Jacek Sroka

6

!!!

Przygotował: Jacek Sroka

Dziedziczenie i polimorfizm ●

Dziedziczenie class Osoba { String imię = "Jan"; String nazwisko; }

void idźNaImprezę() {}

class Student extends Osoba { boolean niewyspany = false; void idźNaImprezę() { niewyspany = true; super.idźNaImprezę(); } } ●

void idźNaWykład() { niewyspany = false; }

Polimorfizm Osoba o = new Student(); o.idźNaImprezę();

7

Przygotował: Jacek Sroka

Dziedziczenie, Interfejsy, Pakiety ●



Dziedziczenie to nowy dla OO sposób na ponowne wykorzystanie kodu –

nadklasy są zazwyczaj bardziej ogólne/abstrakcyjne



nie widzą nic o swoich podklasach



metody z nadklasy są automatycznie dostępne w podklasach, ale można je przesłonić (z dokładnością do modyfikatorów dostępu)

Nadklasy abstrakcyjne mogą nie definiować wszystkich metod –



Interfejsy nie definiują żadnych metod –



wtedy nie można tworzyć egzemplarzy

namiastka wielodziedziczenia

Klasy są grupowane w pakiety –

przestrzenie nazw



sposób dystrybucji

8

Przygotował: Jacek Sroka

9

Wielokrotne użycie kodu ●



Agregacja całkowita (ang. composition) – związek całość część –

Czas życia część ograniczony przez czas życia całości



Część należy tylko do jednej całości



Klasa tworzy i używa obiekty innych klas

Uogólnienie (ang. generalization) – związek między nadklasą i podklasą –

Podklasa jest szczególnym przypadkiem nadklasy



Podklasy można używać wszędzie tam gdzie nadklasy



Nie zależy od implementacji klas (to że w tej chwili są podobnie zaimplementowane nie ma znaczenia)



Dziedziczenie i przedefiniowywanie metod



Przykłady: Owoc-Gruszka, Ptak-Wrona, Figura-Kwadrat

Okienko

Ramka

Okienko

Dialog

Przygotował: Jacek Sroka

10

Polimorfizm ●









Sprzeczne tendencje w programowaniu –

Oprogramowanie ma być zamknięte: żeby inni nie zepsuli tego co już działa



Oprogramowanie ma być otwarte: żeby mogło się rozwijać jak się sprawdzi

Możemy rozszerzać program/zrąb dodając nowe klasy i korzystając z zasady podstawialności Zachowujemy kontrolę typów przez kompilator Kod rozpoznający metody, których klas wykonać dostajemy za darmo i nie trzeba go uaktualniać Przykład Figura.rysuj(), Object.toString()

Przygotował: Jacek Sroka

Przykład toString() i super class Osoba { private String imię, nazwisko; // ... @Override public String toString() { return "imię = " + imię + ", nazwisko = " + nazwisko; } } class Student extends Osoba { //String, aby numer mógł zawierać nie tylko cyfry (np. I-123456) private String nrIndeksu; //...

}

@Override public String toString() { return super.toString() + ", nr indeksu = " + nrIndeksu; }

11

Przygotował: Jacek Sroka

12

super w konstruktorach class Osoba { public Osoba(String imię, String nazwisko){ this.imię = imię; this.nazwisko = nazwisko; } } class Student extends Osoba { public Student(String imię, String nazwisko, String nrIndeksu){ super(imię, nazwisko); this.nrIndeksu = nrIndeksu; } } class StudStypend extends Student { private int stypendium; public StudStypend(String imię, String nazwisko, String nrIndeksu, int stypedium){ super(imię, nazwisko, nrIndeksu); this.stypendium = stypedium; }

}

@Override public String toString(){ return super.toString() + ", stypendium = " + stypendium; }

Przygotował: Jacek Sroka

13

instanceof ●



Dynamiczne sprawdzanie typów jest niewygodne, bo błędy pojawiają się w trakcie uruchomienia, a nie kompilacji, ale można się trochę zabezpieczyć operator instanceof if (o instanceof Student) { Student s = (Student) o; s.idźNaWykład(); //lub ((Student) o).idźNaWykład(); }



Dla każdej referencji do obiektu spełnione jest ??? instanceof Object



null nie jest egzemplarzem żadnej klasy (null instanceof Object) == false



Tablice też są obiektami (new int[5] instanceof Object) == true



Przemieszanie hierarchii klas da się wykryć w czasie kompilacji class Cat {} class Dog {} Dog d = new Dog(); System.out.println(d instanceof Cat);

Przygotował: Jacek Sroka

14

Wzorce projektowe ●











Pewne wypróbowane i poprawne rozwiązania problemów projektowych mogą być (i były) wyrażane jako dobre praktyki, heurystyki lub wzorce – nazwane przepisy zadanie-rozwiązanie. (na podstawie "Applying UML And Patterns" Craiga Larmana). Sam termin „wzorzec” ma sugerować coś powtarzalnego. To nie są nowe pomysły! Dwa istotne zbiory wzorców to wzorce GoF i GRASP. GRASP to metodyczne podejście do uczenia się podstaw projektowania obiektowego. GRASP: Wysoka spójność, Luźne sprzężenie, Polimorfizm, Rzeczoznawca, Twórca, Zarządca, Pośrednictwo, Czysty wymysł, Kapsułkowanie zmienności GoF (23 ogólnie uznane wzorce): Fabryka, Singleton, Adapter, Strategia, Kompozyt, Fasada, Obserwator (Delegowanie, Wydawca-Prenumerator)

Przygotował: Jacek Sroka

15

Adapter ●



Kontekst/Problem: Jak poradzić sobie z niekompatybilnymi interfejsami lub dostarczyć stabilny interfejs dla podobnych komponentów posiadających różniące się interfejsy? Rozwiązanie: Używając pośredniego obiektu adaptera przekształć oryginalny interfejs komponentu na inny.

Dodajemy poziom pośredni składający się z obiektów, które przystosowują różnorodne zewnętrzne interfejsy do spójnego interfejsu używanego w aplikacji! Konwencja nazewnicza: nazwę wzorca wbudowujemy w nazwę typu Adaptery są Fasadami (opakowują dostęp do podsystemu lub systemu w pojedynczym obiekcie).

Przygotował: Jacek Sroka

16

Fabryka Kto powinien tworzyć obiekty, np. obiekty (adaptery) reprezentujące zewnętrzne usługi (mogące mieć różne interfejsy, np. AdapterKartyVisa). Chcemy utrzymać rozdzielenie zagadnień – obiekty dziedzinowe odpowiadają jedynie za logikę aplikacji (chcemy zachować wysoką spójność). (Nie zawsze jest to możliwe – programowanie aspektowe).





public NaszaFabryka { ... public AdapterKarty getAdapterKarty() { //odczytaj z pliku konfiguracyjnego jakiego adaptera użyć

}

}

//utwórz odpowiedni i zwróć go

Przygotował: Jacek Sroka

17

Singleton ● ● ● ●

Jak uzyskać dostęp do fabryki z poprzedniego przykładu? Kto ją tworzy? Zazwyczaj potrzeba tylko jednej fabryki? Możemy przekazywać fabrykę jako parametr, ale możemy użyć wzorca Singleton

public static synchronized NaszaFabryka getEgzemplarz() { // sekcja krytyczna, jeżeli aplikacja jest wielowątkowa if ( egzemplarz == null ) { egzemplarz = new FabrykaUsług(); } return egzemplarz; } //czyli fabryka staje się dostępna globalnie! ●

egzemplarz jest atrybutem statycznym, a konstruktor fabryki jest prywatny

Przygotował: Jacek Sroka

18

Przygotował: Jacek Sroka

19

Pakiety przykład package mój.pierwszy.pakiet; import java.util.Date; public class MojeUtils { public static void main(String[] args) { System.out.println("Witaj Świecie!"); } }

javac ./mój/pierwszy/pakiet/MojeUtils.java java mój.pierwszy.pakiet.MojeUtils

Przygotował: Jacek Sroka

20

Pakiety ●

Pomiędzy deklaracją pakietu i deklaracjami klas i interfejsów możemy wskazać jakich klas i interfejsów chcemy używać bez kwalifikowania ich nazw nazwą pakietu: import pak1.pak2.Klasa; import pak1.Interfejs; import pak1.*; //nie obejmuje podpakietów //java.lang.* jest importowane domyślnie







JVM ładuje potrzebne klasy w chwili pierwszego odwołania, a odnajduje je dzięki wartości CLASSPATH (zmienna środowiska lub parametr wywołania) –

java -cp … HelloWorld



Jeżeli CLASSPATH=/java:. to poszukiwany będzie plik /java/pak1/pak2/Klasa.class



jeżeli taki nie istniej to ./pak1/pak2/Klasa.class

W CLASSPATH można też wskazać archiwum (jar lub zip) Nie znalezienie importowanej klasy/interfejsu powoduje błąd podczas kompilacji

Przygotował: Jacek Sroka

21

Przygotował: Jacek Sroka

22

Pakiety c.d. Jeżeli pakiety pak1 i pak2 zawierają klasę A to:







import pak1.A; import pak2.A; spowoduje błąd podczas kompilacji import pak1.*; import pak2.A; będzie poprawne i używana będzie A z pak2, chyba że A jest również definiowana w aktualnym pakiecie import pak1.*; import pak2.*; będzie poprawne, o ile nigdzie nie próbujemy odwoływać się do A lub definiujemy tą klasy w aktualnym pakiecie

Przygotował: Jacek Sroka

Import składowych statycznych ●

Przykład dla stałych (można też metody) double r = Math.cos(Math.PI * theta); import static java.lang.Math.PI; import static java.lang.Math.*;//też mogą wystąpić konflikty nazw //np. MAX_VALUE z Integer i Long double r = cos(PI * theta);



Przydaje się z java.util.Arrays i java.util.Collections



Stosować oszczędnie żeby nie zaśmiecić przestrzeni nazw –

jeszcze gorzej Constant Interface Antipattern, bo zaśmieca publiczne API

23

Przygotował: Jacek Sroka

Niektóre standardowe pakiety ●



java.lang (Object, String, Integer, ..., System, Math, Throwable, Thread) java.io (File, Reader, Writer, InputStream, OutputStream)



java.net (Socket, ServerSocket, URL)



java.util (Collection, List, Set, Map, Iterator)



javax.swing



...

24

Przygotował: Jacek Sroka

25

Modyfikatory dostępu ●





Dla atrybutów, metod, konstruktorów, klas i interfejsów –

public (dostęp publiczny) – bez ograniczeń



protected (dostęp chroniony) – z pakietu i podklas (nawet w innym pakiecie)



brak modyfikatora (dostęp domyślny) – umożliwia dostęp z pakietu



private (dostęp prywatny) – z klasy zadeklarowanej na najwyższym poziomie struktury programu, w której jest zawarta (nie koniecznie bezpośrednio) deklaracja opatrzona tym modyfikatorem dostępu

Dostęp do klasy pozwala: –

tworzyć egzemplarze



rozszerzać



odwoływać się do atrybutów i metod, chyba że ich modyfikatory ograniczają

Typ tablicowy dostępny jest wtedy i tylko wtedy, gdy dostępny jest typ elementów tablicy

Przygotował: Jacek Sroka

26

Modyfikatory przykład ●

Import się nie uda, bo klasa jest niewidoczna package po.egzamin.czerwiec; class Zadanie {} package po.egzamin.wrzesień; import po.egzamin.czerwiec.Zadanie class NoweZadanie extends Zadanie {}



A tu już tak package po.egzamin.czerwiec; public class Zadanie {} package po.egzamin.wrzesień; import po.egzamin.czerwiec.Zadanie class NoweZadanie extends Zadanie {}



Jak oznaczymy final, to public nie pomoże (ale się zaimportuje)

Przygotował: Jacek Sroka

27

Klasy abstrakcyjne ●

Klasy z niekompletną implementacją



Nie można tworzyć ich egzemplarzy



Przydają się jak chcemy wprowadzić wspólny kod do hierarchii

abstract class PartiaPolityczna { void dzielIZwyciężaj() { //... generujTematZastępczy(); } }

abstract String generujTematZastępczy();

class XYZ extends PartiaPolityczna { String generujTematZastępczy() { //... } }

Przygotował: Jacek Sroka

Klasy abstrakcyjne przykład abstract class Pojemnik { public abstract void dodaj(int elt); public void dodajTab(int[] tab){ for(int i: tab) dodaj(i); }

}

public abstract String toString(); //mimo że toString() było zdefiniowane w Object

28

Przygotował: Jacek Sroka

public class PojemnikTablicowy extends Pojemnik { private int[] tab; // Niezm.: dane są w tab[0..ile-1] private int ile=0; PojemnikTablicowy(){ tab = new int[1]; // 1024 byłoby bardziej naturalne } @Override public void dodaj(int elt){ if (ile >= tab.length){ int[] pom = new int[tab.length*2]; for (int i=0; i