我建議你不要使課堂public但公開構造函數並讓人們使用您的類實現的公共接口。將API啓動爲公共接口(也許是一些公共抽象類)並隱藏實現類是一個好主意,不要將它們標記爲公共,以便可以隨時改變它們。然後你可以在你的包中提供一個公共工廠方法,它實例化你的包私有類並將它們作爲接口類型返回。這是一個公共的接口:
package stackoverflow;
public interface Widget {
public void doWidgetWork(String work);
}
這裏是「package private」的實現。編譯器不會讓代碼相同的包導入之外,也使用這個類在所有:
package stackoverflow;
/*package*/ class WidgetHidden implements Widget {
public WidgetHidden(String configOptionA, String configOptionB){
// ...
}
public WidgetHidden(){
// ...
}
public void doWidgetWork(String work)[
// ...
}
}
通知有該字/ 包第二次出現的/是註釋(這是不合法的在Java在那裏使用那個詞),但是很多程序員在這個位置使用這樣的評論來向人們表明,這個類不是公開的,它意味着開發者真的打算讓這個班級故意「封裝私人」。爲了讓人們實例從你的包之外你提供一個靜態工廠類的類(其他實例工廠類):
package stackoverflow;
public class WidgetFactory {
public static Widget newInstance(String configOptionA, String configOptionB) {
return new Widget(String configOptionA, String configOptionB);
}
}
工廠類的全部意義在於,它隱藏了您的內部類(那些你隱藏爲私人包)。隨着時間的推移,您可以更改您的工廠類以返回新類或重命名或刪除WidgetHidden類。
許多框架通過將其放入名爲「內部」的包中來指示其他開發人員不應該使用哪些類。公共接口將位於主包(例如「com.stackoverflow.widget」)中,而隱藏類僅包含在公開的工廠類(例如「com.stackoverflow.widget.internal」)中的隱藏類。
該主題的變體是對工廠類不使用靜態方法;使其成爲一種常規方法。取決於方法是否是靜態的,這些替代品被稱爲「靜態工廠」或「實例工廠」。不使這個方法成爲靜態的,對於使用你的包的人來說似乎更多的工作,因爲他們首先必須在使用它來創建Widget之前實例化你的工廠對象。哪裏是有幫助的是,當人們可能要設置一些默認設置在工廠的構造函數的所有部件,然後使用無靜態的newInstance方法來指定任何超出默認值:
public class WidgetInstanceFactory {
private String defaultOptionA = null;
public WidgetInstanceFactory(String defaultOptionA) {
this.defaultOptionA = defaultOptionA;
}
public Widget newInstance(String optionB) {
return new WidgetHidden(this.defaultOptionA, optionB);
}
}
它可以避開包私有使用反射保護以查找和調用構造函數。 Spring框架的一個非常好的功能是,它將實例化即使沒有工廠類也不公開的類(儘管提供Spring很樂意使用的工廠類也更有禮貌)。下面的代碼將工作:
package stackoverflow.other;
class TestInstantiate {
private Widget myWidget = null;
public TestInstantiate(){
this.myWidget = instantiatePackagePrivateClass("stackoverflow.WidgetHidden");
}
private Widget instantiatePackagePrivateClass(String className)
throws ClassNotFoundException, NoSuchMethodException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
@SuppressWarnings("unchecked")
Class<FileUploadSequence> clazz = (Class<Widget>) Class.forName(className);
Constructor<Widget> constructor = clazz.getConstructor(new Class[]{});
constructor.setAccessible(true);
Widget widget = (Widget) constructor.newInstance((Object[])null);
return widget;
}
}
在那個例子中我使用了無參數的構造函數,但顯然你可以找到並使用相同的方法調用兩個字符串構造函數。很明顯,這樣的代碼繞過了編寫WidgetHidden的程序員的意圖;他們想隱藏它,因爲它們很可能會改變它。任何使用這樣的後門來操縱包私人對象的人都應該知道,WidgetHidden類不是他們正在使用的框架的公共API的一部分,因此可能在沒有事先通知的情況下被刪除或更改,你正在使用的軟件包。將它重命名爲WidgetInternal並將其放入「內部」包中,使得您更多地告訴人們「不要使用」。 JVM具有可選的安全設置,可以防止人們使用這些技巧;但運行JVM的人必須對其進行外部配置,以便禁用此類技巧,這些技巧僅在您希望運行別人不信任的代碼時纔有用,並且可以防止此類技巧出現。
Josha Block 2nd Edition的Effective Java書有很多討論,例子以及嘗試編寫一個好的API時的陷阱細節。它有很多細節來解釋爲什麼你總是應該儘可能多地隱藏許多類,以及其他許多好的「交易技巧」。
是的,但目標是與私人,我可以得到它的工作方式,但它不是我想要的。我想用私人範圍來限制範圍。 – hhh 2010-05-03 02:15:53
你想限制哪個範圍?構造函數的?類'?如果你想要一個私有的構造函數,你將無法實例化這個類。通常,你想要一個私有構造函數的唯一場景是一個Factory類。 – 2010-05-03 02:18:13
「同一個包」意思是「在文件開頭有'導入pkg_name'還是需要同一個目錄? – hhh 2010-05-03 02:29:55