0

Let's say I've got a generic java class Filter<InputType, OutputType> which receives an input object and transforms it to an output object.

Now I've got two other classes (NoInputFilter<OutputType>, NoOutputFilter<InputType>) which should do the same except they haven't an InputType respectively an OutputType.

Now the three classes have some functionality in common. The Filter class should handle the input object the same way like the NoOutputFilter does, and it should handle the output object the same way like the NoInputFilter does.

So I've got something like this in terms of functionality, where B is Filter:

Three sets

Now, Java doesn't allow to inherit from multiple classes. Is there a design pattern to do something like this?

My first approach was to have two interfaces. One interface for the input, one interface for the output. The Filter class implements both. But this way I have to implement the interfaces two times each.

Then I thought about to give the Filter class (which implements the two interfaces) instances of NoOutputFilter and NoInputFilter and just call the right method instead of have duplicated code. But this breaks the semantics, has a lot of boilerplate code, and isn't a very clean approach in my opinion. Here a little demonstration:

class Filter<InputType, OutputType> implements INoOutputFilter<InputType>, INoInputFilter<OutputType> {
  private final INoOutputFilter<InputType> noOutputFilter;
  private final INoInputFilter<OutputType> noInputFilter;

  public void InputType setInput(InputType input) {
     noOutputFilter.setInput(input);
  }

  public OutputType getOutput() {
     noInputFilter.getOutput();
  }

  [...]
}

The last idea was to turn the class hierarchy up site down:

public class Filter<InputType, OutputType>
public class NoInputFilter<OutputType> extends Filter <Void, OutputType>
public class NoOutputFilter<InputType> extends Filter <InputType, Void>

This way, the classes behaves the right way, no duplicated code, but e.g. the NoOutputFilter has a public Void getOutput() method. Even though you can not instantiate something from Void, the API of those two classes are wrong. This would be something like this:

enter image description here

So this isn't a nice approach as well.

Do you have any hint how I could solve this in Java? Is there a design pattern for this?

3
  • 2
    Just to be clear, when you talk about composition, the "a lot of boilerplate code" are the two lines defining and initializing the inner filters, or there is something else? Because for your problem definition, the standard answer is usually "composition" which is the second solution you describe.
    – SJuan76
    Sep 19, 2015 at 18:47
  • Note that in your example I would change the name of the interfaces to avoid them being negative; for example INoOutputFilter would become InputFilter. Then the NoOutputFilter class would implement only InputFilter
    – SJuan76
    Sep 19, 2015 at 18:50
  • @SJuan76 the two methods were only for presentation of the problem. There are some methods more. But maybe I should think about composition again. I gonna change the negative names. Thank you.
    – Obenland
    Sep 19, 2015 at 19:50

3 Answers 3

0

First of all, Java 8 introduced Default Methods, which is in fact multi-inheritance in Java.

History showed that complicated inheritance structure is cancer. You should favor composition over inheritance. In this case you are mixing input filter with output filter to create artificial bi-directional filter parent. Not clean for me at all.

I don't have your full picture, but from what you described, I would consider two interfaces: InputFilter and OutputFilter. Eventual Bi-directional filter implementation would implement both interfaces.

And if there would be some common functionality, I would extract that common logic into separate class and use it as dependency.

5
  • I'm new to Java 8 but I us it already. So Default Methods sounds great and the right thing for my problem. Although I should rethink about composition. Maybe it has more benefits for me than I thought. Thank you!
    – Obenland
    Sep 19, 2015 at 19:58
  • 1
    From its beginning Java has made illegal multiple inheritance due to the troubles it causes; in fact it is very cleary stated that default methods introduction is only as a "last-measure" to introduce new logic into old API interfaces. Telling anyone "if you want multi-inheritance use default methods" is very bad advice.
    – SJuan76
    Sep 19, 2015 at 20:23
  • 1
    @SJuan76 You probably didn't read my answer, because I corrected him about non-existence of multi-inheriantce in Java, but at the same time suggested to consider composition over inheritance.
    – luboskrnac
    Sep 19, 2015 at 20:26
  • @SJuan76 Thanks for stating the correct idea/usage of Default Methods. You are right, it shouldn't be used here. So I gonna use composition pattern like you and luboskrnac said.
    – Obenland
    Sep 19, 2015 at 22:01
  • 1
    Java always had multiple inheritance of type (via interfaces). With introduction of default methods (to implement functional interfaces) it added multiple inheritance of behavior. It never had and will never have multiple inheritance of state.
    – PM 77-1
    Sep 19, 2015 at 23:01
1

I think your approach is wrong. You are looking at it in terms of classes instead of results. What you - in my opinion - should do is something like this:

  • Define an input interface
  • Define an output interface
  • Define a base class implementing both, with nullable protected properties
  • Define a concrete class for NoInput that exposes the output stuff
  • Define a concrete class for Filter that exposes both.

That's it.

A

3
  • Sounds like composition, right?
    – Obenland
    Sep 19, 2015 at 19:51
  • Mmmmmmh... you can use composition if you want, i.e. use one class for input, one for output and then a holder class with nullable properties. That is, in a way, probably better but it always depends on the use case. Interfaces are mostly a good thing if you think that one day you will have wildly different things connected to each other by some form of contract. Otherwise, they are just a check box item. I did suggest the interfaces because I think that it is likely that he/she will have this need at some point, just not now. Sep 19, 2015 at 19:56
  • At this point in time, he can well do with a base class and refactor later if needed. But that's why I suggested the interfaces: so that if/when a need arises to do that, he's all setup already. Sep 19, 2015 at 19:57
0

Have you considered this?

public interface Input<InputType>   { ... }

public interface Output<OutputType> { ... }

public interface Filter<InputType,OutputType>
    extends Input<InputType>, Output<OutputType> { }

To give a more general-purpose answer to your question:

Multi-inheritance in Java

(Please note that this is somewhat experimental, and I have not ran it by other experienced java developers to see what they think about it. Perhaps I will receive some feedback here.)

The way to achieve multi-inheritance in Java is by means of interfaces. If you have class A extending classes X and Y then you need to begin by defining interfaces for X and Y, so that A can implement both interfaces. The question now becomes, once you have all this in place, how can you avoid code duplication. Prior to Java 8, this was a mess. With Java 8, this becomes considerably easier and cleaner. (Still not as clean as I would like it to be, but at least manageable.)

Whenever I create an interface in Java 8 which is bound to be implemented in various different ways, I add Decorator and Defaults sub-interfaces to it. So, I build something like this:

public interface Zork
{
    boolean foo();
    boolean bar( int a );

    interface Defaults extends Zork
    {
        @Override
        default boolean foo()
        {
            return bar( 0 );
        }
    }

    interface Decorator extends Defaults
    {
        Zork getDecoratedZork();

        @Override
        default boolean bar( int a )
        {
            Zork decoree = getDecoratedZork();
            return decoree.bar( a );
        }
    }
}

This way:

  • A class wishing to provide an implementation of Zork can be declared with implements Zork.Defaults and refrain from having to implement methods for which defaults have been provided.

  • A class wishing to act as a decorator of Zork can be declared with implements Zork.Decorator, supply an implementation for the getDecoratedZork(), and implement whatever subset of Zork is necessary, allowing the rest of the methods to be delegated to the decorated Zork.

  • A class wishing to multiply inherit Zork can be declared as follows:

(Note that since Zork is an interface, the MultipleInheritor class can implement many other interfaces like Zork.)

public class MultipleInheritor implements Zork.Decorator
{
    private final Zork myZork = new Zork.Defaults()
    {
        @Override
        public boolean bar( int a )
        {
            return a % 2 == 0;
        }
    }

    @Override
    public Zork getDecoratedZork()
    {
        return myZork;
    }
}
3
  • Why don't you add the default-methods directly to the base-interface? Is there any advantage I overlooked in the added verbosity and the extra-interface? Sep 20, 2015 at 17:06
  • @Deduplicator I do not remember right now the major reason why I do it. If and when I remember, I will add a comment here. In the mean time, one reason (which is probably not the main reason) is because in theory, an implementor which needs to override all methods may mistakenly omit overriding one of them, and never find out, because a default was provided. By not including default methods in the base interface I reserve the option of implementing the interface in its pure form, and if I ever forget to implement one of the methods, I will be told so by the compiler.
    – Mike Nakis
    Sep 20, 2015 at 17:23
  • @Deduplicator An example of an implementor which may need to override all methods is the decorator, though in practice I have all of my decorators extend the Defaults interface.
    – Mike Nakis
    Sep 20, 2015 at 17:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.