首先澄清一下:我所说的"接口"是指组件之间的任何交互点,不一定是"抽象类型"那种接口。(虽然如果我可以使用这样的接口来做到这一点,那就太好了。)
我正在编写一个被设计为可扩展的类。问题域是全局优化,因此任何继承我的类的最终用户程序员(我将称他们为Steve)将提供他们自己的问题和算法来解决它;我只是提供了框架(网络、用户界面等)。史蒂夫的程序可能需要任意数量的配置设置;我希望我的代码能够从用户那里获得这些设置,就像它获得自己的设置一样(从GUI控制面板、XML配置文件或其他)。Steve不应该复制我的任何代码,也不应该破坏Java的类型安全或封装模型。
理想情况下,Steve可以这样写:
public class SteveClass extends MyFrameworkClass {
@Configurable
private int foo;
@Configurable
private String bar;
private boolean baz;
// implementations of my abstract methods, etc.
}
Configurable
是我将提供的一个注释,它表明注释的变量是我应该从用户那里获得的配置设置。如您所见,我还希望Steve能够声明其他实例变量供他自己内部使用,我永远不会碰它们。
我将通过在MyFrameworkClass
中包含一个方法来实现这一点,该方法将迭代getClass().getDeclaredFields()
并识别具有Configurable
注释的字段。然后,它将用户输入写入这些字段。它不需要看起来完全像这样,有注释和所有的,但是你知道的。
foo
和bar
是私有的,MyFrameworkClass
中的代码不能写入它们,即使是反射。如果它们是包私有的,也没有帮助,因为Steve的代码不会和我的在同一个包中,我宁愿不要求他将它们公开,或者添加任何其他接口,允许除了我以外的任何人访问这些字段。
有什么方法可以做我想要的,无论是通过摆弄SecurityManager
还是做一些完全不同的事情?
如果我在您的位置,我会试图找到一种方法来使用Spring框架来为我做这件事。这样用户就可以这样写:
@Component
public class SteveClass {
@Value("${steve.foo}")
private int foo;
@Value("${steve.bar}")
private String bar;
private boolean baz;
@Autowired(required = false)
private DaveClass dave;
}
你所要做的就是建立一个上下文,其中有这些属性可用;这是微不足道的。(Spring确实会在安全场景背后摸索,但它是以"正确"的方式这样做的;您所需要做的就是确保Spring是受信任的,并且您的其余代码几乎不需要相同级别的权限。)当然,它也给了史蒂夫很多其他的工具来帮助他建立自己的应用程序,但这是另一个问题的故事。
您可以使用Class.getDeclaredFields
来获取所有类的声明字段(包括非公共字段),然后使用Field.setAccessible(true)
来访问它。
Field foo = HasPrivate.class.getDeclaredField("foo");
foo.setAccessible(true);
SteveClass steve = new SteveClass();
System.out.println(steve); // "0" -- I set up SteveClass.toString() to just print foo
foo.setInt(hp, 1234);
System.out.println(steve); // "1234"
但这意味着你依赖于SecurityManager来允许你这样做。默认情况下是这样的,但这可能不适用于所有用户。如果这是一个令人望而却步的要求,还有其他不太方便但可能更健壮的选项(比如要求构造函数接受一个Configuration对象,然后用户可以从中获取值)。
-
创建Configuration接口
interface Configuration { int foo ( ) ; String bar ( ) ; }
-
使用Configuration界面
public class SteveClass extends MyFrameworkClass { private Configuration configuration ; // implementations of my abstract methods, etc. }
我想这就是所谓的委托