11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 1
11. JavaBeans
11.1 Wprowadzenie
JavaBeans
to model przeno
ś
nych komponentów, stworzony w j
ę
zyku
Java (od 1996 r.), przeznaczony do tworzenia małych,
komponentów
programowych
wielokrotnego
u
ż
ytku,
w
szczególno
ś
ci wykorzystywanych w narz
ę
dziach programowania
wizualnego (np. BeanBox, VisualAge, JBuilder).
Bean
jest komponentem programowym – mo
ż
e by
ć
obiektem
wizualnym lub niewizualnym (jak np. kolejka czy stos), który mo
ż
e
wchodzi
ć
w skład apletów i aplikacji.
Pakiet
java.beans
nale
ż
y do podstawowego “Core API”
ś
rodowiska
Java.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 2
Ziarno (Bean)
jest klas
ą
, z której mo
ż
na korzysta
ć
w narz
ę
dziu
projektowym (w fazie tworzenia programu), gdy
ż
posiada ona:
1.
gwarantowane cechy podstawowe, takie jak
serializacja
i
klonowanie
;
2.
implementuje
ustalone konwencje (nazw)
;
3.
deklaratywna (abstrakcyjna) posta
ć
składowych 3 rodzajów:
a. zdarzenia,
b. własno
ś
ci,
c. metody.
4.
korzysta z
mechanizmu introspekcji i refleksji
(meta-danych).
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 3
Gwarantowane cechy podstawowe:
•
bezparametrowy konstruktor (wzorce „Factory“),
•
interfejs
Clonable
, metoda
Clone
;
•
interfejs
Serializable
, pusty bez metod (serializacja składowych za
wyj
ą
tkiem
transient
i
static
) - metody
writeObject,
readObject;
lub
•
interfejs
Externalizable
, metody
writeExternal,
readExternal
.
Konwencje nazw
•
Zdarzenie
E
– para metod
addEListener, removeEListener
•
Własno
ść
P
– para metod
(
getP, setP
), wzgl
ę
dnie (
isP, setP
) dla
logicznego (
Boolean
)
P
:
- własno
ść
zwi
ą
zana – wymaga powiadamiania obserwatorów
zdarzenia typu
PropertyChange
-
własno
ść
z ograniczeniem - z prawem weta klientów, realizowana
poprzez wyj
ą
tek
PropertyVetoException
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 4
11.2 Definiowanie zdarze
ń
dla klasy ziarna
EventObject
Klasa
java.util.EventObject
jest bazow
ą
klas
ą
dla reprezentacji zdarze
ń
dla komponentów „ziaren” (Bean).
public class EventObject extends Object
implements java.io.Serializable {
public EventObject (Object source);
public Object getSource();
public String toString();
}
Mo
ż
na korzysta
ć
bezpo
ś
rednio z tej klasy dla wyra
ż
enia zdarzenia, ale
zwykle jednak tworzy si
ę
jej klasy pochodne dla specyficznych rodzajów
zdarze
ń
. Wtedy obiekt obsługi zdarzenia (obserwator) uzyskuje w
powiadomieniu o zdarzeniu obiekt specyficznej klasy pochodnej od
EventObject
.
Np.
public class HireEvent extends EventObject {
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 5
private long hireDate;
public HireEvent (Object source) {
super (source);
hireDate = System.currentTimeMillis();
}
public HireEvent (Object source, long hired) {
super (source);
hireDate = hired;
}
public long getHireDate () {
return hireDate;
}
}
EventListener
Pusty interfejs
EventListener
słu
ż
y do zaznaczania klasy jako
potencjalnego obserwatora zdarze
ń
. Konkretny rodzaj obsługi zdarze
ń
wynika z implementowania specyficznego interfejsu
EventTypeListener
,
pochodnego od
EventListener.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 6
Np. dla nowego zdarzenia
HireEvent
odpowiedni interfejs nazywałby si
ę
HireListener
. Taki interfejs deklaruje metody obsługi tego zdarzenia. Np.
public interface HireListener extends java.util.EventListener {
public abstract void hired (HireEvent e);
// Metoda obsługi zdarzenia
}
Ź
ródło zdarzenia
Obiekty obserwatorów rejestruj
ą
si
ę
w obiekcie-
ź
ródle zdarzenia lub
wyrejestrowuj
ą
dzi
ę
ki odpowiednim metodom rejestracji, tworzonym
według wzoru:
public synchronized void addListenerType(ListenerType l);
public synchronized void removeListenerType(ListenerType l);
Przykładowy kod obu metod dla naszego komponentu:
private Vector hireListeners = new Vector();
public synchronized void addHireListener (HireListener l) {
hireListeners.addElement (l);
}
public synchronized void removeHireListener (HireListener l) {
hireListeners.removeElement (l);
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 7
}
Po zaj
ś
ciu zdarzenia wymagane jest powiadomienie obserwatorów tego
zdarzenia. Np. zrealizujemy metod
ę
dla rozgłaszania zdarzenia:
protected void notifyHired () {
Vector l;
// Utwórz obiekt opisu zdarzenia:
HireEvent h = new HireEvent (this);
// Kopiuj wektor z obiektami obsługi – zabezpiecza to przed zmianą
// obserwatorów podczas rozgłaszania:
synchronized (this) {
l = (Vector)hireListeners.clone();
}
// Powiadom po kolei wszystkich obserwatorów:
for (int i=0; i<l.size(); i++) {
HireListener hl = (HireListener)l.elementAt (i);
hl.hired(h);
}
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 8
11.3 Własno
ś
ci
Jest to publiczna cecha klasy ziarna, reprezentowana zwykle przez
nie-publiczne pole. Mo
ż
e mie
ć
dost
ę
p: read-write, read-only, lub
write-only. Wyró
ż
nia si
ę
4 rodzaje własno
ś
ci:
•
zwykłe,
•
indeksowane (indeksery),
•
zwi
ą
zane,
•
ograniczone.
Zwykłe własno
ś
ci
Dla własno
ś
ci P o dost
ę
pie „read-write” wymagana jest definicja pary
metod – konwencj
ą
jest nazywanie ich:
setP, getP
.
Np. własno
ść
salary :
float salary;
public void setSalary (float newSalary) {
salary = newSalary;
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 9
public float getSalary () {
return salary;
}
Własno
ś
ci typu logicznego mog
ą
zamieni
ć
metod
ę
getP
na
isP
. Np.
boolean trained;
public void setTrained (boolean trained) {
this.trained = trained;
}
public boolean isTrained () {
return trained;
}
Indeksery
Indekser to własno
ść
przechowuj
ą
ca tablic
ę
warto
ś
ci. Konwencja:
public void setPropertyName (PropertyType[] list)
public void setPropertyName (PropertyType element, int position)
public PropertyType[] getPropertyName ()
public PropertyType getPropertyName (int position)
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 10
Np. indekser
item
w klasie
List
mo
ż
e wygl
ą
da
ć
nast
ę
puj
ą
co:
public class ListBean extends List {
public String[] getItem () {
return getItems ();
}
public synchronized void setItem (String item[]) {
removeAll();
for (int i=0;i<item.length;i++)
addItem (item[i]);
}
public void setItem (String item, int position) {
replaceItem (item, position)
}
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 11
Własno
ś
ci zwi
ą
zane
Teraz uczynimy własno
ść
salary
własno
ś
ci
ą
zwi
ą
zan
ą
. Je
ś
li dwa obiekty
korzystaj
ą
z tej własno
ś
ci to o jej zmianie, powodowanej przez jeden z
nich, jest powiadamiany drugi obiekt.
W
tym
celu
nale
ż
y
zrealizowa
ć
list
ę
obserwatorów
dla
PropertyChangeEvent
poprzez klas
ę
PropertyChangeSupport
.
Najpierw tworzymy list
ę
obserwatorów:
private PropertyChangeSupport changes = new PropertyChangeSupport
(this);
Nale
ż
y ni
ą
zarz
ą
dza
ć
:
public void addPropertyChangeListener ( PropertyChangeListener p) {
changes.addPropertyChangeListener (p);
}
public void removePropertyChangeListener (PropertyChangeListener p) {
changes.removePropertyChangeListener (p);
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 12
Je
ś
li zajdzie zdarzenie (tu jest nim wywołanie metody
setSalary
),
sprawdzamy czy warto
ść
własno
ś
ci uległa zmianie i je
ś
li tak, to
powiadamiamy obserwatorów:
public void setSalary (float salary) {
Float oldSalary = new Float (this.salary);
this.salary = salary;
changes.firePropertyChange (
"salary", oldSalary, new Float (this.salary));
}
Po stronie obserwatora potrzebna jest metoda
propertyChange
:
public void propertyChange(PropertyChangeEvent e);
W metodzie tej sprawdzamy metod
ą
getPropertyName
to czy otrzymali
ś
my
zdarzenie wła
ś
ciwego typu -
PropertyChangeEvent
.
Własno
ś
ci z ograniczeniami
Te własno
ś
ci s
ą
podobne do własno
ś
ci zwi
ą
zanych ale dodatkowo do listy
PropertyChangeListener
ziarno zarz
ą
dza list
ą
VetoableChangeListener
.
Wtedy przed wykonaniem zmiany własno
ś
ci ziarno pyta obserwatorów na
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 13
li
ś
cie
VetoableChangeListener
o pozwolenie. Je
ś
li odpowied
ź
jest
negatywna to ziarno zgłasza wyj
ą
tek
PropertyVetoException
, deklarowany
przez metod
ę
setP
.
Np. uzupełniamy własno
ść
salary
o :
private VetoableChangeSupport vetoes = new VetoableChangeSupport (this);
public void addVetoableChangeListener ( VetoableChangeListener v) {
vetoes.addVetoableChangeListener (v);
}
public void removeVetoableChangeListener (VetoableChangeListener v){
vetoes.removeVetoableChangeListener (v);
}
Zmieniamy metod
ę
setSalary
:
public void setSalary (float salary) throws PropertyVetoException {
Float oldSalary = new Float(this.salary);
vetoes.fireVetoableChange("salary", oldSalary, new Float(salary));
this.salary = salary;
changes.firePropertyChange("salary", oldSalary, new Float(this.salary));
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 14
Po
stronie
obserwatora
dodajemy
metod
ę
interfejsu
VetoableChangeListener
o nazwie
vetoableChange
:
public void vetoableChange(PropertyChangeEvent e)
throws PropertyVetoException;
Mo
ż
liwe jest te
ż
rozdzielenie obu list – zdarzenia zmiany własno
ś
ci i
zdarzenia wetowalnej zmiany własno
ś
ci – zarz
ą
dzanie oddzieln
ą
list
ą
dla
ka
ż
dej własno
ś
ci. Wzorzec tego rozwi
ą
zania:
public void addPropertyNameListener ( PropertyChangeListener p);
public void removePropertyNameListener ( PropertyChangeListener p);
public void addPropertyNameListener (VetoableChangeListener v);
public void removePropertyNameListener (VetoableChangeListener v);
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 15
11.4 Trwało
ść
/ serializacja
Ziarna s
ą
obiektami ze stanen
i powinny by
ć
trwałymi obiektami
.
1) Standardowa serializacja - interfejs
Serializable
Jest to zdolno
ść
obiektu do zapami
ę
tania swojego stanu w celu
pó
ź
niejszego odtworzenia. W tym celu komponenty korzystaj
ą
z
mechanizmu
serializacji w j
ę
zyku Java
.
Podstawowe interfejsy w zakresie serializacji to
ObjectInput
i
ObjectOutput
a podstawowe klasy implementuj
ą
ce te interfejsy to
ObjectInputStream
i
ObjectOutputStream
.
Obiekt klasy implementuj
ą
cej
Serializable
jest serializowany metod
ą
ObjectOutput.writeObject
(z
obiektem
b
ę
d
ą
cym
parametrem)
i
deserializowany metod
ą
ObjectInput.readObject
.
Standardowa realizacja mechanizmu serializacji pozwala zapami
ę
ta
ć
wszystkie
niestatyczne
i
nietymczasowe (non-transient)
pola obiektu w
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 16
pliku binarnym o formacie danych przyj
ę
tym w metodach
writeObject /
readObject.
Plik posiada rozszerzenie
.ser
.
Je
ś
li w polu serializowanego obiektu referowany jest inny obiekt, to
równie
ż
ten inny obiekt jest rekursywnie zapami
ę
tywany - ta
dekompozycja jest kontynuowana, a
ż
pozostan
ą
wył
ą
cznie dane typów
prostych, które s
ą
bezpo
ś
rednio zapami
ę
tywane.
Przykład
. Utwórzmy obiekty klasy
TreeNode
:
TreeNode top = new TreeNode("top");
top.addChild(new TreeNode("left child"));
top.addChild(new TreeNode("right child"));
Zapami
ę
tujemy te obiekty:
FileOutputStream fOut = new FileOutputStream("test.ser");
ObjectOutput out = new ObjectOutputStream(fOut);
out.writeObject(top);
// Zapisz obiekt – zapisze również jego 2
// podobiekty
out.flush();
out.close();
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 17
Po zako
ń
czeniu programu i ponownym jego restarcie mo
ż
liwe b
ę
dzie
odtworzenie obiektu na podstawie pliku test.ser.
FileInputStream fIn = new FileInputStream("test.ser");
ObjectInputStream in = new ObjectInputStream(fIn);
TreeNode n = (TreeNode)in.readObject();
W metodzie
readObject()
nast
ę
puje alokacja pami
ę
ci dla obiektu ziarna i
wywoływany jest bezargumentowy konstruktor jego klasy.
2) Interfejs
Externalizable
Je
ś
li plik z serializowanym obiektem ma posiada
ć
specyficzny format, to
programista powinien skorzysta
ć
z interfejsu
Externalizable
i odpowiednio
zdefiniowa
ć
jego 2 metody:
readExternal
i
writeExternal
. Klasa
serializowanego obiektu musi posiada
ć
bezargumentowy konstruktor.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 18
3) Trwało
ść
długo-okresowa
Poj
ę
cie to oznacza model trwałego pami
ę
tania ziaren w formacie XML.
XMLEncoder, XMLDecoder
Klasa
XMLEncoder
przeznaczona jest to wypisywania serializowalnych
obiektów do plików tekstowych w formacie XML.
Przykład.
Wypisanie ziarna i jego własno
ś
ci w postaci XML:
XMLEncoder encoder = new XMLEncoder(
new BufferedOutputStream(
new FileOutputStream( "Beanarchive.xml" ) ) );
encoder.writeObject( object );
encoder.close();
Klasa
XMLDecoder
przeznaczona jest do odtworzenia obiektu z
dokumentu XML, wcze
ś
niej utworzonego przez
XMLEncoder.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 19
Przykład
XMLDecoder decoder = new XMLDecoder(
new BufferedInputStream(
new FileInputStream( "Beanarchive.xml" ) ) );
Object object = decoder.readObject();
decoder.close();
Znaczniki
XML
Dla reprezentacji ziarna w j
ę
zyku XML u
ż
ywane s
ą
nast
ę
puj
ą
ce znaczniki:
•
W preambule XML podana jest wersja XML i rodzaj kodowania.
•
Znacznik
<java>
zawiera wszystkie elementy obiektu ziarna.
•
Znacznik
<object>
reprezentuje zbiór wywoła
ń
metod niezb
ę
dnych do
rekonstrukcji obiektu z jego serializowanej postaci :
•
<object class="javax.swing.JButton" method="new">
•
<string>Ok</string>
•
</object>
lub wyra
ż
enia
<object class="javax.swing.JButton">
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 20
<void method="setText">
<string>Cancel</string>
</void>
</object>
•
Znaczniki dla definiowania typów prostych:
o
<boolean>
o
<byte>
o
<char>
o
<short>
o
<int>
o
<long>
o
<float>
o
<double>
•
Np.
<int>5555</int>
•
Znacznik
<class>
dla reprezentacji instancji klasy
Class
:
•
<class>java.swing.JFrame</class>
•
Znacznik
<array>
dla reprezentacji tablicy:
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 21
•
<array class="java.lang.String" length="5">
•
</array>
Przykład archiwum w XML generowanego przez komponent
SimpleBean
:
<?xml version="1.0" encoding="UTF-8" ?>
<java>
<object class="javax.swing.JFrame">
<void method="add">
<object class="java.awt.BorderLayout" field="CENTER"/>
<object class="SimpleBean"/>
</void>
<void property="defaultCloseOperation">
<object class="javax.swing.WindowConstants" field="DISPOSE_ON_CLOSE"/>
</void>
<void method="pack"/>
<void property="visible">
<boolean>true</boolean>
</void>
</object>
</java>
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 22
4) Problem serializacji
Jak zapewni
ć
serializowalno
ść
obiektu naszej klasy?
1. Czy nasza klasa implementuje interfejs
Serializable
(bezpo
ś
rednio lub
po
ś
rednio) lub
Externalizable
?
2. Czy wszystkie pola klasy
s
ą
serializowalne
?
Pola typów prostych (tzw. „dla warto
ś
ci”) s
ą
zawsze serializowalne.
Pozostałe pola, b
ę
d
ą
ce typu klas, nale
ż
y sprawdza
ć
indywidualnie. Np.
klasa
Image
nie jest serializowalna. Nale
ż
y wtedy zdecydowa
ć
, czy
nale
ż
y i jak zapami
ę
tywa
ć
takie pola – przy odtwarzaniu wczytywa
ć
dane z osobnego pliku, stawia
ć
je na warto
ś
ci domy
ś
lne zaznaczy
ć
pola
jako
transient
, itd.
3. Upewni
ć
si
ę
, czy chcemy zapami
ę
ta
ć
wszystkie pola, które nie s
ą
zaznaczone jako
transient
lub
static
.
4. Czy odpowiada nam
domy
ś
lny mechanizm serializacji
, czy te
ż
chcemy
zapami
ę
ta
ć
obiekt w innej strukturze, np. dokonuj
ą
c jego kompresji lub
szyfrowania?
5. Jak zainicjalizowa
ć
pola
transient
i
static
po deserializacji obiektu
?
6. Czy proces deserializacji ma by
ć
uzupełniony o walidacj
ę
?
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 23
Walidacja deserializowanego obiektu
zwykle polega na sprawdzeniu jego
poprawno
ś
ci - zgodno
ś
ci z zadanym modelem dla danego typu. W tym
celu nale
ż
y utworzy
ć
i zarejestrowa
ć
metod
ę
walidacji.
Przykład.
Chcemy odtworzy
ć
obiekt klasy
Date
, ale ma on odpowiada
ć
„ostatniej” dacie a nie “pierwszej dacie”.
W tym celu mo
ż
emy wprowadzi
ć
pole charakteryzowane jako
transient
,
które jest zerowane podczas deserializacji.
public class TreeNode implements Serializable {
Vector children;
TreeNode parent;
String name;
transient Date date;
// Pole przejściowe - nietrwałe
public TreeNode(String s) {
children = new Vector(5);
name = s;
initClass();
}
private void initClass () {
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 24
date = new Date();
// Tu nadajemy nową wartość polu przejściowemu
}
...
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
s.defaultReadObject();
initClass();
}
}
Uwaga: metody
writeObject
i
readObject
nadpisywane s
ą
parami – w
przeciwnym razie nadpisanie metody
writeObject
nie byłoby tu potrzebne.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 25
5) Odtworzenie / utworzenie ziarna metod
ą
-
Beans.instantiate
Zamiast tworzenia instancji ziarna operatorem
new
mo
ż
na te
ż
skorzysta
ć
z
metody
Beans.instantiate
. Jest to typowy sposób odtworzenia obiektu
ziarna po uprzedniej serializacji. W ten sposób nie musimy zna
ć
nazw
wszystkich potencjalnych klas ziaren w momencie tworzenia kodu.
Np. utworzenie obiektu klasy
TextField
bez korzystania z
new
:
Component c = (Component)Beans.instantiate(null, "java.awt.TextField");
Jest to równowa
ż
ne w działaniu z kodem:
Component c = new TextField();
Aby przekona
ć
si
ę
o ró
ż
nicy utwórzmy klas
ę
pochodn
ą
MyTextField
:
public class MyTextField extends java.awt.TextField {
}
Nast
ę
pnie w poni
ż
szym programie dokonamy serializacji obiektu klasy
MyTextField:
import java.awt.*;
import java.io.*;
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 26
public class TfSaver {
public static void main(String args[]) {
MyTextField mtf = new MyTextField();
// Ustaw własności:
Font ft = new Font("Serif", Font.ITALIC, 36);
mtf.setFont(ft);
mtf.setText("Hello World");
// Serializuj:
try {
FileOutputStream f = new FileOutputStream("MyTextField.ser");
ObjectOutputStream s = new ObjectOutputStream(f);
s.writeObject(mtf);
s.flush();
} catch (Exception e) {
System.out.println(e);
}
System.exit(0);
}
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 27
Aplikacja korzystaj
ą
ca z instancji
MyTextField
b
ę
dzie miała ju
ż
ustawione
własno
ś
ci
font
i
text
(na czcionk
ę
36-pt, italic, Serif i napis "Hello World").
W celu odtworzenia zastosujemy wywołanie
Beans.instantiate
o postaci:
Component c = (Component)Beans.instantiate(null, "MyTextField");
Dzi
ę
ki metodzie
Beans.instantiate
mamy wi
ę
ksz
ą
elastyczno
ść
tworzenia
instancji: je
ś
li istnieje plik o nazwie
NazwaKlasy.ser
(w tym przypadku
MyTextField.ser
) to nast
ą
pi deserializacja obiektu na podstawie danych z
tego pliku; w przeciwnym razie obiekt ziarna zostanie zainicjalizowany
bezargumentowym konstruktorem w tej klasie.
Gdy istnieje
MyTextField.ser
:
Gdy nie ma pliku
MyTextField.ser:
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 28
6) Zarz
ą
dzanie wersjami klas ziaren
Ewentualne zmiany, dokonywane w definicji klasy, mog
ą
dotyczy
ć
struktury danych lub metod.
Je
ś
li zmieniamy struktur
ę
trwałych danych klasy ziarna to obiekty
wcze
ś
niejszych wersji tej klasy nie mog
ą
by
ć
deserializowane.
Nawet dodanie metody do klasy ziarna czyni obiekt wcze
ś
niejszej wersji
niemo
ż
liwy do deserializacji – próba deserializacji zako
ń
czy si
ę
wyj
ą
tkiem
java.io.InvalidClassException
.
Jednak dodanie lub usuni
ę
cie metody nie ma wpływu na informacj
ę
o
stanie obiektu (metody wyra
ż
aj
ą
zachowanie obiektu a to nie jest
serializowane) i z tego punktu widzenia obiekty dwóch wersji klasy,
ró
ż
ni
ą
ce si
ę
jedynie metodami, powinny by
ć
zgodne w procesie
deserializacji.
Aby zapewni
ć
mo
ż
liwo
ść
odtworzenia stanu obiektu “poprzedniej” wersji
klasy w obiekcie “nowej” wersji tej klasy nale
ż
y doda
ć
pole statyczne
zawieraj
ą
ce warto
ść
“Stream Unique Identifier (SUID)”:
Np.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 29
private static final long serialVersionUID = -2966288784432217853L;
Warto
ść
pola jest regenerowana podczas deserializacji i jest
porównywana z aktualn
ą
definicj
ą
klasy.
Warto
ś
ci SUID s
ą
generowane przez program
serialver
z argumentem,
b
ę
d
ą
cym nazw
ą
klasy, w wyniku algorytmu z funkcj
ą
mieszaj
ą
c
ą
(„hash”).
Program
serialver
mo
ż
e by
ć
tez wywołany jawnie z opcj
ą
–show
i wtedy
pojawi si
ę
graficzny interfejs, który pozwoli programi
ś
cie przej
ąć
warto
ść
SUID, wygenerowan
ą
dla zadanej klasy i umie
ś
ci
ć
j
ą
w kodzie programu.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 30
11.5 Introspekcja i refleksja
1) Mechanizm refleksji
Nale
ż
y on do podstawowych elementów j
ę
zyka – jego API stanowi
pakiet
java.lang.reflection
. Umo
ż
liwia on m.in.:
•
Okre
ś
lenie
klasy
obiektu.
Np.
TextField t = new TextField();
Class c = t.getClass();
// Klasa na podstawie obiektu
Class s = c.getSuperclass();
// Klasa bazowa
Class c = java.awt.Button.class;
// Jawnie znana klasa
Class c = Class.forName(strg);
// Nazwa to strg
•
Informowanie o
modyfikatorach klasy, polach, metodach,
konstruktorach i klasie bazowej
.
•
Informowanie o przynale
ż
no
ś
ci
stałych i metod do interfejsu
.
•
Utworzenie obiektu
klasy nieznanej podczas programowania.
Np.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 31
Class cl = Class.forName(className);
obiekt = cl.newInstance();
•
Pobranie i ustawienie warto
ś
ci pola, nawet gdy podczas
programowania nie jest znana nazwa tego pola.
•
Wywołanie metody, nawet gdy nie jest znana jej nazwa.
•
Utworzenie i modyfikacj
ę
tablicy o elementach nieznanego typu.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 32
2) Interfejs
BeanInfo
BeanInfo
pozwala rozszerzy
ć
mechanizm refleksji w odniesieniu do ziaren.
Dla pewnej klasy programisty
Beanclass
mo
ż
na stworzy
ć
klas
ę
BeanclassBeanInfo
i zaimplementowa
ć
we własny sposób metody
BeanInfo
.
Istnieje klasa
SimpleBeanInfo
, z której mo
ż
e dziedziczy
ć
nasza klasa
BeanclassBeanInfo
i wtedy wystarczy nadpisa
ć
jedynie niektóre z metod
BeanInfo
.
Tryb pracy ziarna: “w czasie projektowania” i “w czasie wykonania”
Podczas projektowania klasa ziarna musi udost
ę
pnia
ć
narz
ę
dziu
projektowemu informacje pozwalaj
ą
ce na edycj
ę
(i ustawianie) własno
ś
ci
oraz definicje rozszerze
ń
własnych programisty.
Musi te
ż
udost
ę
pnia
ć
metody i zdarzenia tak, aby program projektowy
“Builder” mógł wygenerowa
ć
wła
ś
ciwy kod metod klasy ziarna.
Metoda
Beans.isDesignTime
pozwala sprawdzi
ć
tryb pracy ziarna.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 33
Interfejs
BeanInfo
dostarcza
w
fazie
projektowej
(narz
ę
dziom
programowania wizualnego) wybranych informacji (meta-danych) o klasie
ziarna
Beanclass
.
Jawna
specyfikacja
klasy
BeanclassBeanInfo
jest
nadpisaniem
standardowej
introspekcji
i
blokuje mechanizm refleksji
w fazie
projektowej.
Własna implementacja interfejsu
BeanInfo
umo
ż
liwia:
o
Specyfikacj
ę
własnej ikonki dla ziarna;
o
Dostarczenie dodatkowego opisu ziarna;
o
Dostarczenie opisów cech (klasy pochodne od
FeatureDescriptor
)
BeanDescriptor
EventSetDescriptor
PropertyDescriptor
MethodDescriptor
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 34
3) Introspekcja
Jest to proces
pozyskiwania informacji
o własno
ś
ciach, metodach i
zdarzeniach danego ziarna. Do tego celu słu
ż
y klasa
Introspector
.
Innym sposobem introspekcji jest skorzystanie z mechanizmu
refleksji
(“Reflection”).
Klasa
Introspector
odwołuje si
ę
do wła
ś
ciwej klasy-implementacji
BeanInfo
dla klasy ziarna – dzi
ę
ki metodzie
getBeanInfo
posiadaj
ą
cej parametr typu
Class
:
TextField tf = new TextField ();
BeanInfo bi = Introspector.getBeanInfo (tf.getClass());
Je
ś
li nie zdefiniowano
specyficznej klasy implementuj
ą
cej interfejs
BeanInfo
dla klasy ziarna, to
Introspector
korzysta z mechanizmu refleksji
w celu pobrania opisów zdarze
ń
, własno
ś
ci i metod klasy ziarna.
Opis zdarze
ń
Metoda
getEventSetDescriptors
podaje wszystkie zdarzenia zgłaszane
przez
zadan
ą
klas
ę
ziarna.
Dla
ka
ż
dej
pary
metod
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 35
add/removeListenerTypeListener
(zwracaj
ą
cych
void
) powstaje jeden opis
zdarzenia:
EventSetDescriptor[] esd = bi.getEventSetDescriptors();
for (int i=0;i<esd.length;i++) System.out.print (esd[i].getName() + " ");
System.out.println ();
Wynik wykonania powy
ż
szego kodu dla klasy
TextField
:
text mouse key component action focus mouseMotion
Opis własno
ś
ci
Metoda
getPropertyDescriptors
podaje wszystkie własno
ś
ci klasy ziarna.
Np.
public void setPropertyName(PropertyType value);
public PropertyType getPropertyName();
public boolean isPropertyName();
PropertyDescriptor[] pd = bi.getPropertyDescriptors();
for (int i=0;i<pd.length;i++) System.out.print (pd[i].getName() + " ");
System.out.println ();
Wynik wykonania powy
ż
szego kodu dla klasy
TextField
:
selectionStart enabled text preferredSize
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 36
foreground visible background selectedText
echoCharacter font columns echoChar name
caretPosition selectionEnd minimumSize editable
Opis metod
Metoda
getMethodDescriptors
podaje wszystkie publiczne metody klasy
ziarna. Pozwala o wywoła
ć
te metody bez znajomo
ś
ci ich nazwy. Dla
ka
ż
dej metody pobieramy typy jej parametrów dzi
ę
ki metodzie
getParameterDescriptors
.
MethodDescriptor[] md = bi.getMethodDescriptors();
for (int i=0;i<md.length;i++) System.out.print (md[i].getName() + " ");
System.out.println ();
Wynik wykonania powy
ż
szego kodu dla klasy
TextField
to wydruk nazw
155 metod, w wi
ę
kszo
ś
ci dziedziczonych z klasy C
omponent.
Lista metod
obejmuje równie
ż
metody
add/removeListenerTypeListener
.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 37
Specyficzna klasa implementuj
ą
ca
BeanInfo
Definiuj
ą
c własn
ą
klas
ę
implementuj
ą
c
ą
BeanInfo
mo
ż
emy ograniczy
ć
elementy klasy ziarna udost
ę
pniane edytorom projektowym.
Dla klasy ziarna o przykładowej nazwie
SizedTextField
wła
ś
ciwa nazwa
klasy implementuj
ą
cej
BeanInfo
wynosi
SizedTextFieldBeanInfo
.
Np. gdy chcemy w trybie introspekcji udost
ę
pni
ć
jedynie jedn
ą
własno
ść
tej klasy - length - to zdefiniujemy nast
ę
puj
ą
c
ą
klas
ę
typu
BeanInfo
:
import java.beans.*;
public class SizedTextFieldBeanInfo extends SimpleBeanInfo {
private final static Class beanClass = SizedTextField.class;
public PropertyDescriptor[] getPropertyDescriptors() {
try {
PropertyDescriptor length = new PropertyDescriptor("length",
beanClass);
PropertyDescriptor [] rv = {length};
return rv;
} catch (IntrospectionException e) {
throw new Error(e.toString());
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 38
}
}
}
Teraz zamiast 17 własno
ś
ci klasy
TextField
, jedyn
ą
wy
ś
wietlan
ą
własno
ś
ci
ą
stworzonej klasy pochodnej b
ę
dzie nowa własno
ść
: length.
Oczywi
ś
cie w klasie
SizedTextField
nale
ż
y jeszcze zdefiniowa
ć
metody
set/getLength
. Jednak wszystkie własno
ś
ci i metody tej klasy s
ą
nadal
dost
ę
pne dla programu i mog
ą
by
ć
wywoływane w wykonywanym
programie. Równie
ż
mechanizm refleksji “widzi” wszystkie publiczne
metody.
Mo
ż
na przekaza
ć
do edytora projektu czytelniejsz
ą
nazw
ę
klasy. Np. dla
klasy
SizedTextField
czytelniejsze jest oddzielenie słów składowych:
public BeanDescriptor getBeanDescriptor() {
BeanDescriptor bd = new BeanDescriptor(beanClass);
bd.setDisplayName
("Sized Text Field")
;
return bd;
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 39
Mo
ż
na przekaza
ć
ikonk
ę
dla klasy ziarna, przy pomocy nast
ę
puj
ą
cej
metody dodanej do klasy implementuj
ą
cej
BeanInfo
:
public Image getIcon (int iconKind) {
if (iconKind == BeanInfo.ICON_COLOR_16x16) {
Image img = loadImage("sized.gif");
return img;
}
return null;
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 40
11.6 Przykład klasy ziarna
Klasy ziaren nie musz
ą
reprezentowa
ć
graficznych obiektów, chocia
ż
takie
jest ich typowe zastosowanie – w tej technologii stworzono pakiety AWT i
Swing dla
ś
rodowiska Java. Dobr
ą
reguł
ą
dla stwierdzenia, czy warto
implementowa
ć
klas
ę
jako ziarno, jest pozytywna odpowied
ź
na pytanie,
czy warto stosowa
ć
technik
ę
„drag-and-drop” do pochwycenia obiektu tej
klasy z zestawu komponentów i przeniesienia go do przestrzeni roboczej
projektu, podczas tworzenia aplikacji.
Klasa
Hire
, zrealizowana w technologii ziarna, symuluje minimalny zestaw
operacji dla zatrudnienia pracownika.
package hire;
import java.util.*;
public class HireEvent extends EventObject {
private long hireDate;
public HireEvent(Object source) {
super(source);
hireDate = System.currentTimeMillis();
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 41
}
public long getHireDate() {
return hireDate;
}
}
Obserwatorzy
zainteresowani
procesem
zatrudnienia
musz
ą
zaimplementowa
ć
odpowiedni interfejs i zarejestrowa
ć
si
ę
w celu
uzyskania powiadomie
ń
o zaj
ś
ciu zdarzenia:
package hire;
import hire.*;
public interface HireListener extends java.util.EventListener {
public abstract void hired(HireEvent e);
}
W klasie Hire zarz
ą
dzamy list
ą
obserwatorów:
package hire;
...
public class Hire implements Serializable {
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 42
...
private Vector hireListeners = new Vector();
private PropertyChangeSupport changes =
new PropertyChangeSupport(this);
public Hire() {
}
public synchronized void addHireListener(HireListener l) {
hireListeners.addElement(l);
}
public synchronized void removeHireListener(HireListener l) {
hireListeners.removeElement(l);
}
Powiadomienie obserwatorów nast
ę
puje dzi
ę
ki metodzie
notifyHired():
protected void notifyHired() {
Vector l;
HireEvent h = new HireEvent(this);
synchronized(this) {
l = (Vector)hireListeners.clone();
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 43
}
for (int i = 0; i < l.size(); i++) {
HireListener hl = (HireListener)l.elementAt(i);
hl.hired(h);
}
}
Własno
ść
salary
w klasie
Hire
jest zwi
ą
zana:
public void addPropertyChangeListener( PropertyChangeListener p) {
changes.addPropertyChangeListener(p);
}
public void removePropertyChangeListener( PropertyChangeListener p) {
changes.removePropertyChangeListener(p);
}
public void setSalary(float salary) {
float old = this.salary;
this.salary = salary;
changes.firePropertyChange("salary", new Float(old), new Float(salary));
}
public float getSalary() {
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 44
return salary;
}
Z klas
ą
ziarna zwi
ą
zana jest klasa implementuj
ą
ca
BeanInfo
– jak dot
ą
d
udost
ę
pniaj
ą
ca (publikuj
ą
ca) jedynie własno
ść
salary
:
package hire;
import java.beans.*;
public class HireBeanInfo extends SimpleBeanInfo {
public PropertyDescriptor[] getPropertyDescriptors() {
try {
PropertyDescriptor pd1 = new PropertyDescriptor("salary", Hire.class);
pd1.setBound(true);
return new PropertyDescriptor[] {pd1};
} catch (Exception e) {
return null;
}
}
}
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 45
11.7 BeanContext
Pakiet
java.beans.beancontext
zawiera
interfejsy
i
klasy
przeznaczone do grupowania ziaren w hierarchi
ę
i dodawanie ziaren
do kontenera:
•
Hierarchia ziaren mo
ż
e stanowi
ć
abstrakcyjne
ś
rodowisko
(kontekst)
funkcjonowania poszczególnych ziaren JavaBeans
•
Elementy maszyny wirtualnej Java wspomagaj
ą
dynamiczne
dodawanie dowolnych usług
do
ś
rodowiska JavaBeans.
•
JVM
udost
ę
pnia
ziarnom
mechanizmy
poszukiwania
i
wywoływania usług
w
ś
rodowisku JavaBeans.
BeanContext
- logiczne poł
ą
czenie ziaren w posta
ć
“kontekstu”, z
którym poszczególne ziarna mog
ą
si
ę
kontaktowa
ć
.
Dwa rodzaje kontekstu
BeanContext
:
- tylko zawieranie - interfejs
BeanContext
;
- zawieranie i usługi - interfejs
BeanContextServices.
11. JavaBeans
W. Kasprzak: Programowanie zdarzeniowe.
11- 46
Oba interfejsy posiadaj
ą
standardowe implementacje:
BeanContext
BeanContextSupport
BeanContextServices
BeanContextServicesSupport
Interfejs
BeanContextChild
umo
ż
liwia zagnie
ż
d
ż
oym ziarnom
JavaBeans komunikacj
ę
z obejmuj
ą
cym je kontekstem
BeanContext
.