I have two classes Animal and Dog i.e
public class Animal {
private String name,age;
//public getters and setters
}
public class Dog extends Animal {
private String color;
//public getters and setters
}
I am using java 8 functional programming to extract the fields from the json node i.e
public class EntityExtraction {
private Function<JsonNode, Animal> extractAnimal = node -> {
Animal animal = new Animal();
animal.setName(node.get("name").asText()));
animal.setAge(node.get("age").asText()));
return animal;
};
private Function<JsonNode, Dog> extractDog = node -> {
Dog dog = new Dog();
dog.setName(node.get("name").asText()));
dog.setAge(node.get("age").asText()));
dog.setColor(node.get("color").asText()));
return dog;
};
}
Th problem is that it's not object oriented. If there is a new field in Animal class then i have to explicitly it in both functional methods i.e extractDog and extractAnimal.
Is there a way to set the superclass fields inside the Dog class using extractDog method without doing it via constructor i.e something like
private Function<JsonNode, Dog> extractDog = node -> {
Dog dog = new Dog();
//extract superclass fields
//dog.animal = extractAnimal.apply(node);
dog.setColor(node.get("color").asText()));
return dog;
};
You could write a method* that would give a correct instance for further filling.
private <T extends Animal> T extractAnimal(JsonNode node, Supplier<T> animalSupplier) {
T animal = animalSupplier.get();
animal.setName(node.get("name").asText());
animal.setAge(node.get("age").asText());
return animal;
}
After you get an object populated with Animal's properties, you can continue packing it accordingly to the type:
Dog dog = extractAnimal(node, Dog::new);
dog.setColor(node.get("color").asText());
...
Cat cat = extractAnimal(node, () -> getPopulatedCat());
Update:
To avoid refactoring, call the new method i.e extractAnimal(JsonNode node, Supplier<T> animalSupplier) from the Functional method extractAnimal i.e
private Function<JsonNode, Animal> extractAnimal = node -> extractAnimal(node, Animal::new);
*To follow the functional paradigm, you shouldn't necessarily be operating exclusively with Java functional types. The above method expresses the idea more eloquently than the private Function field does.
Reduce the extraction mappings to extract only the fields of the corresponding type and declare them as BiConsumer:
public class EntityExtraction {
private static final BiConsumer<JsonNode, Animal> extractAnimal = (node, animal) -> {
animal.setName(node.get("name").asText());
animal.setAge(node.get("age").asText());
};
private static final BiConsumer<JsonNode, Dog> extractDog = (node, dog) -> {
dog.setColor(node.get("color").asText());
};
}
Each BiCosnumer takes a JsonNode from which it extracts the values for a certain Animal type.
Now we extend EntityExtraction by a factory method which will return an extracted Dog:
public static Dog extractDog(JsonNode node) {
Dog dog = new Dog();
BiConsumer<JsonNode, Dog> extraction = extractDog.andThen(extractAnimal);
extraction.accept(node, dog);
return dog;
}
It takes only a JsonNode as input. Since it is supposed to extract a Dog it creates an instance of and applies all needed extraction mappings to it.
To map a JsonNode we use the factory method.
Dog dog = EntityExtraction.extractDog(node);
More complex extraction mappings can be build by chaining extraction mapper.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With