Programowanie obiektowe (Java) – 6
1
Dziedziczenie
Dziedziczenie (ang. inheritance) [7] - ”Związek pomiędzy klasami obiektów określający przekazywanie cech
(definicji atrybutów, metod, itd.) z nadklasy do jej podklas np. obiekt klasy Pracownik dziedziczy wszystkie
własności (definicje atrybutów, metody) określone w ramach klasy Osoba. Dziedziczenie jest podstawowym
mechanizmem sprzyjającym ponownemu użyciu.”
W Javie do oznaczania dziedziczenia klas wykorzystywane jest słowo kluczowe extends.
1
package
pl.kielce.tu.lab6;
2
3
class
Urzadzenie {
4
void
wlacz() {
5
System.out.println(
"Wyłączam urządzenie"
);
6
}
7
8
void
wylacz() {
9
System.out.println(
"Wyłączam urządzenie"
);
10
}
11
}
12
13
class
Telefon
extends
Urzadzenie {
14
void
dzwon() {
15
System.out.println(
"Dzwonię"
);
16
}
17
}
18
19
public class
TestInheritance {
20
public static void
main(String[] args) {
21
Urzadzenie u =
new
Urzadzenie();
22
u.wlacz();
23
u.wylacz();
24
Telefon k =
new
Telefon();
25
k.wlacz();
26
k.dzwon();
27
k.wylacz();
28
}
29
}
Przykład 1: src/pl/kielce/tu/lab6/TestInheritance.java {link}
1.1
Dziedziczenie z wykorzystaniem klas wewnętrznych
W Javie każda klasa można dziedziczyć tylko po jednej klasie bazowej, nie ma wielodziedziczenia klas
(dziedziczenia wielobazowego). Poniższy kod jest niepoprawny.
1
class
X{}
2
class
Y{}
3
public class
Z
extends
X, Y {}
//ERROR
Jeżeli klasa zawiera klasy wewnętrzne to każda z nich także może dziedziczyć po jednej klasie.
1
class
X{}
2
class
Y{}
3
public class
Z
extends
X {
4
class
ZY
extends
Y{}
5
}
1
W połączeniu z możliwością dostępu przez klasy wewnętrzne do części prywatnej klasy zewnętrznej umoż-
liwia to uzyskanie zachowania częściowo zbliżonego do wielodziedziczenia klas:
1
package
pl.kielce.tu.lab6;
2
3
class
Ship {
4
private
String name;
5
6
Ship(String name) {
7
this
.name = name;
8
}
9
10
void
sail() {
11
System.out.println(
"The "
+ name +
" is sailing"
);
12
}
13
}
14
15
class
Car {
16
private
String name;
17
18
Car(String name) {
19
this
.name = name;
20
}
21
22
void
drive() {
23
System.out.println(
"The "
+ name +
" is driving"
);
24
}
25
}
26
27
class
Amphibian
extends
Ship {
28
private
AmphibianCar car;
29
30
Amphibian(String name) {
31
super
(name);
32
car =
new
AmphibianCar(name);
33
}
34
35
Ship transformIntoShip() {
36
return this
;
37
}
38
39
Car transformIntoCar() {
40
return
car;
41
}
42
43
public void
move(
boolean
water) {
44
if
(water) {
45
Ship s = transformIntoShip();
46
s.sail();
47
}
else
{
48
Car c = transformIntoCar();
49
c.drive();
50
}
51
}
52
53
private class
AmphibianCar
extends
Car {
54
AmphibianCar(String name) {
55
super
(name);
56
}
57
}
58
}
2
59
60
public class
TestAmphibian {
61
public static void
main(String[] args) {
62
Amphibian a =
new
Amphibian(
"amphibian"
);
63
boolean
water =
true
;
64
a.move(water);
65
water =
false
;
66
a.move(water);
67
68
Car c = a.transformIntoCar();
69
c.drive();
70
71
Ship s = a.transformIntoShip();
72
s.sail();
73
}
74
}
Przykład 2: src/pl/kielce/tu/lab6/TestAmphibian.java {link}
1.2
Klasa java.lang.Object
Jeżeli dla klasy nie zostanie jawnie podana klasa, po której ma ona dziedziczyć, to jako klasa bazowa
zostanie użyta klasa java.lang.Object. Przykładowa klas X:
1
class
X{}
Po deasemblacji klasy X:
1
Compiled from
"X.java"
2
class
X
extends
java.lang.Object{
3
X();
4
}
Metody zdefiniowane w klasie java.lang.Object:
•
public
Object()
– konstruktor
•
protected
Object clone()
throws
CloneNotSupportedException
– zwraca kopie obiektu, metoda wykorzystywana podczas klonowania,
•
public boolean
equals(Object obj)
– służy do porównywania obiektów, nie chodzi tu o porównanie referencji (do tego służy ”==”) lecz
sprawdzenie równość (równoważności) obiektów na podstawie ich wartości,
Zasady tworzenia własnej metody equals(Object obj)
:
–
zwrotność (ang. reflexive): dla każdej referencji x różnej od null, wywołanie x.equals(x) powinno
zwrócić true,
–
symetryczność (ang. symmetric): dla wszystkich referencji x oraz y różnych od null, wywołanie
x.equals
(y) powinno zwrócić true wtedy i tylko wtedy jeżeli y.equals(x) też zwróci true,
–
przechodniość (ang. transitive): dla wszystkich referencji x, y oraz z różnych od null, jeżeli
x.equals
(y) zwraca true oraz y.equals(z) zwraca true, to x.equals(z) powinno też zwracać true,
1
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)
3
–
spójność (ang. consistent): dla wszystkich referencji x, y oraz z różnych od null, wielokrotne
wywołanie x.equals(y) powinno zawsze zwracać true lub zawsze zwracać f alse, pod warunkiem,
że informacje wykorzystane do porównywania nie uległy w międzyczasie zmianie,
–
dla każdej referencji x różnej od null, wywołanie x.equals(null) powinno zwrócić f alse.
Zazwyczaj podczas przesłaniania metody equals() należy przesłonić metodę hashCode(). Związane to
jest z zachowaniem następującej zasady: jeżeli dwa obiekty są sobie równe (x.equals(y) zwraca true) to
mają także równe kody mieszające / haszujące (x.hashCode() == y.hashCode()).
Metoda equals(Object obj) posiada jeden parametr Objectobj. Oznacza to, że jeżeli w klasie Student
została przesłonięta metoda equals(Object obj) to porównywane będą dwa obiekty this (klasa Student)
oraz obj (klasa Object). W większości przypadków przed wykonaniem porównania konieczne będzie
wykonanie rzutowania (pkt. 2 oraz kolejne).
•
protected void
finalize()
throws
Throwable
– metoda wywoływana w trakcie odśmiecania, służy do finalizacji obiektu, czyli wykonania dodatkowych
czynności gdy obiekt jest usuwany,
•
public final
Class<?> getClass()
– zwraca klasę, do której należy obiekt,
1
public class
HelloWorld {
2
public static void
main(String[] args) {
3
HelloWorld h =
new
HelloWorld();
4
System.out.println(h.getClass() +
" has super "
+ h.getClass().getSuperclass());
5
Object o =
new
Object();
6
System.out.println(o.getClass() +
" has super "
+ o.getClass().getSuperclass());
7
}
8
}
•
public int
hashCode()
– zazwyczaj jej domyślna implementacja konwertuje wewnętrzny adres obiektu na int,
Zasady tworzenia metody hashCode()
:
–
jeżeli metoda ta wywoływana jest kilkukrotnie podczas działania aplikacji to musi zwracać ten sam
wynik, pod warunkiem że informacje wykorzystywane w metodzie equals() nie uległy zmianie, nie
jest konieczne, aby metoda zwracała taki sam wynik pomiędzy dwoma uruchomieniami aplikacji,
–
jeżeli dwa obiekty są równe zgodnie z metodą equals(), to wywołanie dla nich metody hashCode()
musi zwracać ten sam wynik,
–
jeżeli dwa obiekty nie są równe zgodnie z metodą equals(), to wywołanie dla nich metody
hashCode
() nie musi zwracać różnego wyniku.
Metoda System.identityHashCode(java.lang.Object obj) umożliwia pobranie wyniku dla domyślnej
metody hashCode(), niezależnie od tego czy metoda ta została przesłonięta czy nie.
•
public final void
notify()
public final void
notifyAll()
– metody służące do budzenia wątku lub wątków czekających na monitorze obiektu,
•
public
String toString()
2
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()
4
– zwraca tekstową reprezentację obiektu, domyślnie jest to:
getClass().getName() +
'@'
+ Integer.toHexString(hashCode())
Zaleca się przesłonięcie tej metody we wszystkich klasach pochodnych. Metoda ta w połączeniu z kon-
struktorem z jednym parametrem typu tekstowego umożliwia stworzenie mechanizmu konwersji obiektu
na tekst oraz w odwrotną stronę. Przykład:
1
class
Student{
2
private
String imie, nazwisko, ulica;
3
int
numer;
4
5
Student(String imie, String nazwisko, String ulica,
int
numer){
6
this
.imie = imie;
7
this
.nazwisko = nazwisko;
8
this
.ulica = ulica;
9
this
.numer = numer;
10
}
11
12
Student(String text){
13
String [] tmp = text.split(
"#"
);
14
this
.imie = tmp[0];
15
this
.nazwisko = tmp[1];
16
this
.ulica = tmp[2];
17
this
.numer = Integer.valueOf(tmp[3]);
18
}
19
20
@Override
21
public
String toString(){
22
return
imie +
"#"
+ nazwisko +
"#"
+ ulica +
"#"
+ numer;
23
}
24
}
1
public class
TestStudent{
2
public static void
main(String [] args){
3
Student student =
new
Student(
"Jan"
,
"Kowalski"
,
"Al. 1000-lecia P. P."
, 7);
4
String text = student.toString();
5
Student student2 =
new
Student(text);
6
}
7
}
•
public final void
wait()
throws
InterruptedException
public final void
wait(
long
timeout)
throws
InterruptedException
public final void
wait(
long
timeout,
int
nanos)
throws
InterruptedException
– metody umożliwiające oczekiwanie aż wątek nie zostanie obudzony w wyniku wywołania metody
notify() lub notifyAll().
1.3
Final
Final[2] – „ogólnie oznacza ’coś co nie może być zmienione’. Możemy bowiem chcieć uniemożliwić dokony-
wanie zmian z dwóch względów: projektowania lub wydajności. Ponieważ oba powody różnią się zasadniczo,
słowo final może być stosowane niewłaściwie.”
„Dla typu elementarnego to wartość staje się stała, lecz dla referencji do obiektu to referencja zostaje
stała.”
Metody finalne [2] – „Po pierwsze jest to niejako założenie blokady na metodę, aby zabronić klasom
pochodnym jakichkolwiek zmian – jest to podyktowane wymogami projektowymi, kiedy chcemy mieć pewność,
5
że zachowanie metody zostanie niezmienione i nie będzie mogło być przesłonięte. Drugi powód stosowania
metod finalnych jest związany z kwestią wydajności. (. . . )”
Klasy finalne [2] – „(. . . ) nie chcemy z niej dziedziczyć i również nie pozwalamy na to komukolwiek innemu.
Innymi słowy, z jakichś powodów projekt klasy zakłada, że nigdy nie będzie potrzeby dokonywania żadnych
zmian lub też ze względów bezpieczeństwa nie chcemy dalej dziedziczyć.”
1.3.1
Finalne atrybuty
Atrybuty finalne statyczne muszą zostać zainicjalizowane w momencie deklarowania (linia 8 w przykładach
3 i 4) lub w bloku inicjalizacji egzemplarza (linia 14 w przykładach 3 i 4).
1
public class
Final1 {
2
3
class
MyFinal {
4
}
5
6
final static
MyFinal f1 =
new
MyFinal();
7
final static
MyFinal f2;
8
final static
MyFinal f3;
// error: The blank field f3 might not have been initialized
9
final static
MyFinal f4;
// error: The blank field f4 might not have been initialized
10
11
static
{
12
f2 =
new
MyFinal();
13
}
14
15
public static void
main(String[] args) {
16
f3 =
new
MyFinal();
// error: The final field f3 can not be assigned
17
}
18
}
Przykład 3: src/pl/kielce/tu/lab6/finals/Final1.java {link}
1
public class
Final2 {
2
3
class
MyFinal {
4
}
5
6
final static int
f1 = 1;
7
final static int
f2;
8
final static int
f3;
// error: The blank field f3 might not have been initialized
9
final static int
f4;
// error: The blank field f4 might not have been initialized
10
11
static
{
12
f2 = 1;
13
}
14
15
public static void
main(String[] args) {
16
f3 = 2;
// error: The final field f3 can not be assigned
17
}
18
}
Przykład 4: src/pl/kielce/tu/lab6/finals/Final2.java {link}
Atrybuty finalne niestatyczne muszą zostać zainicjalizowane w momencie deklarowania (linia 8 w przy-
kładach 5 i 6), w bloku inicjalizacji egzemplarza (linia 14 w przykładach 5 i 6) lub w konstruktorze (linia 18
w przykładach 5 i 6).
1
public class
Final3 {
2
3
class
MyFinal {
4
}
6
5
6
final
MyFinal f1 =
new
MyFinal();
7
final
MyFinal f2;
8
final
MyFinal f3;
9
final
MyFinal f4;
// error: The blank field f4 might not have been initialized
10
11
{
12
f2 =
new
MyFinal();
13
}
14
15
Final3() {
16
f3 =
new
MyFinal();
17
}
18
}
Przykład 5: src/pl/kielce/tu/lab6/finals/Final3.java {link}
1
public class
Final4 {
2
3
class
MyFinal {
4
}
5
6
final int
f1 = 1;
7
final int
f2;
8
final int
f3;
9
final int
f4;
// error: The blank field f4 might not have been initialized
10
11
{
12
f2 = 2;
13
}
14
15
Final4() {
16
f3 = 3;
17
}
18
}
Przykład 6: src/pl/kielce/tu/lab6/finals/Final4.java {link}
1.3.2
Finalne zmienne lokalne
Zmienne lokalne, które zostały oznaczone jako finalne muszą zostać zainacjalizowane w momencie dekla-
racji i od tego momentu nie można pod nie przypisywać nowej wartości.
1.3.3
Finalne parametry metod
Pod parametry metod oznaczone jako finalne nie może być przypisane nowa wartość.
1
public class
Final5 {
2
3
static void
test1(
int
i) {
4
i++;
5
System.out.println(
"test1(): "
+ i);
6
}
7
8
static void
test2(
final int
i) {
9
i++;
// error: The final local variable can not be assigned.
10
System.out.println(
"test1(): "
+ i);
11
}
12
13
public static void
main(String[] args) {
14
int
j = 1;
7
15
System.out.println(
"main(): "
+ j);
16
test1(j);
17
System.out.println(
"main(): "
+ j);
18
System.out.println();
19
20
System.out.println(
"main(): "
+ j);
21
test2(j);
22
System.out.println(
"main(): "
+ j);
23
System.out.println();
24
}
25
}
Przykład 7: src/pl/kielce/tu/lab6/finals/Final5.java {link}
1
public class
Final6 {
2
3
static void
test1(Final6 f) {
4
f =
new
Final6();
5
System.out.println(
"test1(): "
+ f);
6
}
7
8
static void
test2(
final
Final6 f) {
9
f =
new
Final6();
// error: The final local variable can not be assigned.
10
System.out.println(
"test1(): "
+ f);
11
}
12
13
public static void
main(String[] args) {
14
Final6 f =
new
Final6();
15
System.out.println(
"main(): "
+ f);
16
test1(f);
17
System.out.println(
"main(): "
+ f);
18
System.out.println();
19
20
System.out.println(
"main(): "
+ f);
21
test2(f);
22
System.out.println(
"main(): "
+ f);
23
System.out.println();
24
}
25
}
Przykład 8: src/pl/kielce/tu/lab6/finals/Final6.java {link}
1.3.4
Finalne metody
1
class
MyFinalMethod {
2
final public void
method() {
3
}
4
}
5
6
class
MyFinalMethod2
extends
MyFinalMethod {
7
// error: method() in MyFinal2 cannot override method() in MyFinal
8
public void
method() {
9
}
10
}
Przykład 9: src/pl/kielce/tu/lab6/finals/Final7.java {link}
1.3.5
Finalne klasy
8
1
final class
MyFinalClass {
2
}
3
4
// error: cannot inherit from final MyFinal
5
class
MyFinalClass2
extends
MyFinalClass {
6
}
Przykład 10: src/pl/kielce/tu/lab6/finals/Final8.java {link}
2
Rzutowanie
Kast (ang. cast) [7] – „Termin C, C++ i innych interfejsów oznaczający operator konwersji typu. Zwykle
przyjmuje postać nazwy typu w nawiasach okrągłych, stawianej przed wielkością podlegającą konwersji typu.”
Kast w dół (ang. downcast) - „Konwersja typu na typ stojący niżej w hierarchii (na podtyp).” Kast w górę
(ang. upcast) - „Konwersja typu na typ stojący wyżej w hierarchii (na nadtyp).”
1
package
pl.kielce.tu.lab6;
2
3
class
Pojazd {}
4
5
class
Samochod
extends
Pojazd {}
6
7
class
Motocykl
extends
Pojazd {}
8
9
public class
TestCast {
10
public static void
main(String[] args) {
11
Pojazd[] p =
new
Pojazd[2];
12
// poniżej rzutowanie w górę
13
p[0] =
new
Samochod();
14
p[1] =
new
Motocykl();
15
// poniżej poprawne rzutowanie w dół
16
Samochod s = (Samochod) p[0];
17
Motocykl m = (Motocykl) p[1];
18
// poniżej niepoprawne rzutowanie w dół
19
// java.lang.ClassCastException
20
Motocykl m2 = (Motocykl) p[0];
21
// java.lang.ClassCastException
22
Samochod s2 = (Samochod) p[1];
23
}
24
}
Przykład 11: src/pl/kielce/tu/lab6/TestCast.java {link}
2.1
Operator instanceOf
Operator instanceof zwraca prawdę jeśli obiekt można rzutować na podany typ.
1
public class
TestInstanceOf {
2
3
static
Random r =
new
Random();
4
5
public static
Object makeNew() {
6
switch
(r.nextInt() % 2) {
7
case
0:
8
return new
String(
"TEST"
);
9
default
:
10
return new
Integer(123);
11
}
9
12
}
13
14
public static void
main(String[] args)
throws
Exception {
15
for
(
int
j = 0; j < 10; j++) {
16
Object o = makeNew();
17
if
(o
instanceof
String) {
18
String s = (String) o;
19
System.out.println(
"String == "
+ o.getClass().getSimpleName() +
" "
+ ←֓
s.toLowerCase());
20
}
else if
(o
instanceof
Integer) {
21
Integer i = (Integer) o;
22
System.out.println(
"Integer == "
+ o.getClass().getSimpleName() +
" "
+ ←֓
i.floatValue());
23
}
else
{
24
throw new
RuntimeException(
"Object of unknown class"
);
25
}
26
}
27
}
28
}
Przykład 12: src/pl/kielce/tu/lab6/TestInstanceOf.java {link}
2.2
Metoda isInstance
()
„Metoda isInstance() klasy Class pozwala na dynamiczne testowanie typu obiektu.”
1
public class
TestIsInstance {
2
3
static
Random r =
new
Random();
4
5
public static
Object makeNew() {
6
switch
(r.nextInt() % 2) {
7
case
0:
8
return new
String(
"TEST"
);
9
default
:
10
return new
Integer(123);
11
}
12
}
13
14
public static void
main(String[] args)
throws
Exception {
15
for
(
int
j = 0; j < 10; j++) {
16
Object o = makeNew();
17
if
(String.
class
.isInstance(o)) {
18
String s = (String) o;
19
System.out.println(
"String == "
+ o.getClass().getSimpleName() +
" "
+ ←֓
s.toLowerCase());
20
}
else if
(Integer.
class
.isInstance(o)) {
21
Integer i = (Integer) o;
22
System.out.println(
"Integer == "
+ o.getClass().getSimpleName() +
" "
+ ←֓
i.floatValue());
23
}
else
{
24
throw new
RuntimeException(
"Object of unknown class"
);
25
}
26
}
27
}
28
}
Przykład 13: src/pl/kielce/tu/lab6/TestIsInstance.java {link}
10
3
Polimorfizm
Polimorfizm (ang. polymorphism) [7] – „Termin używany w dwóch nieco różnych znaczeniach (które są
często mylone):
• (1) w terminologii obiektowej: możliwość istnienia wielu metod o tej samej nazwie, powiązana z możli-
wością wyboru konkretnej metody podczas czasu wykonania (dynamicznym wiązaniem);
• (2) w terminologii teorii typów i języków polimorficznych (np. w ML): umożliwienie definiowania funkcji
lub procedur, których argumenty i wynik mogą posiadać jednocześnie wiele typów;”
Statyczne wiązanie (ang. static binding) [7] – „Wiązanie nazw występujących w programie, które ma
miejsce podczas kompilacji.”
Wiązanie dynamiczne (ang. dynamic binding) [7] – „Wiązanie nazw występujących w programie na etapie
wykonania programu; inaczej późne wiązanie.”
Wywołania polimorficzne metod dotyczą tylko metod niestatycznych, natomiast nie dotyczą metod sta-
tycznych oraz operacji dostępu (odczytu / zapisu) do atrybutów zarówno statycznych jak i niestatycznych.
Dostęp do atrybutów oraz metod statycznych powinien być realizowany poprzez klasę, do której należą, np.
Klasa.atrybutStatyczny lub Klasa.metodaStatyczna(). Możliwy jest dostęp poprzez: obiekt.atrybutStatyczny
lub obiekt.metodaStatyczna(). Jest on jednak niezalecany i jego zastosowanie powodujący wydrukowanie
ostrzeżenia podczas kompilacji.
1
package
pl.kielce.tu.lab6;
2
3
class
A {
4
public
String field =
"A"
;
5
public static
String staticField =
"A"
;
6
7
public void
test() {
8
System.out.println(
"A.test()"
);
9
}
10
11
public static void
testStatic() {
12
System.out.println(
"A.testStatic()"
);
13
}
14
}
15
16
class
B
extends
A {
17
public
String field =
"B"
;
18
public static
String staticField =
"B"
;
19
20
public void
test() {
21
System.out.println(
"B.test()"
);
22
}
23
24
public static void
testStatic() {
25
System.out.println(
"B.testStatic()"
);
26
}
27
}
28
29
public class
TestPolymorphism {
30
private final static
String nl = System.getProperty(
"line.separator"
);
31
32
public static void
main(String[] args) {
33
A a =
new
A();
34
a.test();
35
// Warning: the static method testStatic() from the type A should be accessed
36
// in a static way
37
a.testStatic();
38
// Warning: the static field A.testStatic() should be accessed in a static way
39
System.out.println(
"field = "
+ a.field +
", staticField = "
+ a.staticField + nl);
11
40
41
B b =
new
B();
42
b.test();
43
// Warning: the static method testStatic() from the type B should be accessed
44
// in a static way
45
b.testStatic();
46
// Warning: the static field B.testStatic() should be accessed in a static way
47
System.out.println(
"field = "
+ b.field +
", staticField = "
+ b.staticField + nl);
48
49
A ab =
new
B();
50
ab.test();
51
// Warning: the static method testStatic() from the type A should be accessed
52
// in a static way
53
ab.testStatic();
54
// Warning: the static field A.testStatic() should be accessed in a static way
55
System.out.println(
"field = "
+ ab.field +
", staticField = "
+ ab.staticField + nl);
56
}
57
}
Przykład 14: src/pl/kielce/tu/lab6/TestPolymorphism.java {link}
4
Przesłanianie i przeciążanie
4.1
Przesłanianie
Przesłanianie (ang. overriding) [7] – „Odnosi się do sytuacji, kiedy implementacje funkcji (procedur,
operatorów, metod) posiadających tę samą nazwę występują na różnych poziomach hierarchii dziedziczenia
(co najmniej dwóch). W tej sytuacji do obiektu stosuje się funkcję znajdującą się najniżej w części hierarchii
od korzenia do klasy tego obiektu; pozostałe funkcje o tej samej nazwie (z klas bardziej ogólnych, nadklas)
są ’przesłonięte’ przez tę funkcję. Przesłanianie jest realizacją strategii określanej jako ’pojedyncza dyspozy-
cja’ (ang. single dispatching) i jest ściśle powiązane z polimorfizmem. Przesłanianie wymaga dynamicznego
wiązania i jest jednym z ważnych elementów wspomagających ponowne użycie (ang. reuse).”
4.2
Przeciążanie
Przeciążanie (ang. overloading) [7] – „Sytuacja, w której ta sama nazwa (lub symbol) jest użyta do
oznaczenia dwóch lub więcej funkcji (procedur, operatorów lub metod), zaś rozstrzygnięcie tej homonimii
następuje na podstawie kontekstu jej użycia (np. typu lub liczby argumentów operacji).”
4.3
Adnotacja @Override
„Zapis @Override [2] chroni nas przed przypadkowym przeciążeniem tam, gdzie chodzi nam o przesłonię-
cie.” Co stanie się jeżeli linia 23 z adnotacją @Override zostanie odkomentowana?
1
class
Calculator {
2
// przeciążenie printSum(int i, int j, int k, int l, int m, boolean b)
3
void
printSum() {
4
System.out.println(
"Calculator.printSum()"
);
5
}
6
7
// przeciążenie printSum()
8
void
printSum(
int
i,
int
j,
int
k,
int
l,
int
m,
boolean
b) {
9
System.out.println(
"Calculator.printSum(int i, int j, int k, int l, int m, boolean b)"
);
10
}
11
}
12
13
class
Calculator2
extends
Calculator {
14
// przesłonięcie printSum()
15
@Override
12
16
void
printSum() {
17
System.out.println(
"Calculator2.printSum()"
);
18
}
19
20
// przeciążenie czy przesłonięcie ???
21
// @Override
22
void
printSum(
int
i,
int
j,
int
k,
int
l,
boolean
b) {
23
System.out.println(
"printSum(int i, int j, int k, int l, boolean b)"
);
24
}
25
}
Przykład 15: src/pl/kielce/tu/lab6/TestOverride.java {link}
4.4
Kowariancja typów zwracanych
Przesłonięta metoda klasy pochodnej [2] może zwracać typ pochodny względem typu zwracanego przez
metodę bazową. W przykładzie poniżej proszę porównać typy zwracane przez metody potomek() oraz two-
rzenie obiektów k1-k2 i p1-p2.
1
package
pl.kielce.tu.lab6;
2
3
class
Zwierze {
4
void
odglos() {
5
System.out.println(
"???"
);
6
}
7
8
void
dzwiek(
int
iloscPowtorzen) {
9
for
(
int
i = 0; i < iloscPowtorzen; i++)
10
odglos();
11
}
12
13
Zwierze potomek() {
14
return new
Zwierze();
15
}
16
}
17
18
class
Kot
extends
Zwierze {
19
@Override
20
void
odglos() {
21
System.out.println(
"Miau"
);
22
}
23
24
@Override
25
Kot potomek() {
26
return new
Kot();
27
}
28
}
29
30
class
Pies
extends
Zwierze {
31
@Override
32
void
odglos() {
33
System.out.println(
"Hau"
);
34
}
35
36
@Override
37
Pies potomek() {
38
return new
Pies();
39
}
40
}
41
42
public class
TestCovariance {
13
43
public static void
main(String[] args) {
44
Zwierze z1 =
new
Kot();
45
Zwierze z2 =
new
Pies();
46
47
// Type missmatch: can not convert from Zwierze to Kot
48
// Kot k1 = z1.potomek();
49
Kot k1 = (Kot) z1.potomek();
50
// Type missmatch: can not convert from Zwierze to Pies
51
// Pies p1 = z2.potomek();
52
Pies p1 = (Pies) z2.potomek();
53
54
Kot k2 = k1.potomek();
55
Pies p2 = p1.potomek();
56
}
57
}
Przykład 16: src/pl/kielce/tu/lab6/TestCovariance.java {link}
5
Przykładowa treść laboratorium
1. Na stronach 16 oraz 18 znajdują się definicje klasy M yHashCollection oraz T estM yCollection. Klasa
M yHashCollection
w uproszczony sposób przedstawia działanie kolekcji haszującej (mieszającej) z eli-
minacją obiektów o tej samej wartości. Zawiera ona tablicę kubełków (ang. bucket) należących do klasy
M yBucket
. Podczas dodania lub usunięcia obiektu z kolekcji najpierw wyliczana jest wartość funkcji
haszującej (mieszającej) dla obiektu. Następnie na podstawie funkcji reszty z dzielenia wyznaczany
jest numer kubełka, do którego należy dodać (usunąć) obiekt. W klasie T estM yCollection w metodzie
testIntegers
() do kolekcji są dodawane i usuwane liczby. Program działa zgodnie z założeniami. Obiek-
ty o tej samej wartości dodawane są tylko raz. Poprawnie działa także usuwanie obiektów. Celem tego
zadania jest uzyskanie takiego samego zachowania dla metody testStudentsAndT eachers():
• wielokrotne dodanie różnych obiektów o takich samych polach powinno spowodować dodanie obiek-
tu tylko raz,
• usunięcie obiektu powinno być możliwe także poprzez podanie innego obiektu, ale posiadającego
takie same pola.
Modyfikować można tylko klasy P erson, Student oraz T eacher. Oczekiwany wynik działania programu
znajduje się na str. 20 .
2. Proszę stworzyć rozbudowaną hierarchię klas dotyczącą komponentów graficznych, takich jak Okna,
Panele, Przyciski, ListyWyboru, itp. W tym zadaniu powinny pojawić się przykłady:
• dziedziczenia, w tym także dziedziczenia z wykorzystaniem klas wewnętrznych,
• przeciążania i przesłaniania metod zdefiniowanych we własnych klasach oraz klasie Object, adno-
tacji @Override,
• rzutowania w górę (na klasę bazową) i w dół (na klasę pochodną), wykorzystania operatora in-
stanceOf lub metody isInstance(),
• wszystkich zastosowań słowa kluczowego final.
3. Inne
Literatura
[1] Schildt Herbert, Java. Kompendium programisty. Wydanie VIII, Helion, 2012
[2] Eckel Bruce, Thinking in Java, Edycja polska, Wydanie IV, Helion, 2006
[3] Horstmann Cay S., Cornell Gary, Java. Podstawy, Wydanie VIII, Helion, 2008
14
[4] Horstmann Cay S., Cornell Gary, Java. Techniki zaawansowane, Wydanie VIII, Helion, 2009
[5] Bloch Joshua, Java. Efektywne programowanie. Wydanie II, Helion, 2009
[6] Brackeen David, Barker Bret, Vanhelsuwe Laurence, Java. Tworzenie gier, Helion, 2004
[7] Słownik terminów z zakresu obiektowości. Kazimierz Subieta, Akademicka Oficyna Wydawnicza PLJ,
Warszawa 1999
[8] Sedgewick Robert, Wayne Kevin, Algorytmy. Wydanie IV, Helion, 2012
Materiały
do
przedmiotu
dostępne
są
na
stronach:
http://achilles.tu.kielce.pl/
http://weaii-
moodle.tu.kielce.pl/
15
Klasa MyHashCollection
package
pl.kielce.tu.lab6;
public class
MyHashCollection {
private
MyBucket[] buckets;
private int
bucketsQuantity;
public
MyHashCollection(
int
bucketsQuantity,
int
bucketCapacity) {
this
.bucketsQuantity = bucketsQuantity;
this
.buckets =
new
MyBucket[bucketsQuantity];
for
(
int
i = 0; i < bucketsQuantity; i++)
this
.buckets[i] =
new
MyBucket(bucketCapacity);
}
/**
* Adds the object only when it does not exists in the collection
*
* @param obj
* the object which should be added
* @return true if the object was added, and false otherwise
*/
public boolean
add(Object obj) {
int
hashCode = obj.hashCode();
int
bucketNumber = hashCode % bucketsQuantity;
return
buckets[bucketNumber].add(obj);
}
/**
* Deletes object from the collection
*
* @param obj
* the object which should be deleted
* @return true if the object was deleted, and false otherwise
*/
public boolean
delete(Object obj) {
int
hashCode = obj.hashCode();
int
bucketNumber = hashCode % bucketsQuantity;
return
buckets[bucketNumber].delete(obj);
}
@Override
public
String toString() {
String answer =
"["
;
for
(
int
i = 0; i < buckets.length; i++) {
answer += buckets[i];
if
(i != buckets.length - 1)
answer +=
", "
;
}
answer +=
"]"
;
return
answer;
}
private class
MyBucket {
final private static int
NONE = -1;
private
Object[] objects;
public
MyBucket(
int
bucketCapacity) {
objects =
new
Object[bucketCapacity];
}
16
/**
* Adds the object only when it does not exists in the bucket
*
* @param obj
* the object which should be added
* @return true if the object was added, and false otherwise
*/
public boolean
add(Object obj) {
int
empty = NONE;
for
(
int
i = 0; i < objects.length; i++) {
// checks whether there is an empty space
if
(empty == NONE && objects[i] ==
null
)
empty = i;
// checks whether the object already was added
if
(objects[i] !=
null
&& objects[i].equals(obj))
return false
;
}
// not empty space
if
(empty == NONE)
return false
;
// adds object
objects[empty] = obj;
return true
;
}
/**
* Deletes object from the bucket
*
* @param obj
* the object which should be deleted
* @return true if the object was deleted, and false otherwise
*/
public boolean
delete(Object obj) {
for
(
int
i = 0; i < objects.length; i++) {
// checks whether the object was added
if
(objects[i] !=
null
&& objects[i].equals(obj)) {
objects[i] =
null
;
return true
;
}
}
return false
;
}
@Override
public
String toString() {
String answer =
"["
;
for
(
int
i = 0; i < objects.length; i++) {
answer += objects[i] !=
null
? objects[i] :
"_"
;
if
(i != objects.length - 1)
answer +=
", "
;
}
answer +=
"]"
;
return
answer;
}
}
}
Przykład 17: src/pl/kielce/tu/lab6/MyHashCollection.java {link}
17
Klasa TestMyCollection
package
pl.kielce.tu.lab6;
class
Person {
int
id;
String firstName, lastName;
Person(
int
id, String firstName, String lastName) {
this
.id = id;
this
.firstName = firstName;
this
.lastName = lastName;
}
}
class
Student
extends
Person {
String group;
Student(
int
id, String firstName, String lastName, String group) {
super
(id, firstName, lastName);
this
.group = group;
}
}
class
Teacher
extends
Person {
String title;
Teacher(
int
id, String title, String firstName, String lastName) {
super
(id, firstName, lastName);
this
.title = title;
}
}
public class
TestMyCollection {
private static void
testIntegers() {
MyHashCollection list =
new
MyHashCollection(4, 4);
list.add(1);
list.add(1);
list.add(9);
list.add(2);
list.add(3);
System.out.println(list);
list.delete(3);
System.out.println(list);
list.add(5);
System.out.println(list);
}
private static void
testStudentsAndTeachers() {
MyHashCollection list =
new
MyHashCollection(4, 4);
list.add(
new
Student(1,
"Jan"
,
"Kowalski"
,
"Group_1"
));
list.add(
new
Student(1,
"Jan"
,
"Kowalski"
,
"Group_1"
));
list.add(
new
Teacher(9,
"prof."
,
"Adam"
,
"Mickiewicz"
));
list.add(
new
Student(2,
"Piotr"
,
"Nowak"
,
"Group_1"
));
list.add(
new
Student(3,
"Tomasz"
,
"Kowal"
,
"Group_1"
));
System.out.println(list);
list.delete(
new
Student(3,
"Piotr"
,
"Nowak"
,
"Group_1"
));
System.out.println(list);
list.add(
new
Student(5,
"Wojciech"
,
"Król"
,
"Group_1"
));
System.out.println(list);
18
}
public static void
main(String[] args) {
testIntegers();
testStudentsAndTeachers();
}
}
Przykład 18: src/pl/kielce/tu/lab6/TestMyCollection.java {link}
19
Aktualny wynik działania programu:
1
[[_, _, _, _], [1, 9, _, _], [2, _, _, _], [3, _, _, _]]
2
[[_, _, _, _], [1, 9, _, _], [2, _, _, _], [_, _, _, _]]
3
[[_, _, _, _], [1, 9, 5, _], [2, _, _, _], [_, _, _, _]]
4
[[S1, S2, S3, _], [T9, _, _, _], [_, _, _, _], [S1, _, _, _]]
5
[[S1, S2, S3, _], [T9, _, _, _], [_, _, _, _], [S1, _, _, _]]
6
[[S1, S2, S3, _], [T9, S5, _, _], [_, _, _, _], [S1, _, _, _]]
Oczekiwany wynik działania programu:
1
[[_, _, _, _], [1, 9, _, _], [2, _, _, _], [3, _, _, _]]
2
[[_, _, _, _], [1, 9, _, _], [2, _, _, _], [_, _, _, _]]
3
[[_, _, _, _], [1, 9, 5, _], [2, _, _, _], [_, _, _, _]]
4
[[_, _, _, _], [S1, T9, _, _], [S2, _, _, _], [S3, _, _, _]]
5
[[_, _, _, _], [S1, T9, _, _], [S2, _, _, _], [_, _, _, _]]
6
[[_, _, _, _], [S1, T9, S5, _], [S2, _, _, _], [_, _, _, _]]
20