Reputation: 25686
First, a clarification: By "interface", I mean any point of interaction between components, not necessarily the "abstract type" kind of interface. (Although if I can use such an interface to do this, great.)
I'm writing a class which is designed to be extended. The problem domain is global optimization, so any end-user-programmer who subclasses my class (I'll call them Steve) will be providing their own problem and algorithm to solve it; I just provide the framework (networking, user interface, etc.). Steve's program might require any number of configuration settings; I want my code to be able to get those settings from the user the same way it gets its own settings (be that from a GUI control panel, an XML configuration file, or whatever). Steve should not have to duplicate any of my code, nor should he have to break Java's type safety or encapsulation model.
Ideally, Steve would be able to write something along these lines:
public class SteveClass extends MyFrameworkClass {
@Configurable
private int foo;
@Configurable
private String bar;
private boolean baz;
// implementations of my abstract methods, etc.
}
Configurable
is an annotation that I would provide, that indicates that the annotated variable is a configuration setting that I should get from the user. As you can see, I also want Steve to be able to declare other instance variables for his own internal use, that I never touch.
I was going to implement this by including a method in MyFrameworkClass
that would iterate through getClass().getDeclaredFields()
and identify the fields that have Configurable
annotations. It would then writes the user input to those fields. It doesn't have to look exactly like this, with annotations and all, but you get the idea.
The problem with this, of course, is that foo
and bar
are private and code in MyFrameworkClass
can't write to them, even reflectively. It wouldn't help if they were package-private, since Steve's code won't be in the same package as mine, and I'd rather not require him to make them public, or to add any other interface that would allow anyone but me to access the fields.
Is there any way to do what I want, either by fiddling with SecurityManager
or doing something completely different?
Upvotes: 1
Views: 214
Reputation: 137787
If I was in your position, I'd be trying to find a way to use the Spring framework to do this for me. That would then let users write this:
@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;
}
All you'd have to do then is to build a context that has those properties available within it; that's pretty trivial. (Spring does poke around behind the security scenes, but it does so in the “correct” way; all you need to do is to ensure that Spring is trusted, and the rest of your code doesn't need nearly the same level of permissions.) Of course, it also gives Steve plenty of other tools to help him build his app, but that's a story for another question.
Upvotes: 2
Reputation: 10891
Create a Configuration interface
interface Configuration
{
int foo ( ) ;
String bar ( ) ;
}
Use the Configuration interface
public class SteveClass extends MyFrameworkClass {
private Configuration configuration ;
// implementations of my abstract methods, etc.
}
I think this is called delegation.
Upvotes: 0
Reputation: 43436
You can use Class.getDeclaredFields
to get all of the class's declared fields (including non-public ones), and then Field.setAccessible(true)
to gain access to it.
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"
But that means you're depending on the SecurityManager to allow you to do that. It does by default, but that may not work for all users. If that's a prohibitive requirement, there are other, less convenient but potentially more robust options (like requiring that the constructor take a Configuration object, which the user can then get values from).
Upvotes: 1