Radu Ionescu
Radu Ionescu

Reputation: 3532

Stream reduce operation using interface

I have the following structure

public interface ICommon{
   ICommon add(ICommon other);
}

public class Foo implements ICommon{
    ...

    ICommon add(ICommon other){
        return new Bar().add(other);
    }

}

public class Bar implements ICommon{
    ...

    ICommon add(ICommon other){
        ...
    }

}

As part of a composite pattern.

I wanted to use the streams reduce operation, but somehow I can not force the type inference to the interface. I am using this.

List<Foo> list;
list.stream().reduce( new Foo(), (a,b) -> a.add(b));

I am getting an error that ICommon can not be cast to Foo.

I have tried to force cast on parameters but with no success.

Upvotes: 2

Views: 253

Answers (3)

Holger
Holger

Reputation: 298123

There is the three-arg version of reduce allowing to change the element type when performing the reduction:

list.stream().reduce(new Foo(), ICommon::add, ICommon::add);

While a reference to the same method is used here, the second argument is a function (BiFunction) with an (ICommon,Foo) -> ICommon signature, whilst the third argument is a function (BinaryOperator) with an (ICommon,ICommon) -> ICommon signature.

Another option is to do a type safe change of the existing List’s type:

Collections.<ICommon>unmodifiableList(list).stream().reduce(new Foo(), ICommon::add);

Since immutable lists can guaranty to return values of the super type of the actual element type, while preventing the inserting of new elements, this wrapper allows to change the element type to a super type. Further, since stream operations are read-only operations, the wrapper redirects the stream() invocation to the original list, just returning it as a stream of the supertype. So there is no performance difference to using list.stream() directly.

Upvotes: 1

Mena
Mena

Reputation: 48404

The problem here is that your List is parametrized with Foo, but your reduction operations will be parametrized with ICommon due to the add invocation.

While all Foos are ICommons, not all ICommons will be Foos.

The easiest way for you would be to parametrize your List with ICommon instead, without changing anything else in your (visible) code.

Something like:

List<ICommon> list = [some list of Foos];
list.stream().reduce( new Foo(), (a,b) -> a.add(b));

Upvotes: 4

Radu Ionescu
Radu Ionescu

Reputation: 3532

Found the solution to the problem. The reduce operation has the signature

T reduce(T identity, BinaryOperator<T> accumulator);

All I had to do is cast to a stream of ICommon

list.stream().map(x-> (ICommon) x).reduce( new Foo(), (a,b) -> a.add(b));

Upvotes: 1

Related Questions