Reputation: 60831
I'm wondering if there is a way to specify that a method gets called in advance of a class method. I know something like this should be posssible, since JUnit has before(), what I want to do is similar.
Here is a concrete example of what I'd like to do
class A {
public void init(int a) {
System.out.println(a);
}
@magic(arg=1)
public void foo() {
//
}
public static void main() {
A a = new A();
a.foo();
}
}
//Output: 1
Basically I want an annotation to tell either the compiler or the jvm call init() before foo()
Upvotes: 6
Views: 10313
Reputation: 12901
Why are you doing this? Are you attempting to avoid having a constructor with many arguments (using setters then calling init) or are you avoiding having many constructors that all have similar arguments? If this is the case, you can use a builder pattern.
public class Foo {
int a, b, c, d, e;
Foo(int a, int b, int c, int d, int e) { this.a=a; /*etc*/ }
}
public class FooBuilder {
int a,b,c,d,e;
FooBuilder A(int a) { this.a=a; return this;}
FooBuilder B(int b) { this.b=b; return this;}
//etc
Foo create(){ return new Foo(a,b,c,d,e);
}
If this doesn't work, I'd suggest looking into AOP. I'd mark the methods that must have init() called already with an annotation [perhaps @requires('init') or the like] and make you AOP framework insert the proper code. Be careful that multiple init's either don't have side effects or that you do proper synchronization on your has_init_been_called state.
Upvotes: 3
Reputation: 3759
I assume that the problem here is as follows:
init()
method that will finish construction.init()
will be called right after the constructor.My suggestion is to use a factory object or method. The simplest way is to make the constructor private, add a construct()
method with the parameters of the constructor or something of that sort, and then have the construct()
method both create the object and call init()
, then return it.
Upvotes: 0
Reputation: 91931
There is no direct way to do this in the java language. What you are seeing in JUnit is the framework making a decision about how to run the methods by calling the methods annotated with @Before first. It is very easy to find annotated methods and run them, but that is the responsibility of the caller.
The problem you present is too simple to know the right way to a solution. AspectJ does address this need by manipulating the byte code (essentially calling the init() method when foo() is called by changing the bytecode to make that happen), but I can't imagine introducing that as a hack around a problem.
If you can present an interface or a wrapper object to this class, you could do it that way. But I would suggest you post the ugly hack that got you into this situation in the first place in a separate question, and then post how your current hack solution requires that method calls be intercepted and why that is the case, and if there are better workarounds. That way we can help address the underlying need better.
Upvotes: 1
Reputation: 8953
If you have interface A
you can wrap instances of this interface with Proxy
and inside invoke
method of its InvocationHandler
you are free to check whether method is annotated and perform some actions depending on that:
class Initalizer implements InvocationHandler {
private A delegate;
Initializer(A delegate) {
this.delegate = delegate;
}
public Object invoke(Object proxy, Method method, Object[] args) {
if (method.isAnnotationPresent(magic.class)) {
magic annotation = method.getAnnotation(magic.class);
delegate.init(magic.arg);
}
method.invoke(delegate, args);
}
}
A realA = ...;
A obj = Proxy.newProxyInstance(A.class.getClassLoader(), new Class[] {A.class}, new Initializer(realA));
Or you can try using "before" advice of AspectJ. It will be something like the next:
@Aspect
public class Initializer {
@Before("@annotation(your.package.magic) && target(obj) && @annotation(annotation)")
private void initialize(A obj, magic annotation) {
a.init(annotation.arg);
}
}
I'm not sure that snippets are working, they just illustrate idea.
Upvotes: 10
Reputation: 3593
AOP does this with what are known as pointcuts AspectJ might have what you need.
Simplistically speaking, you would add before advice to your foo() method which would call init()
Upvotes: 2