我正在用这样的类编写一个API:
public class NKMPMission {
private String name;
private int age;
public NKMPMission(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
我的问题:
- 如何确保此
NKMPMission
类的用户仅访问 getter? - 如何为此函数引入 setter,以便我作为开发人员可以设置,但用户无法设置?
常用方法是根本不公开该类,而只是向该类公开接口。一个面向公众的界面和一个面向开发人员的界面。
然后,您需要一个工厂来创建它们。
/**
* Expose this interface to the public.
*/
public interface INKMPMission {
public String getName();
public int getAge();
}
/**
* Only expose this interface to developers.
*/
interface IDeveloperNKMPMission {
public void setName(String name);
public void setAge(int age);
}
public static class NKMPMissionFactory {
/**
* Expose only the INKMPMission construction.
*/
public INKMPMission make(String name, int age) {
return new NKMPMission(name, age);
}
/**
* Protected version for developers.
*/
IDeveloperNKMPMission forDeveloper(INKMPMission it) {
return IDeveloperNKMPMission.class.cast(it);
}
/**
* Private so no-one outside the factory knows about the inner workings.
*/
private static class NKMPMission implements INKMPMission, IDeveloperNKMPMission {
private String name;
private int age;
private NKMPMission(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String getName() {
return name;
}
@Override
public int getAge() {
return age;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setAge(int age) {
this.age = age;
}
}
}
对于真正的偏执狂,您甚至可以使用代理。这将使通过反射使用二传手变得困难(但并非不可能(。
/**
* Expose only the INKMPMission construction.
*/
public INKMPMission make(String name, int age) {
return new NKMPMissionProxy(new NKMPMission(name, age));
}
/**
* Protected version for developers.
*/
protected IDeveloperNKMPMission forDeveloper(INKMPMission it) {
if (it instanceof NKMPMissionProxy) {
it = ((NKMPMissionProxy) it).theMission;
}
return IDeveloperNKMPMission.class.cast(it);
}
/**
* A proxy for the truly paranoid - makes using reflection more difficult (but not impossible)
*/
private static class NKMPMissionProxy implements INKMPMission {
private final NKMPMission theMission;
private NKMPMissionProxy(NKMPMission theMission) {
this.theMission = theMission;
}
@Override
public String getName() {
return theMission.getName();
}
@Override
public int getAge() {
return theMission.getAge();
}
}
1(我如何确保此NKMPMIssion类的用户只能访问getter。
你不能。
2(我如何为这个函数引入setter,以便作为开发人员我应该能够设置,但用户不应该能够设置。
听起来你正在编写一个API。如果从该 API 的公共方法返回NKMPMIssion
实例,则可以调用资源库。即使你将它们标记为private
或protected
,它们仍然可以通过反射来调用。也就是说,通常使它们不public
就足够了。它至少会说"如果你打电话给这些,你就处于不受支持的领域。
如果要使其更难,可以返回一个实例,该实例将外观包裹在NKMPMIssion
实例周围。但这只会使它变得更加困难,并非不可能,因为立面实例必须具有对NKMPMIssion
实例的引用,该实例(即使它是private
(可以通过反射访问。
最简单的方法是让 API 仅使用接口,并使类成为实现细节。
public interface INKMPMission {
String getName();
int getAge();
}
public class SomeService{
private class MyNKMPMission implements INKMPMission {
//put getters and setters here
}
public List<INKMPMission> getMissions(){
//put some MyNKMPMissions in a list
}
}
由于MyNKMPMission是私有的,因此消费者将永远无法进行向下投射并访问设置器。
(以某种方式(,但你不应该那样做:
- 在每个资源库中构造一个新的异常
- 检查生成的堆栈跟踪
- 如果调用者类不在你的包中(或硬编码一些直接类名/方法名(,抛出一个非法访问错误
这种方式既不漂亮,也不快,因为您必须检查对二传手的每一次访问。
另一种方法是使用@CallerSensitive
注释,尽管它是专有的API,因此并非在所有plattforms/jre实现上都可用:https://stackoverflow.com/a/22627383/1164913
干净且在大多数情况下足够的方法是使用仅向客户端提供 getter 的接口,并将其返回给客户端。
您似乎正在尝试编写 API。假设用户是指使用您的 API 的开发人员。
在这种情况下,将 setter 设为protected
并构建一些有意义的包结构,以便只有子成员和包成员才能看到。
如果你想保护它,即使是孩子的,除了把它保密之外别无
回答问题:
问题 1.你不能像T.J.克劳德在他的回答中所说的那样。
问题 2.我建议您按照以下顺序从最简单到最难尝试以下方法,并采取您认为最合适的努力回报选项:
- 看看:Java 访问修饰符。 创建一个接口,
- 并将该接口与公共方法公开给"NKMPMission"类的"最终"用户,如 Esben Skov Pedersen 答案中所述
- 最后你可以做OldCurmudgeon答案中提到的代理方法
我会做什么:
如果你的类集要在内部使用,那么我会选择选项 1 与良好的 javadoc 和项目标准相结合,这应该就足够了。
如果您正在创建公共 API,我会选择选项 2。
恕我直言,在这种情况下,选项 3 增加了不必要的复杂性,并且几乎没有什么好处,因为无论如何都可以访问每个类方法或属性抛出反射(正如许多人提到的(。我想每个人都知道这样一个事实,即对 API 隐藏方法的访问抛出反射是黑客的、危险的,并且不方便项目的管理,因为 API 提供者有权更改隐藏方法的实现,而无需进一步通知最终用户。