Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use the java Stream.map Function together with an switch statement?

I want to transfer a stream of objects to different objects depending on a type. For example :

Stream<Animal> animals = Arrays.stream(Arrays.asList(new Animal("A"),new Animal("B")));
Stream result = animals.map(animal) ->{
    switch (animal.getType()) {
       case "A" : return new Bird(animal);
       case "B" : return new LION(animal);
       case "C" : return new Tiger(animal);
       case "D" : return new FISH(animal);  
    }
}

Is this an functional-programming "anti-pattern"?

Can I achive the above differently with functional programming?

(Remark: I also don't like is that every time I add a new type I have to update all my switch statements)

like image 838
jack Avatar asked Dec 19 '22 12:12

jack


2 Answers

@TimB is correct in its answer . This is not related to functional programming.

As you stated:

every time I add a new type I have to update all my switch statements

your "factory lambda" is breaking the Open/closed principle:

software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

You could create this animal factory that follows this principle:

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;

public class AnimalFactory {
    private final Map<String, Function<Animal, Object>> delegateFactories
        = new HashMap<String, Function<Animal,Object>>();

    public AnimalFactory withFactory(String type, Function<Animal, Object> factory) {
        delegateFactories.put(type, factory);
        return this;
    }

    public Object createAnimal(Animal animal) {
        return delegateFactories.get(animal.getType()).apply(animal);
    }
}

you could them use it easily by taking advantage of java 8 feature:

public static void main(String[] args) {
    Stream<Animal> animals = Arrays.asList(new Animal("A"),new Animal("B")).stream();
    AnimalFactory animalFactory = new AnimalFactory();
    animalFactory.withFactory("A", Bird::new);
    animalFactory.withFactory("B", Lion::new);
    animalFactory.withFactory("C", Tiger::new);
    animalFactory.withFactory("D", Fish::new);

    Stream result = animals.map(animalFactory::createAnimal);
}
like image 59
gontard Avatar answered Dec 22 '22 02:12

gontard


The alternative to switch is to use a Map which maps the values to objects encapsulating the desired behavior. Even as this pattern exists for a long time, Java 8 adds new possibilities for implementing it in a straight-forward manner:

// one-time initialization code
Map<String,Function<Animal,Animal>> factories=new HashMap<>();
factories.put("A", Bird::new);
factories.put("B", Lion::new);
factories.put("C", Tiger::new);
factories.put("D", Fish::new);

// use case
Stream<Animal> animals = Stream.of(new Animal("A"),new Animal("B"));
Stream result = animals.map(a -> factories.get(a.getType()).apply(a));
like image 29
Holger Avatar answered Dec 22 '22 02:12

Holger