47

I have a final non-static member:

private final HashMap<String,String> myMap;

I would like to initialize it using a method called by the constructor. Since myMap is final, my "helper" method is unable to initialize it directly. Of course I have options:

I could implement the myMap initialization code directly in the constructor.

MyConstructor (String someThingNecessary)
{
    myMap = new HashMap<String,String>();

    myMap.put("blah","blahblah");
    // etc...

    // other initialization stuff unrelated to myMap
}

I could have my helper method build the HashMap, return it to the constructor, and have the constructor then assign the object to myMap.

MyConstructor (String someThingNecessary)
{
    myMap = InitializeMyMap(someThingNecessary);

    // other initialization stuff unrelated to myMap
}

private HashMap<String,String> InitializeMyMap(String someThingNecessary)
{
    HashMap<String,String> initializedMap = new HashMap<String,String>();

    initializedMap.put("blah","blahblah");
    // etc...

    return initializedMap;
}

Method #2 is fine, however, I'm wondering if there's some way I could allow the helper method to directly manipulate myMap. Perhaps a modifier that indicates it can only be called by the constructor?

MyConstructor (String someThingNecessary)
{
    InitializeMyMap(someThingNecessary);

    // other initialization stuff unrelated to myMap
}


// helper doesn't work since it can't modify a final member
private void InitializeMyMap(String someThingNecessary)
{
    myMap = new HashMap<String,String>();

    myMap.put("blah","blahblah");
    // etc...
}
csj
  • 21,818
  • 2
  • 20
  • 26
  • Do you know what people answering your question will do? Yes, they will try it out by opening Eclipse and copy-pasting your code! :) – Simon May 18 '10 at 17:36
  • 8
    @Simon My question is requesting an alternative to what I have tried. I am wondering if someone with better knowledge of that language than I can guide me to a keyword or paradigm I am unaware of. – csj May 18 '10 at 17:38
  • 3
    the answer is No. Source: Java language specification 8.3.1.2 – mihi May 18 '10 at 17:42
  • ;)... np... but that's what I did ^^ – Simon May 18 '10 at 17:42
  • 2
    @Simon I guess I wasn't sure what you were getting at. I thought perhaps you were implying that I could have answered my own question by trying out my code in Eclipse. – csj May 18 '10 at 17:55

3 Answers3

18

Method #2 is your best option. The problem is that if you have an assignment in a private method there is nothing preventing other code in the class outside the constructor calling it, which would then create an issue with an attempted second assignment to the final field.

Java has no construct of a separate method that can only be called during construction.

For completeness, we can make a third option, where you assign the map at initialization and then have the helper method fill it:

 private final HashMap<String, String> myMap = new HashMap<String, String();

And then:

 MyConstructor (String someThingNecessary)
 {
    initializeMyMap(someThingNecessary);

    // other initialization stuff unrelated to myMap
 }


 // helper doesn't work since it can't modify a final member
 private void initializeMyMap(String someThingNecessary)
 {

     myMap.clear();
    myMap.put("blah","blahblah");
    // etc...
  }

And if you really want to be confusing you can use an initializer instead of a constructor, but you should not do that, so unless you really need to know, I won't expand on that.

Yishai
  • 90,445
  • 31
  • 189
  • 263
  • 6
    Actually, Java has. It's called a constructor ;-) – Joey May 18 '10 at 17:39
  • 1
    @Johannes no, he means there is no sub-constructor designation that would allow the assignment of final variables on the condition that the method could only be called from the constructor. – corsiKa May 18 '10 at 17:40
  • @Yishai. Thanks for the alternative. Also, regarding initializers, I don't think it would work anyway, since the HashMap contents are actually dependent on the argument given to the constructor. – csj May 18 '10 at 18:00
  • Perhaps I'm being dense, but I can't think of a reason not to initialize it when it's declared as Yishai suggests here, then just fill it from the helper. Maybe csj could elaborate? – Stephen P May 18 '10 at 18:03
  • @Stephan P To my knowledge, a Java initializer cannot be provided any arguments upon object instantiation. This is in contrast to constructors which readily accept arguments, and can utilize them for initialization. Since the contents of my HashMap are dependent upon my constructor parameter, I cannot see any way to utilize an initializer - which Yishai mentioned in passing, in the last sentence of this answer. – csj May 18 '10 at 18:10
  • @csj Yes, but the arguments to the initialization statement would be the *capacity* and *load factor* of the HashMap: `new HashMap(30, 0.75)` **not** the contents of the map: `myMap.put("blah", "blahblah")` which can still be loaded within the helper method, because the map contents are not 'final', only the map itself. – Stephen P May 18 '10 at 18:26
  • @Stephen P. Okay, I get what you're saying now. Is there an advantage to creating the object in an initializer versus the constructor, if in either case its content initializing method will be called from the constructor? – csj May 18 '10 at 20:01
  • @csj, well for this case it can help with the helper method. Otherwise it is just a matter of style. For example, you can be comfortable that no weird logic in the constructor left you with a null value (that doesn't apply to final, but in general). – Yishai May 18 '10 at 21:51
14

How about implementing a private constructor that initializes your HashMap, and then have your main constructor(s) call that private constructor?

For example--

// Helper function to initialize final HashMap.
private MyConstructor()
{
    myMap = new HashMap<String,String>();
    myMap.put("blah","blah");
}

MyConstructor (String someThingNecessary)
{
    // Initialize the HashMap.
    this();
    // Other initialization code can follow.
}

You can modify the signature of the private helper constructor as needed (e.g. to provide parameter data or to make the signature distinct from any public constructors).

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
cjerdonek
  • 5,814
  • 2
  • 32
  • 26
  • 1
    I think this solution is what @Joey was alluding to above when he said, "Actually, Java has. It's called a constructor," in response to Yishai's remark that "Java has no construct of a separate method that can only be called during construction." – cjerdonek Jul 04 '11 at 20:06
  • I kind of like this idea. Unfortunately, it prevents me from giving the method a sensible name (like, InitializeMyMap) that represents what it's initializing. Thank you for posting to an old question. I always appreciate new ideas, or clarification of old ones. – csj Jul 04 '11 at 23:48
  • Thanks, @csj. I was looking for an answer to the same question and thought of this. None of the previously proposed approaches would work very well for my use case of simultaneously initializing several final fields. As for the name, yes, it could be better. But at least it's consistent with constructors initializing something. :) I'm new to Java and wouldn't be surprised if there's an even better solution out there. – cjerdonek Jul 06 '11 at 21:57
  • This is a great answer. I have multiple constructors that all do very similar registration and data initialization. Each constructor is now a one-line call to a private constructor. All the differences come as parameters to the private constructor. This is much more maintainable! – Brent K. Jan 25 '21 at 15:12
  • This doesn't work because you can't have any other code before the call to this(); – bikeman868 Jan 12 '23 at 00:43
1

Option #2 is the most resuable option, because you can share it among all constructors. What we would need here, are collection initializers of c#. :)

(BTW: #3 won't compile)

Simon
  • 9,255
  • 4
  • 37
  • 54
  • Thanks for your help Simon. I knew from the get-go that #3 wouldn't compile. I was hoping there might be a way to make it legal by guaranteeing that the helper would only be callable from the constructor. It appears that this isn't possible. – csj May 18 '10 at 17:47