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());
}