3

I got a pretty simple question but couldn't find anything so far.

I'm trying to create two class constructors.
The first constructor gets 2 Strings and one HashMap and initializes the class variables.

public Foo(String a, String b, HashMap<String, String> c) {
    this.a = a;
    this.b = b;
    this.c = c;
}

The second constructor should only get the 2 Strings and create a "default"-HashMap.

Usually you just call this() with the default-value inside but I could not find a way to do this with a HashMap.

public Foo(String a, String b) {
    this(a, b, new HashMap<String, String>().put("x", "y").put("f","g"));
}

Eclipse marks an error:

Type mismatch: cannot convert from String to HashMap<String,String>

And otherwise the this()-call cannot be the first statement in the function.

public Foo(String a, String b) {
    HashMap<String, String> c = new HashMap<String, String>();
    c.put("x", "y");
    c.put("f", "g");
    this(a, b, c);
}

Any ideas how to solve this?

Worst case I had to duplicate the code, but I was wondering if there is no better way.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
John
  • 187
  • 1
  • 16
  • 2
    your reference to `c` will still be the same. you could simply use `this(a, b, new HashMap());` as your first statement and go on working with `c`. – SomeJavaGuy Nov 24 '15 at 13:44
  • 1
    Map's put returns current value (V). Hence you get the TypeMismatch. – Amit Dalal Nov 24 '15 at 13:48

5 Answers5

5

If this Map is a constant, you could store it as a constant and reuse it. This avoids recreation of the Map each time a new Foo is created but it is then shared across all Foos.

public class Foo {

    private static final Map<String, String> DEFAULT = new HashMap<>();

    static {
        DEFAULT.put("x", "y");
        DEFAULT.put("f","g");
    }

    public Foo(String a, String b) {
        this(a, b, DEFAULT);
    }

    public Foo(String a, String b, Map<String, String> c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

}

You could also create a static method returning the correct value. Note that the method needs to be static because you can't call an instance method inside this().

public class Foo {

    public Foo(String a, String b) {
        this(a, b, getDefaultMap());
    }

    public Foo(String a, String b, Map<String, String> c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    private static Map<String, String> getDefaultMap() {
        Map<String, String> map = new HashMap<>();
        map.put("x", "y");
        map.put("f", "g");
        return map;
    }

}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
4

It´s unneccessary to create a local variable of c.

You could simply call it like this.

public Foo(String a, String b) {
    this(a, b, new HashMap<String, String>());
    c.put("x", "y");
    c.put("f", "g");
}

Since your other constructor this(String, String, HashMap<String, String>()) will assign the new HashMap to c invoking methods on c will also fill this newly created HashMap and will assign the default values that you did provide to it.

Another solution could be to create a static method and just call it

public static HashMap<String, String> createDefaultHashMap() {
    HashMap<String, String> c = new HashMap<String, String>();
    c.put("x", "y");
    c.put("f", "g");
    return c;
}

public Foo(String a, String b) {
    this(a, b, Foo.createDefaultHashMap());;
}
SomeJavaGuy
  • 7,307
  • 2
  • 21
  • 33
1

The HashMap method .put returns a String so you are passing a String when calling the 3 argument constructor in your 2 argument constructor.

cfreih
  • 100
  • 4
  • 9
0

You can do a trick like this:

public Foo(String a, String b) {
    this(a, b, new HashMap<String, String>(){
       {
           put("x", "y");
           put("f", "g");
       }
    });
}

It creates anonymous class that inherits HashMap and defines init block with putting values.

Oleksandr Horobets
  • 1,335
  • 14
  • 26
  • 1
    This is called double-brace initialization but I'm not sure if I would recommend it. – Tunaki Nov 24 '15 at 13:56
  • Yes but do note that it creates an anonymous inner class. More info [here](http://stackoverflow.com/q/1958636/1743880). – Tunaki Nov 24 '15 at 14:00
0

You can work around the "call to this() must be the first statement" by adding a static method that creates your default Map instance:

public Foo(String a, String b) {
    this(a, b, createDefaultMap());
}

private static Map<String, String> createDefaultMap() {
    Map<String, String> defaultMap = new HashMap<String, String>();
    defaultMap.put("x", "y");
    defaultMap.put("f", "g");

    return defaultMap;
}

It is mandatory that createDefaultMap() is static, as you may not access the half-constructed instance at this point.

Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43