manabreak manabreak - 4 months ago 9
Java Question

Way to handle two-dimensional conditions when creating objects?

I have two variables,

, that determine which kind of object I need to create. At the moment, both of these can hold two different values, and the objects are created as such:

Result createResult(int foo, int bar) {
if(foo == 0) {
if(bar == 0) return new FirstResult();
if(bar == 1) return new SecondResult();
}else if(foo == 1) {
if(bar == 0) return new ThirdResult();
if(bar == 1) return new FourthResult();
return null;

// Common interface for all the objects
interface Result {


This work for just these four types, but what if there's more types? How should the object creation be handled so that it would be more efficient?


The if/else pattern should be very efficient up to a considerable size. However if things get really huge this changes and also readability suffers. I suggest a HashMap-Lookup for the correct constructor for faster result determination. This requires that you create a class that can be used as a key in a HashMap:

class CreationParams {
    private final int foo;
    private final int bar;

    CreationParams(final int foo, final int bar) { = foo; = bar;

    // make sure to implement hashCode & equals so this class can be efficiently used in a Map

As already mentioned in the comment above, you absolutely need to implement hashCode and equals. If you do not implement equals or hashCode it will not work as the hashmap lookup will fail if the key is not the same (even if equal).

Now you can easily map a combination of parameters to a constructor and lookup the result. Consider this example:

class ResultFactory {
    private static final Map<CreationParams, Supplier<Result>> factories = createFactoryMap();

    private static Map<CreationParams, Supplier<Result>> createFactoryMap() {
        final Map<CreationParams, Supplier<Result>> result = new HashMap<>();
        result.put(new CreationParams(0, 0), FirstResult::new);
        result.put(new CreationParams(0, 1), SecondResult::new);
        // ...
        return result;

    Result createResult(int foo, int bar) {
        return factories.get(new CreationParams(foo, bar)).get();

The point is that all your constructors (well delegators to the constructor) are now saved in the map factories. The lookup factories.get(new CreationParams(foo, bar)) will be faster then lots of if statements as soon as some critical size is reached because it does not need to iterate all possible target objects, but only those with a colliding hash. Then you can immediately call get or in the real world you may want to check for null and throw some kind of exception in this case before.

Without Java 8

If your are stuck on an older Java version your have basically two possible workarounds. In both cases you will need to create your own Supplier interface like this (well technically you don't need the interface for the reflection variant because it only requires a single implementing class that you could also use directly):

interface Supplier {
    Result get();

Then one way is to use reflection which needs less source code:

class ReflectionSupplier implements Supplier {
    final Class<? extends Result> clazz;

    ReflectionSupplier(final Class<? extends Result> clazz) {
        this.clazz = clazz;

    public Result get() {
        try {
            return clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new Error(e);

Now you can add classes to the Map as follows:

result.put(new CreationParams(0, 0), new ReflectionSupplier(FirstResult.class));

The other alternative is to just use (anonymous) classes for each instance. The advantage is that there is a bunch of errors which can be compile time discovered (such as no default constructor available). The disadvantage is that this creates loads of lines. Just add to your map like this:

result.put(new CreationParams(0, 0), new Supplier() {
    public Result get() {
        return new FirstResult();