views:

231

answers:

3

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...
}
+3  A: 

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
Actually, Java has. It's called a constructor ;-)
Joey
@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.
glowcoder
@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
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
@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
@csj Yes, but the arguments to the initialization statement would be the *capacity* and *load factor* of the HashMap: `new HashMap<String,String>(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
@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
@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
+1  A: 

You would need to wrap it like this

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

    // other initialization stuff unrelated to myMap
}


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

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

    return tempMap;
}

edit: fixed return type on second method

glowcoder
+1  A: 

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