views:

283

answers:

5

I see many Java examples using dependency injection with private fields without a public setter like this:

public SomeClass {
  @Inject
  private SomeResource resource;
}

But that is a bad idea when the injection should be performed manually for example in unit tests.

There are several possibilities to solve this:

  • add a public setter: setSomeResource(SomeResource r)
  • make the field public
  • make the field package protected

I'd like to avoid the setter, since nothing really happens in it. So I'd prefer public or package protected. What do you recommend?

+4  A: 

I recommend using setter. In this question are the benefits of using getters and setters.

Daniel Moura
+5  A: 

I prefer the setter

  • it is easier to debug (put a breakpoint in a setter rather than on field access / modification)
  • easier to log
  • easier to add some validation (although this is not always the best place)
  • easier to support bidirectional maintainance (though IOC container can take care of that)
  • any other "manual AOP" purpose

But that's just my opinion

Ehrann Mehdan
These are good arguments. I will use setter or constructor injection (available since EJB 3.1) now.
deamon
Would make the object mutable which might be a problem
willcodejavaforfood
+6  A: 

One way to avoid creating a setter for the field is using constructor injection. This even allows you to declare the field as final.

It goes like this:

public class SomeClass {
    private final SomeResource resource;

    @Inject
    public SomeClass(SomeResource resource) {
        this.resource = resource;
    }
}
Andre Rodrigues
+1  A: 

Adding setters is not an optimal solution, since you are adding production code which is not needed.

An alternative is to use Spring's ReflectionTestUtils class to inject your test dependencies using reflection, see http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html

Adriaan Koster
+1  A: 

With the help of the answer to my (related to this one) question:

http://stackoverflow.com/questions/2579258/how-do-app-servers-inject-into-private-fields

I coded this simple example on how to inject without setters. Perhaps it helps

//......................................................
import java.lang.annotation.*;
import java.lang.reflect.*;

//......................................................
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Inject {
}

//......................................................
class MyClass {

    @Inject
    private int theValue = 0;

    public int getTheValue() {
        return theValue;
    }
} // class

//......................................................
public class Example {

    //......................................................
    private static void doTheInjection(MyClass u, int value) throws IllegalAccessException {

        Field[] camps = u.getClass().getDeclaredFields();

        System.out.println("------- fields : --------");
        for (Field f : camps) {
            System.out.println(" -> " + f.toString());
            Annotation an = f.getAnnotation(Inject.class);
            if (an != null) {
                System.out.println("       found annotation: " + an.toString());
                System.out.println("       injecting !");
                f.setAccessible(true);
                f.set(u, value);
                f.setAccessible(false);
            }
        }

    } // ()

    //......................................................
    public static void main(String[] args) throws Exception {

        MyClass u = new MyClass();

        doTheInjection(u, 23);

        System.out.println(u.getTheValue());

    } // main ()
} // class

Run output:

------- fields : --------
 -> private int MyClass.theValue
       found annotation: @Inject()
       injecting !
23
cibercitizen1
Thanks for the suggestion. You could use @Inject from JSR 330.
deamon