J2SE 5.0의 JavaBeans 컴포넌트 API에 여러 다양한 기능들이 추가되었는데, IndexedPropertyChangeEvent에 대한 지원도 그 중 하나이다. JavaBeans 컴포넌트 API는 JavaBeans 컴포넌트(JavaBean 또는 그냥 '빈'이라고도 한다)의 Regular Property에 대한 변경 내용을 보고하는 수단을 제공했었는데, IndexedPropertyChangeEvent에 대한 지원으로 빈의 Indexed Property 변경에 관한 추가 정보를 보고하는 기능이 부가되었다.
대부분의 개발자들은 JavaBean 컴포넌트 속성에 관해 익숙한 편이다. 즉, 각자의 클래스에 set과 get 메소드를 간단히 추가하기만 하면 set과 get 다음의 이름에 의해 정의되는 읽기/쓰기 속성을 설정할 수 있다. 다시 말해, 클래스가 setName() 및 getName()으로 명명된 메소드를 가지고 있다면 이 클래스는 name이라는 이름의 JavaBean 컴포넌트 속성을 가지게 된다.
컴포넌트 속성에는 Regular와 Indexed의 두 종류가 있는데, Indexed Property는 각각의 값이 Index에 의해 액세스되는 복수의 값을 가진다는 점에서 Regular Property와 구분된다. 한편 name과 같은 Regular Property를 위한 set과 get 메소드는 다음과 같은 형태를 띠게 된다.
Regular Property:
public void setName(String name)
public String getName()
name이 Indexed Property인 경우에 메소드는 다음과 같다.
Indexed Property:
public void setName(int index, String name)
public String getName(int index)
public void setName(String[] names)
public String[] getName()
빈은 속성값의 변경사항이 사용자에게 통지되도록 설계될 수 있으며, addPropertyChangeListener() 메소드를 이용하여 PropertyChangeListener 오브젝트를 빈에 등록할 수도 있다. 이 경우 PropertyChangeEvent 오브젝트나 IndexedPropertyChangeEvent 오브젝트를 통해 리스너에 변경 사항이 통지된다. 값 변경 시 PropertyChangeEvent를 생성하는 속성을 Bound Property라고 한다.
PropertyChangeListener 클래스는 다음과 같이 하나의 메소드를 가진다.
public void propertyChange(PropertyChangeEvent pce)
그렇다면 propertyChange()에 대한 인자가 PropertyChangeEvent 타입인 경우에는 IndexedPropertyChangeEvent를 어떻게 통지받게 되는가? 그 대답은 IndexedPropertyChangeEvent 클래스가 PropertyChangeEvent의 서브클래스라는 사실에서 찾아볼 수 있다. 따라서, propertyChange()의 내부에서는 어떤 타입의 인자를 얻는지 확인하기 위한 instanceof 체크가 필요하다.
public void propertyChange(PropertyChangeEvent pce) {
String name = pce.getPropertyName();
if (pce instanceof IndexedPropertyChangeEvent) {
IndexedPropertyChangeEvent ipce =
(IndexedPropertyChangeEvent) pce;
int index = ipce.getIndex();
System.out.println("Property: " + name + "; index: "
+ index);
} else {
System.out.println("Property: " + name);
}
System.out.println("; value: " + pce.getNewValue());
}
IndexedPropertyChangeEvent를 사용하는 예제 프로그램을 살펴보기에 앞서 Bound Indexed Property에 대한 변경을 보고하는 방법에 초점을 맞추어 보도록 하자. 다음은 앞서 살펴본 Name Indexed Propterty의 업데이트를 보고하는 코드이다.
private PropertyChangeSupport changeSupport;
public ClassConstructor() {
changeSupport = new PropertyChangeSupport(this);
}
public void setName(int index, String name) {
String oldName = this.name;
this.name = name;
changeSupport.fireIndexedPropertyChange("name", index,
oldName, name);
}
PropertyChangeSupport 클래스는 java.beans 패키지(JavaBeans 컴포넌트 API 패키지)에 들어 있는 지원 클래스이고, fireIndexedPropertyChange() 메소드는 Bound Indexed Property(이 경우에는 name)에 대한 변경을 addPropertyChangeListener() 메소드를 통해 등록된 모든 리스너에게 보고한다.
다음은 전체 예제의 내용이다.
import java.beans.*;
import java.util.*;
public class IndexedSampleBean {
private PropertyChangeSupport changeSupport;
private Map<Integer, String> names;
private String title;
public IndexedSampleBean() {
changeSupport = new PropertyChangeSupport(this);
names = new HashMap<Integer, String>();
}
public void setTitle(String title) {
String oldTitle = this.title;
this.title = title;
changeSupport.firePropertyChange("title", oldTitle, title);
}
public String getTitle() {
return title;
}
public void setName(int index, String name) {
String oldName = names.get(index);
names.put(index, name);
changeSupport.fireIndexedPropertyChange("name", index,
oldName, name);
}
public String getName(int index) {
return names.get(index);
}
public void addPropertyChangeListener(
PropertyChangeListener l) {
changeSupport.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(
PropertyChangeListener l) {
changeSupport.removePropertyChangeListener(l);
}
public static void main(String[] args) throws Exception {
IndexedSampleBean bean = new IndexedSampleBean();
PropertyChangeListener listener =
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent pce) {
String name = pce.getPropertyName();
if (pce instanceof IndexedPropertyChangeEvent) {
IndexedPropertyChangeEvent ipce =
(IndexedPropertyChangeEvent) pce;
int index = ipce.getIndex();
System.out.print("Property: " + name +
"; index: " + index);
} else {
System.out.print("Property: " + name);
}
System.out.println("; value: " + pce.getNewValue());
}
};
bean.addPropertyChangeListener(listener);
bean.setName(1, "John");
bean.setName(2, "Ed");
bean.setName(3, "Mary");
bean.setName(4, "Joan");
bean.setTitle("Captain");
System.out.println("Name at 3 is: " + bean.getName(3));
System.out.println("Title is: " + bean.getTitle());
}
}
클래스는 name으로 명명된 Indexed Property를 정의하고 4개의 위치에서 name을 설정한다. 이와 함께 title로 명명된 Regular Bound Property 역시 생성되어 설정된다. 리스너는 이름이나 타이틀을 설정하고 이어서 속성 이름, 인덱스(해당될 경우), 값을 프린트하기 위한 각 호출을 통지받는다. 그러면 클래스는 특정 위치에서 이름을 얻고 단일 타이틀을 프린트하기 전에 이를 먼저 프린트한다.
프로그램을 실행하면 다음과 같은 결과가 나온다.
>java IndexedSample
Property: name index: 1 value: John
Property: name index: 2 value: Ed
Property: name index: 3 value: Mary
Property: name index: 4 value: Joan
Property: title; value: Captain
Name at 3 is: Mary
Title is: Captain