views:

300

answers:

7

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()

+1  A: 

Just call Init() at the start of foo()?

Maximilian Mayerl
obviously. the above example simplifies what i'm trying to do
Mike
A: 

Have a look at AspectJ. It will help you do what you are asking.

Chris Kaminski
+2  A: 

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()

Martin OConnor
+5  A: 

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.

Rorick
This example was very helpful. Thank you.
Mike
You are welcome)
Rorick
+1  A: 

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.

Yishai
A: 

I assume that the problem here is as follows:

  1. You have a constructor that can partially build the object, but can't completely build it because of the way the class must be constructed. (I can't think of an example offhand.)
  2. So you need an init() method that will finish construction.
  3. So you want to have some kind of guarantee that 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.

jprete
+1  A: 

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.

KitsuneYMG