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