-1

Im just asking for interest. Is it possible to make something like this by using Java's Stream? (would be toys.stream()...)

Set<String> colors = new HashSet<>();

toys.forEach((toy) -> {
    if (toy.getType() == Toys.BIKE) {
        colors.add(((Bike) toy.getData()).getFrameColor());
    } else {
        colors.add(((Skateboard) toy.getData()).getColor());
    }
});

I should mention, that it is not possible for me to store the color in a superclass.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
MysteriousPerson
  • 670
  • 7
  • 21

1 Answers1

4

That's not a proper way of exploiting inheritance. A better way would be to have something like

abstract class Toy {
  abstract Color getActualColor();
}

class Bike {
  @Override Color getActualColor() { return getFrameColor(); }
}

class Skateboard {
  @Override Color getActualColor() { return getColor(); }
}

...
toys.stream().map(Toy::getActualColor).collect(Collectors.toSet());

Which makes use of polymorphism so that you don't have to check the type of an instance at runtime, which is bad 99% of the time. Mind that your design seems to have the data by composition inside Toy, sort of

class Toy {
  ToyData data;
}

but the design doesn't change, you just have twice mapping, eg stream().map(Toy::getData).map(ToyData::getActualColor).

According to your edits a solution would have to use a set of interfaces to mark the various features, something like:

class Toy
{
    final ToyData data;

    public Toy(ToyData data) { this.data = data; }

    public ToyData getData() { return data; }
}

interface ToyData { } 

interface Colored
{
    Color getColor();
}

class Bike implements ToyData, Colored
{
    @Override public Color getColor() { return Color.WHITE; }
}

class Skateboard implements ToyData, Colored
{
    @Override public Color getColor() { return Color.BLACK; }
}

class Ship implements ToyData { }


public static void main (String[] args) throws java.lang.Exception
{
    List<Toy> toys = new ArrayList<>();

    toys.add(new Skateboard());
    toys.add(new Bike());
    toys.add(new Ship());

    Set<Color> = toys.stream()
                     .map(Toy::getData)
                     .filter(t -> t instanceof Colored)
                     .map(t -> ((Colored)t).getColor())
                     .collect(Collectors.toSet());
}
Jack
  • 131,802
  • 30
  • 241
  • 343
  • Yup. This. Always this. You could also use double-dispatch; but that might be overkill... – Boris the Spider Jun 04 '16 at 23:51
  • It is not possible for me to do this in a superclass. Ive made a bad example code for this. Think of the count of the wheels or something like this. Not any toy has wheels, so the method does not exist. Is it possible to use stream without putting all the mehtods in the superclass? – MysteriousPerson Jun 04 '16 at 23:54
  • @MysteriousPerson: not without reflection. A good design in this situation would be to have a method in superclass like `bool supportsFeature(Feature feature)` so that you can do `stream().filter(t -> t.supportsFeature(Feature.WHEELS)).map(...)` – Jack Jun 04 '16 at 23:56
  • Thanks. Your actual example gave me some inspiration. Im using an interface for this :) – MysteriousPerson Jun 04 '16 at 23:59
  • @MysteriousPerson: check my edit, you can also use sort of multiple interfaces to obtain what you are looking for. The only downside is that you'll have to use a downcast, which is something you should always avoid also if in this situation it's safe. – Jack Jun 05 '16 at 00:08