Context (Edit)
Some clarification was on demand, so I'll try to sum up what influences the question.
The goal of the project is to provide a certain functionality to programmers, most probably in the form of a library (a JAR with class files, I guess).
To use said functionality, programmers would have to conform to the constraints that must (should) be satisfied. Otherwise it won't function as expected (just like the locks from
java.util.concurrent
, that must be acquired / freed at the appropriate time and place).This code won't be the entry point to the applications using it (ie, has no
main
).There's a limited (and small) amount of operations exposed in the API.
Examples:
Think of a small game, where almost everything is implemented and managed by the already implemented classes. The only thing left for the programmer to do, is to write a method, or a couple of them, that describe what the character will do (walk, change direction, stop, inspect object). I would want to make sure that their methods (possibly marked with an annotation?) just
walk
, orchangeDirection
, or calculatediff = desiredValue - x
, and not, say, write to some file, or open a socket connection.Think of a transaction manager. The manager would be provided by this library, as well as some constant attributes of transactions (their isolation level, time-outs, ...). Now, the programmers would like to have transactions and use this manager. I would want to make sure that they only
read
,write
,commit
, orrollback
on some resources, known to the manager. I wouldn't want them tolaunchRocket
in the middle of the transaction, if the manager does not control any rockets to launch.
The Problem
I want to impose some invariants / restrictions / constraints on the body of a method (or group of methods), to be later implemented by some other programmer, in some other package/location. Say, I give them something along the lines of:
public abstract class ToBeExtended {
// some private stuff they should not modify
// ...
public abstract SomeReturnType safeMethod();
}
It is important (probably imperative), for the purposes of this project, that the method body satisfies some invariants. Or rather, it is imperative that the set of commands this method's implementation uses is limited. Examples of these constraints:
- This method must not perform any I/O.
- This method must not instantiate any unknown (potentially dangerous) objects.
- ...
Put another way:
- This method can call the methods of a known (specific) class.
- This method can execute some basic instructions (maths, assign local variables,
if
s, loops...).
I've been looking through Annotations, and there seems to be nothing close to this.
My options so far:
Define some annotation,
@SafeAnnotation
, and apply it to the method, defining a contract with the implementer, that he will follow the rules imposed, or else the system will malfunction.Define an
Enum
with the allowed operations. Instead of exposing the allowed methods, only a method is exposed, that accepts a list of these enum objects (or something similar to a Control Flow Graph?) and executes it, giving me the control of what can be done.
Example:
public enum AllowedOperations { OP1, OP2 }
public class TheOneKnown {
public void executeMyStuff (List<AllowedOperations> ops) {
// ...
}
}
My Question
Is there any feature in the language, such as annotations, reflection, or otherwise, allowing me to inspect (either at compile time or runtime) if a method is valid (ie, satisfies my constraints)?
Or rather, is there any way to enforce it to call only a limited set of other methods?
If not (and I think not), would this second approach be a suitable alternative?
Suitable, as in intuitive, well designed and/or good practice.
Update (Progress)
Having had a look at some related questions, I'm also considering (as a third option, maybe) following the steps given in the accepted answer of this question. Although, this may require some rethinking on the architecture.
The whole idea of using annotations to impose restrictions seems to require implementing my own annotation processor. If this is true, I might as well consider a small domain-specific language, so that the programmer would use these limited operations, later translating the code to Java. This way, I would also have control over what is specified.