私有成員,即私有方法、域和類型這些概念的幕后思想是它們只是實(shí)現(xiàn)細(xì)節(jié):一個(gè)類的實(shí)現(xiàn)者可以隨意地添加一個(gè)新的私有成員,或者修改和移除一個(gè)舊的私有成員,而不需要擔(dān)心對(duì)該類的客戶造成任何損害。換句話說(shuō),私有成員被包含它們的類完全封裝了。
遺憾的是,在這種嚴(yán)密的盔甲保護(hù)中仍然存在細(xì)小的裂縫。例如,序列化就可以打破這種封裝。如果使一個(gè)類成為可序列化的,并且接受缺省的序列化形式,那么該類的私有實(shí)例域?qū)⒊蔀槠鋵?dǎo)出API的一部分[EJ Item 54,55]。當(dāng)客戶正在使用現(xiàn)有的被序列化對(duì)象時(shí),對(duì)私有表示的修改將會(huì)導(dǎo)致異常或者是錯(cuò)誤的行為。
但是編譯期的錯(cuò)誤又會(huì)怎么樣呢?你能否寫出一個(gè)final的“庫(kù)”類和“客戶”類,這兩者都可以毫無(wú)問(wèn)題地通過(guò)編譯,然后在庫(kù)類中添加一個(gè)私有成員,使得庫(kù)類仍然能夠編譯,而客戶類卻再也不能編譯了?
如果你的解謎方案是要對(duì)庫(kù)類添加一個(gè)私有構(gòu)造器,以抑制通過(guò)缺省的公共構(gòu)造器而創(chuàng)建實(shí)例的行為,那么你只是一知半解。本謎題要求你添加一個(gè)私有成員,嚴(yán)格地講,構(gòu)造器不是成員[JLS 6.4.3]。
本謎題有數(shù)個(gè)解謎方案,其中一個(gè)是使用遮蔽:
package library;
public final class Api {
// private static class String{ }
public static String newString() {
return new String();
}
}
package client;
import library.Api;
public class Client {
String s = Api.newString();
}
如上編寫,該程序就可以毫無(wú)問(wèn)題地通過(guò)編譯。如果我們不注釋掉library.Api中的局部類String的私有聲明,那么Api.newString方法就再也不會(huì)返回java.lang.String類型了,因此變量Client.s的初始化將不能通過(guò)編譯:
client/Client.java:4: incompatible types
found: library.Api.String, required: java.lang.String
String s = Api.newString();
^
盡管我們所做的文本修改僅僅是添加了一個(gè)私有類聲明,但是我們間接地修改了一個(gè)現(xiàn)有公共方法的返回類型,而這是一個(gè)不兼容的API修改,因?yàn)槲覀冃薷牧艘粋€(gè)被導(dǎo)出API所使用的名字的含義。
遺憾的是,在這種嚴(yán)密的盔甲保護(hù)中仍然存在細(xì)小的裂縫。例如,序列化就可以打破這種封裝。如果使一個(gè)類成為可序列化的,并且接受缺省的序列化形式,那么該類的私有實(shí)例域?qū)⒊蔀槠鋵?dǎo)出API的一部分[EJ Item 54,55]。當(dāng)客戶正在使用現(xiàn)有的被序列化對(duì)象時(shí),對(duì)私有表示的修改將會(huì)導(dǎo)致異常或者是錯(cuò)誤的行為。
但是編譯期的錯(cuò)誤又會(huì)怎么樣呢?你能否寫出一個(gè)final的“庫(kù)”類和“客戶”類,這兩者都可以毫無(wú)問(wèn)題地通過(guò)編譯,然后在庫(kù)類中添加一個(gè)私有成員,使得庫(kù)類仍然能夠編譯,而客戶類卻再也不能編譯了?
如果你的解謎方案是要對(duì)庫(kù)類添加一個(gè)私有構(gòu)造器,以抑制通過(guò)缺省的公共構(gòu)造器而創(chuàng)建實(shí)例的行為,那么你只是一知半解。本謎題要求你添加一個(gè)私有成員,嚴(yán)格地講,構(gòu)造器不是成員[JLS 6.4.3]。
本謎題有數(shù)個(gè)解謎方案,其中一個(gè)是使用遮蔽:
package library;
public final class Api {
// private static class String{ }
public static String newString() {
return new String();
}
}
package client;
import library.Api;
public class Client {
String s = Api.newString();
}
如上編寫,該程序就可以毫無(wú)問(wèn)題地通過(guò)編譯。如果我們不注釋掉library.Api中的局部類String的私有聲明,那么Api.newString方法就再也不會(huì)返回java.lang.String類型了,因此變量Client.s的初始化將不能通過(guò)編譯:
client/Client.java:4: incompatible types
found: library.Api.String, required: java.lang.String
String s = Api.newString();
^
盡管我們所做的文本修改僅僅是添加了一個(gè)私有類聲明,但是我們間接地修改了一個(gè)現(xiàn)有公共方法的返回類型,而這是一個(gè)不兼容的API修改,因?yàn)槲覀冃薷牧艘粋€(gè)被導(dǎo)出API所使用的名字的含義。