Java語言欠缺屬性、事件、多重繼承功能。所以,如果要在Java程序中實現一些面向對象編程的常見需求,只能手寫大量膠水代碼。Java Bean正是編寫這套膠水代碼的慣用模式或約定。這些約定包括getXxx、setXxx、isXxx、addXxxListener、XxxEvent等。遵守上述約定的類可以用於若干工具或庫。
舉個例子,假如有人要用Java實現一個單向鏈表類,可能會這樣寫:
// 編譯成 java-int-list_1.0.jar
public final class JavaIntList {
static class Node {
public Node next;
public int value;
}
public Node head;
public int size;
}
上述實現爲了能夠快速獲取鏈表的大小,把鏈表大小緩存在size變量中。用法如下:
JavaIntList myList = new JavaIntList();
System.out.println(myList.size);
JavaIntList的作者很滿意,於是開源了java-int-list庫的1.0版。文件名是java-int-list_1.0.jar。發佈後,吸引了許多用戶來使用java-int-list_1.0.jar。
有一天,作者決定要節省內存,不要緩存size變量了,把代碼改成這樣:
// 編譯成 java-int-list_2.0.jar
public final class JavaIntList {
static final class Node {
public Node next;
public int value;
}
public Node head;
public int getSize() {
Node n = head;
int i = 0;
while (n != null) {
n = n.next;
i++;
}
return i;
}
}
然後發佈了2.0版:java-int-list_2.0.jar。發佈後,原有java-int-list_1.0.jar的用戶紛紛升級版本到2.0。這些用戶一升級,就發現自己的程序全部壞掉了,說是找不到什麼size變量。於是這些用戶就把作者暴打一頓,再也不敢用java-int-list庫了。
這個故事告訴我們,如果不想被暴打致死,你就必須保持向後兼容性。太陽公司在設計Java語言時,也懂得這個道理。所以Java標準庫中,絕對不會出現public int size這樣的代碼,而一定會一開始就寫成:
private int size;
public int getSize() { return size; }
讓用戶一開始就使用getSize,以便有朝一日修改getSize實現時,不破壞向後兼容性。這種public int getSize() { return size; }的慣用手法,就是Java Bean。
現在是2014年,C#、Scala等比Java新的面嚮對象語言自身就提供了語言特性來實現這些常用需求,所以根本不需要Java Bean這樣繁瑣的約定。
比如,假如有個Scala版的ScalaIntList:
// 編譯成 scala-int-list_1.0.jar
object ScalaIntList {
final case class Node(next: Node, value: Int)
}
final class ScalaIntList {
var head: ScalaIntList.Node = null
var size: Int = 0
}
用戶這樣用:
val myList = new ScalaIntList
println(myList.size)
有一天你心血來潮改成這樣:
// 編譯成 scala-int-list_2.0.jar
object ScalaIntList {
final case class Node(next: Node, value: Int)
}
final class ScalaIntList {
var head: ScalaIntList.Node = null
final def size: Int = {
var n = head
var i = 0
while (n != null) {
n = n.next
i++
}
i
}
}
用戶還是照樣能用,根本不破壞向後兼容性。所以Scala程序只要不考慮和Java交互,一般就不需要類似Java Bean這樣的約定。
順便說一句,向後兼容性分爲源代碼級和二進制級,Scala的var或val改爲final def的話,無論源代碼級的向後兼容性,還是二進制級的向後兼容性,都不遭受破壞。但C#的字段改爲屬性的話,雖然不破壞源代碼級的向後兼容性,但是會破壞二進制級的向後兼容性。這是C#的設計缺陷,導致微軟的編碼規範不得不禁止使用公有字段。
總的來說:
1、所有屬性爲private
2、提供默認構造方法
3、提供getter和setter
4、實現serializable接口