3

How do I implement the following pseudocode in Java?

Object getInstance(Class<?> type)
{
  switch (type)
  {
    case A.class:
      return createA(param1, param2);
    case B.class:
      return createB(param3, param4, param5);
    default:
      throw new AssertionError("Unknown type: " + type);
  }
}

I know I can probably implement this using a Map<Class<?>, Callable<Object>> (mapping classes to a method that returns an object) but is there a more efficient/readable way to do this?

UPDATE: I'm sorry for the misleading pseudo code. I did not mean to imply that the classes have no-arg constructors. Each class is constructed differently. I know if-else works but it is not great from an efficiency point of view. It is O(n).

Gili
  • 86,244
  • 97
  • 390
  • 689
  • `switch` in Java is very limited. You can switch on Strings and numbers, that's it. I'm not sure I'd argue for using the (string) type name as the switch .. which leaves the alternative(s) of switching away from switch. – user2864740 Jan 15 '14 at 05:40
  • 1
    @user2864740 - switch on Enums as well. Can't do it on Objects in general though. – Krease Jan 15 '14 at 05:42
  • @Chris True, I still group them together with numbers in my head. I blame C# :> – user2864740 Jan 15 '14 at 05:43
  • Have a look here too http://stackoverflow.com/questions/5579309/switch-instanceof – Abs Jan 15 '14 at 05:48
  • I don't see the problem with switching on `type.getName()` ::shrug:: – Brian Roach Jan 15 '14 at 05:48
  • @JetAbe this is different. I'm asking for switch-on-a-class, not switch-on-an-object. I don't even have access to an object of that class. – Gili Jan 16 '14 at 17:29
  • 2
    @BrianRoach, it works but it's a maintenance nightmare. If a class is renamed, the code will break silently. Refactoring tools handle renaming classes, but not Strings referencing classes. Ideally, the code should follow class renames automatically. – Gili Jan 31 '14 at 15:03

4 Answers4

2

How about using the Class object to create a new instance?

private static final Set<Class> ALLOWED_CLASSES = 
    new HashSet<>(Arrays.asList(A.class, B.class));

Object getInstance(Class<?> type) {
    if (!ALLOWED_CLASSES.contains(type)) {
        throw new AssertionError("Unknown type: " + type);
    }
    return type.newInstance();
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
0

Instead of using switch, just use if.

if (type == A.class) {
    return new A();
} else if (type == B.class) {
    return new B();
} else {
    throw new AssertionError("Unknown type: " + type);
}

This way you can also handle more detailed cases in your if conditions, like subclasses, or interfaces, etc. It also allows your constructors to optionally contain other arguments (following a factory-like pattern), which becomes harder if you just throw them into a map or use reflection.

Krease
  • 15,805
  • 8
  • 54
  • 86
0

If all constructors invoked without parameters,

Object getInstance(Class<?> type)  {
  return type.newInstance();
}

Otherwise, if the number of variants is small, use if statements, otherwise use map as intended.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
0

If none of the calls to new require parameters, then you can use newInstance(). Then we just add some error condition code:

private static final List<Class> classes = Arrays.asList(A.class, B.class, ...);

public static Object getInstance(Class<?> type) {
    if (classes.contains(type))
        return type.newInstance();
    throw new AssertionError("Unknown type: " + type);
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Sorry for the misleading pseudocode. I didn't mean to imply that classes have no-arg constructors. Each type has its own construction mechanism (different parameters for different classes). Thanks anyway. – Gili Jan 16 '14 at 17:37