Reputation: 118
I have an interface :
public interface I {
getBar();
}
I have 2 class : A
and B
implementing I
.
What I want to do is :
public void foo(List<I> items) {
for (I item : items){
if (item instanceof A) this.append((A) item);
if (item instanceof B) this.append((B) item);
}
}
Is there something as of java 8 that would allow me to not have that list of instanceof
and call it like this ?
items.foreach(this::append);
Note :
I could do something like item.add(this)
which would append itself to this
but then I would call a method on an object that modifies the parameter (and only it) but this is bad practice as stated by Robert C. Martin's "Clean Code" :
Arguments are most naturally interpreted as inputs to a function.
Anything that forces you to check the function signature is equivalent to a double-take. It’s a cognitive break and should be avoided. In the days before object oriented programming it was sometimes necessary to have output arguments. However, much of the need for output arguments disappears in OO languages
Upvotes: 0
Views: 96
Reputation: 3081
Actual answer: This is a double-dispatch problem and the Java-centric solution is the Visitor Pattern which is quite a lot of change.
Silly answer: Just for kicks, there's no instanceof
here:
private static final Map<Class<?>, Consumer<I>> DISPATCHER = Map.of(
A.class, this::appendA,
B.class, this::appendB
);
items.forEach(i -> DISPATCHER.get(i.getClass()).accept());
Yeah, it's still branchy and must run many more instructions to get the same result :)
Upvotes: 0
Reputation: 44090
You're thinking about it upside down.
Your class gets these instances of I
and has a different overloaded method based on the type. This overload means it needs to be aware of all implementations, to provide a different overload for each.
The problem, obviously, is that the list must grow as implementations are added.
The solution is to make the implementations of I
responsible for knowing how they are appended.
public interface I {
Bar getBar(); /* you missed the return type, but anyway */
void appendTo(SomeType foo);
}
Your method simply becomes
public void foo(List<I> items) {
for (I item : items){
item.appendTo(this); //or instead of this, some append-able instance
}
}
If someone adds a new implementation of I
, they are forced to write an append method as part of the interface's contract.
Upvotes: 0
Reputation: 8640
what if you take more OOP approach,
interface Appender{ void append(I item); }
for (I item : items){ appenders.getOrDefault(item.class,DEFAULT_APPENDER).append(item); }
Upvotes: 0
Reputation: 2357
just move the instanceof stuff to a generic append func
private void append(I item) {
if (item instanceof A) ...
if (item instanceof B) ...
}
then you can use
items.foreach(this::append);
Upvotes: 1