PropertyDescriptor クラスのソースを見れば、このクラスがかなり複雑な構成を していることが分かります。PropertyDescriptor クラスは、 FeatureDescriptor クラスを継承しており、あるプロパティが「名前」や「値」を 持つという基本的な性質は、このFeatureDescriptor クラスによって担われている ことが分かります。
public class PropertyDescriptor extends FeatureDescriptor { ........ private Class propertyType; // Propertyの属するクラス private Method readMethod; // Propertyを読み出すメソッド private Method writeMethod; // Propertyを書き出すメソッド private boolean bound; // Bound Propertyか? private boolean constrained; // Constrained Propertyか? private Class propertyEditorClass; // Propertyを編集するエディタのクラス }
public class FeatureDescriptor { ........ private boolean expert; // Featureは "expert" か? private boolean hidden; // Featureは "hidden" か? private String shortDescription; // Featureの簡単な説明 private String name; // Featureの「名前」 private String displayName; // Featureのローカルな表示名 private java.util.Hashtable table; // Featureの「値」の格納場所 }
PropertyDescriptorクラスで拡張されたフィールドを見れば、BeansのPropertyは、 「名前」と「値」を持つだけでなく、どのようなクラスの値を持つのか、また、どの ようなメソッドによって読み書きされるのか、どのような種類(Bound、Constrained) のPropertyなのか、そして、それを編集するエディタのクラスの情報までも含んだ ものであることが分かります。
PropertyDescriptorのコンストラクタの型を見てみましょう。 いずれのコンストラクタも IntrospectionExceptionをthrowする可能性を持っている ことが分かります。もう一つ大事なことは、先にも触れましたが、Beansでは、 setter/getterと呼ばれる Propertyの値を読み書きするメソッドが、Propertyそのもの の一部を構成しているということです。
public PropertyDescriptor(String propertyName, Class beanClass) throws IntrospectionException; public PropertyDescriptor(String propertyName, Class beanClass, String getterName, String setterName) throws IntrospectionException ; public PropertyDescriptor(String propertyName, Method getter, Method setter) throws IntrospectionException ;
次のリストは、PropertyDescriptorの第一のコンストラクタのソースです。 このコンストラクタは、Propertyの名前とbeanクラスを与えると、そのクラスの PropertyDescriptorを生成します。この時、次のようなNaming Ruleが使われているの が分かります。今、fooというbeanクラスのbarという名前のPropertyの PropertyDescriptorを作るものとします。コンストラクタ new PropertyDescriptor("bar", foo); は、Introspectionを用いて、まず、クラス fooの中に、"setBar"という名前のメソッドがないかを調べ、あれば、それをsetter メソッドとします。setterの引数が boolean型だったら、今度は、"isBar"という名前の getterメソッドを探します。そうでなければ、"getBar"という名前のgetterを探します。いずれの検索でも、求めるメソッドが見つからなかったら、IntrospectionExceptionが 返されます。このリストで、"isBar"へのintrospectionは、exceptionがキャッチされて いるのは何故か考えて見て下さい。
public PropertyDescriptor(String propertyName, Class beanClass) throws IntrospectionException { setName(propertyName); String base = capitalize(propertyName); writeMethod = Introspector.findMethod(beanClass, "set" + base, 1); // If it's a boolean property check for an "isFoo" first. if (writeMethod.getParameterTypes()[0] == Boolean.TYPE) { try { readMethod = Introspector.findMethod(banClass, "is" + base, 0); } catch (Exception ex) { } } if (readMethod == null) { readMethod = Introspector.findMethod(beanClass, "get" + base, 0); } findPropertyType(); }
PropertyDescriptorがどのように生成されるかという考察は、「Beansで Propertyを 定義するというのはどういうことなのか」という問いに、はっきりとした回答を与え ます。少し意外なことに、あるbeanクラスがPropertyの名前とPropertyTypeが等しい フィールドを持つことは、beanのPropertyにとって必要なことではありません。 肝心なことは、ある特定の型と名前をもつメソッドが存在することです。 bean クラス A で、プロパティ xyz を定義するということは、クラスAの定義の中に、 setXyzという名前のメソッド、あるいは、getXyz(または isXyz)という名前 のメソッドを定義しておくことなのです。 この点での詳しいアルゴリズムは、java.beans.Introspectorクラスの、 getTargetPropertyInfo()メソッドのソースをよく読んでみてください。