Reputation: 97
I have an arraylist of non-primitive objects where each object is an instantiation of one of 10 different classes.
I would like to iterate through the list and for each object in the list invoke a method based on the class of the object.
Having gone through posts I see suggestions using casting;if/else; and instanceof that works. However with large number of classes I have I was wondering if there is a more elegant and terse solution.
Any input appreciated.
Upvotes: 2
Views: 3140
Reputation: 3273
The approach I use most often for this type of dispatch in Java is to map class names to their respective handlers.
Base Handler (can be class or interface according to your needs):
interface Handler {
// Can have a return value if you need it
public void handle(Object k);
}
Example Implementation for String:
class StringHandler implements Handler {
public void handle(Object k) {
if (!(k instanceof String))
throw new RuntimeException("You messed up your dispatcher!!!");
String string = (String) k;
System.out.println(string);
}
}
// During initialization, wire up the dispatcher
Map<String, Handler> dispatcher = new HashMap<>{{
put("classname", new HandlerForClassName());
// more mappings...
}};
// Later
List<Object> objects = new ArrayList<>(); // Whatever you're trying to loop over
for (Object k : objects)
if (k != null)
dispatcher.get(k.getClass()).handle(k);
To explain the last line, you're looking up the classname of k
with the dispatcher to get the handler you wired it to. Then you're calling handle(Object)
on that handler and passing it k
. If you had wired String
to StringHandler
and k
was a string, the value of k
would be printed to stdout.
The code that actually cares about dispatching the objects to their handlers never has to change to accommodate more handlers/object types.
Note that all of the caveats of double brace initialization apply here. The short version is don't pass it around willy-nilly. Or you can just do it the long way.
Also note that this kind of runtime dispatch allows for a lot of flexibility, including modification on-the-fly.
// You can do this at any time
dispatcher.put("classname", new DifferentDispatcher());
Upvotes: 0
Reputation: 2102
There is also another approach with inheritance. You can make all the classes extend a Parent class like below given your classes don't already extend anything else:
public class Parent{
public void doSomething(){
//do generic stuff here
}
}
Then override it in the child class if needed:
public class Child extends Parent{
public void doSomething(){
//do child specific if needed else don't redefine if Parent method is okay for the child.
}
}
Once all list items extend this class, each can call doSomething() to do the job. (Iterate over the collection of Parent items.)
Upvotes: 0
Reputation: 17985
I'd indeed avoid such chains of instanceof
-checks. In fact, I'd avoid chains of if
/else
or switch
in general (exceptions apply).
What we often have, if we have a chain of instanceof
-checks is misplaced responsibility and a lack of polymorphism.
Imagine the following code:
class Type1 {}
class Type2 {}
class Type3 {}
class TypeProcessor {
public void processObjets() {
final List<Object> objects = getObjectList();
for (final Object o : objects) {
if (o instanceof Type1) {
processType1((Type1) o);
} else if (o instanceof Type2) {
processType2((Type2) o);
} else if (o instanceof Type2) {
processType2((Type2) o);
}
}
}
public void processType1(final Type1 o) {
/* Process for Type 1. */
}
public void processType2(final Type2 o) {
/* Process for Type 2. */
}
public void processType3(final Type3 o) {
/* Process for Type 3. */
}
}
This can be refactored like this:
interface Type {
void process();
}
class Type1 implements Type {
public void process() {
/* Process for Type 1. */
}
}
class Type2 implements Type {
public void process() {
/* Process for Type 2. */
}
}
class Type3 implements Type {
public void process() {
/* Process for Type 3. */
}
}
class TypeProcessor {
public void processObjects() {
final List<Type> objects = getObjectList();
for (final Type o : objects) {
o.process();
}
}
}
Upvotes: 8