2

Here is an example of the issue I've come across:

public interface IFoo { ... }

public abstract class Helper implements IFoo {
  public Helper() { ... }

  protected abstract X helperMethod();
}    

public class Foo extends Helper {
  private final String aaa;

  @Inject
  public Foo(String aaa) { this.aaa = aaa; }

  @Override
  X helperMethod() { doSomethingUsingWhatsInjected(aaa); }
}

The issue is that when I bind IFoo to Foo like this:

bind(IFoo.class).to(Foo.class).in(Singleton.class);

it appears like helperMethod() is being called before the aaa has been Injected since I'm seeing aaa as null. But if I instead don't use the class Helper and in-line all of its code directly in Foo, guice doesn't struggle.

What's the difference between these two approaches? Why is helperMethod() called before we know from where we're getting the implementation of IFoo? Can we use Helper along with injection?

apolune
  • 447
  • 2
  • 8

1 Answers1

2

Are you sure you're not calling helperMethod from within Helper's constructor? You omitted that part from the code you posted, but it would match the behavior you're seeing.

public class Test {
  interface IFoo { }

  static abstract class Helper implements IFoo {
    Helper() { helperMethod(); }
    abstract void helperMethod();
  }

  static class Foo extends Helper {
    private final String aaa;

    Foo(String aaa) { this.aaa = aaa; }

    @Override
    void helperMethod() { System.out.println(String.valueOf(aaa)); }
  }

  public static void main(String[] args) {
    // Call helperMethod twice:
    // once in the Helper.Helper(), once right here.
    new Foo("expected").helperMethod();
    // output:
    //   null
    //   expected
  }
}

The first thing Foo does is implicitly call its superclass constructor, as if you typed super(); this necessarily happens as the very first statement in the subclass constructor. Consequently, this happens even before final variables like aaa are set, so your overridden method in Foo sees aaa as null. As in my example, this is not specific to Guice, but Guice injection can trigger the constructor just like anything else can.

This StackOverflow answer offers a more thorough discussion of this problem.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • You are exactly correct. I checked, and I *am* calling `helperMethod()` from within the constructor of `Helper`. I mistakenly omitted that detail when I wrote my example. This answers my question, thank you! – apolune Oct 01 '13 at 17:00