1

I have a problem with Java static initialization. What I want to do is some type checking with generic constants and to translate between types and type names. So I have some typed constants in interface MC and a hash map in inner class Type to translate names to types. Now when I call

MC.Type.getValue("MInteger")
the inner class Type is initialized but not the static constants in the outer class MC so the return value is null. How can I get Java to initialize these constants? I could do

static { Type<?> dummy = MC.MBoolean; }

in class Type but isn't there some better way to do this. Or am I doing this totally wrong.

import java.util.HashMap;
import java.util.Map;

interface MC {
    public static final Type<Boolean> MBoolean = new Type<>("MBoolean");
    public static final Type<Integer> MInteger = new Type<>("MInteger");

    public static class Type<T> {
        private static final Map<String, Type<?>> types = new HashMap<>();
        private final String name;

        private Type(String name) {
            this.name = name;
            types.put(name, this);
        }

        public String getName() {
            return name;
        }

        public static Type<?> getValue(String name) {
            return types.get(name);
        }
    }
}

public class Main {

    public static void main(String[] args) {
        System.out.println(MC.Type.getValue("MInteger"));
        MC.MBoolean.getName();
        System.out.println(MC.Type.getValue("MInteger"));
    }
}

3 Answers 3

1

Since all Type instances are included in your MC class, a very direct approach to solving this problem would be to move registration of the class with the Type.types map from the constructor of Type to its static initializer:

private static final Map<String, Type<?>> types = new HashMap<>();
static {
    types.put(MBoolean.getName(), MBoolean);
    types.put(MInteger.getName(), MInteger);
}
private Type(String name) {
    this.name = name;
    // removed types.put(name, this); from here
}

Demo.

Sign up to request clarification or add additional context in comments.

Comments

1

You can use either static initializer block:

private static final Map<String, Type<?>> types = new HashMap<>();

static {
    types.put(MC.MBoolean.getName(), MC.MBoolean);
    types.put(MC.MInteger.getName(), MC.MInteger);
}

or double brace initialization:

private static final Map<String, Type<?>> types = new HashMap<>() {{
    put(MC.MBoolean.getName(), MC.MBoolean);
    put(MC.MInteger.getName(), MC.MInteger);
}};

First curly braces creates new anonymous subclass of HashMap, second curly braces are instance initializer block which is executed at construction time (arg-less constructor for anonymous classes).

5 Comments

Creating anonymous classes in order to initialize a hashmap is horrible...
@Absurd-Mind why do you think so? In my opinion it is more concise, than spliting hashmap creation and initilazation via static initializer, and so more readable.
Because it creates classes which can mess up stuff in debugging and serializing. Additionally they add computation time if used in tight loops and use extra memory if you have many of them (especially evil in 1.7 and earlier). Also this is likely misunderstood by novice programmers which reduces maintainability. This can be easily confused with syntactic sugar what it definitely is not. Luckily java 9 adds a method which makes this obsolete. In short the downsides are not worth the benefits.
@Absurd-Mind still don't find any problem with this. Anonymous classes became standard since Java 8 (lambdas). Creation of anonymous classes is fast enought to be used also in loops (everything is optimized by JIT compiler). Extra memory? devices nowadays has enough memory. In programming, the main goal has to be readability, then you can start by optmization if needed. "Premature optimization is the root of all evil" Donald Knuth.
@matoni with that attitude all software will become bloatware. Why creating more objects (adding up load to GC, cluttering JVM's space, generally increasing the complexity) when you have a simple, elegant, standard static initialization block ? In this sample the data types used are pretty simple, so probably it won't matter, but it's the principle that's wrong. True premature optimization is bad, but overengineering is even worse.
0

The Constructor won't initialize unless you explicitly call MC.MBoolean. so better you go with the Double brace initialization.

private static final Map<String, Type<?>> types = new HashMap<>() {
            {
                put(MC.MBoolean.getName(), MC.MBoolean);
                put(MC.MInteger.getName(), MC.MInteger);
            }
        };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.