0

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

5
  • 3
    Why do you have append(A) and append(B) at all? The design looks quite iffy to begin with. Commented Jul 29, 2020 at 10:22
  • Imo the method should be append(I) and then inside of it you perform different things based on type. Commented Jul 29, 2020 at 10:25
  • @Kayaman : To be short, I am appending line of a flat file with tons of rules to an object. ILine is the interface. Each line is an object of A or B (or C,D,E...). @Amongalen : change append(I) to add(I) and you have what's in the note. Commented Jul 29, 2020 at 10:38
  • does append(A) and append(B) much different? Commented Jul 29, 2020 at 10:50
  • @user902383 Yes. Commented Jul 29, 2020 at 11:11

4 Answers 4

1

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);
Sign up to request clarification or add additional context in comments.

3 Comments

It is what I ended doing did but it is not that different. I still have that list of isntanceof. I will edit my question to be more clear about that. Thanks anyway.
@Degravef and you'll have those around because of your design. Now you're just playing with where to put them. To get rid of them fully, you would need a bigger design change.
I took this project from an ex-coworker. He made the spring batch reader return a list of ILine's consisting of differents objects of class implementing ILine. I found it weird from the time I saw it but he usually did a good job and was more experienced than I am so I kept it. Now deadline is too close to change it. I will go with this answer.
0

what if you take more OOP approach,

  1. define Appender
interface Appender{
  void append(I item);
}
  1. create your appenders for A B and default appender that do nothing
  2. put appenders into map Map<Class,Appender>
  3. change your code to
 for (I item : items){
     appenders.getOrDefault(item.class,DEFAULT_APPENDER).append(item);
 }

Comments

0

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.

Comments

0

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 :)

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.