Class Conversion Using Java 8

I am learning to convert an object from one type of class to another using java 8. I have a bunch of xjc-generated jaxb classes. Classes do not have a sufficiently friendly structure because they display the xml structure more than the structure of a business object. I don’t want to edit the created classes, because I like to regenerate them whenever the circuit changes, without worrying about saving the settings.

I have a circuit like:

<xs:element name="farm"> <xs:sequence> <xs:element ref="animal" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:element> <xs:element name="animal"> <xs:complexType> <xs:sequence> <xs:element ref="goat"/> <xs:element ref="sheep"/> </xs:sequence> <xs:complexType> </xs:element> <xs:element name="goat"> <xs:complexType> <xs:sequence> goat fields </xs:sequence> <xs:complexType> </xs:element> <xs:element name="sheep"> <xs:complexType> <xs:sequence> sheep fields </xs:sequence> <xs:complexType> </xs:element> 

This generates Java something like:

 class Farm public List<Animal> getAnimals() class Animal public Goat getGoat() public String getGoatField() public Sheep getSheep() public String getSheepField() 

getGoat and getSheep can return null, but they cannot be null. Similarly, at least one of them must be zero. This is done using business rules and database restrictions, but not in xml (although if anyone has a suggestion to structure xml more than desired VOs, I’m all ears)

I would like to convert this class to

 class FarmWrapper public ArrayList<AnimalVO> getAnimals() //optional features tbd //possibly public ArrayList<GoatVO> getGoats() //possibly public ArrayList<SheepVO> getSheep() class GoatVO extends AnimalVO class SheepVO extends AnimalVO 

My idea was to do something like this:

 herd.stream() .filter(Objects::nonNull) .map(a -> { Optional<AnimalVO> goatVO = Optional.ofNullable(a.getGoat()) .map(g -> new GoatVO(g.getGoatField())); Optional<AnimalVO> sheepVO = Optional.ofNullable(a.getSheep()) .map(s -> new SheepVO(s.getSheepField())); return goatVO.orElse(sheepVO.get()); }) .collect(Collectors.toList()); 

Now I fed him with a list, and as soon as he encounters a zero sheep, he throws a NoSuchElementException .

I suppose I have a few questions:

  • Is this approach suitable for splitting my list into classes that use inheritance?
  • What is the best way to use Optionally to protect against potentially null values ​​when you cannot change the classes that pass you nulls
  • What am I missing with goatVO.orElse(sheepVO.get()) working as long as goatVO contains null and then throws NoSuchElementException when sheepVO contains null

What I'm really doing is working with the generated jaxb code and trying to take the generated classes and make the job easier. Traditionally, the project used a wrapper class that converts the generated classes to VO by a significant amount of null checking and int for manipulating BigInteger.

Editing created classes (Goat, Sheep, Animal) is not a starter, because I would like to maintain the ability to recover without worry

+5
source share
4 answers

I think you can make your code work with several settings:

 List<AnimalVO> list = herd.stream() .filter(Objects::nonNull) .map(a -> Optional.ofNullable(a.getGoat()) .map(Goat::getGoatField) .<AnimalVO>map(GoatVO::new) .orElseGet(() -> new SheepVO(a.getSheep().getSheepField()))) .collect(Collectors.toList()); 

Note. I prefer method references by lambdas, so I switched to using them.

Pay attention to the compiler hint in the <AnimalVO>map(GoatVO::new) . This is necessary so that the compiler knows that the type you are binding to is always AnimalVO , otherwise it indicates that the first Optional returns a GoatVO and gives a compilation error in the second Optional , which returns a SheepVO (and SheepVO not a descendant of GoatVO )

Also note that I use the orElseGet() method instead of orElse() . orElseGet() gets the Supplier value by default instead of the default value itself. This means that the default value is selected lazily only if the value of the first Optional missing.


EDIT: If your farm had more animals, i.e. besides Goat and Sheep , now you have a Cow , here is how you could do it:

 List<AnimalVO> list = herd.stream() .filter(Objects::nonNull) .map(a -> Optional.ofNullable(a.getGoat()) .map(Goat::getGoatField) .<AnimalVO>map(GoatVO::new) .orElseGet(() -> Optional.ofNullable(a.getSheep()) .map(Sheep::getSheepField) .<AnimalVO>map(SheepVO::new) .orElseGet(() -> new CowVO(a.getCow().getCowField())))) .collect(Collectors.toList()); 
+3
source
  • Basically, you can do it this way, although the inheritance model would be better ( Sheep extends Animal , Goat extends Animal )
  • If you want to protect against possible zeros, you can do something = optionalValue.orElse(defaultValue) , which will return the value contained in Optional if the value is not null or defaultValue if the value in Optional is null
  • String goatVO.orElse(sheepVO.get()) throws NoSuchElementException because sheepVO contains null . Note that sheepVO.get() evaluates regardless of the value in goatVO .
+3
source

return goatVO.orElseGet(sheepVO::get);

The code eagerly tries to dereference sheepVO optional, even if goatVO present. This makes the second difference lazy, so it only occurs if goatVO missing.

RE: your other two questions, I think converting to a guaranteed non-null interface is great. However, this is probably only useful if your AnimalVO is actually a useful interface (i.e. your code really doesn't care about which animal it works with). If your other code takes care, you might want something like a visitor template so that your consumers can work with individual types.

Better yet, do not work too functionally and simply group your input into two goats and sheeps waiting sheeps and return it; Which of these works best for your use case is highly dependent on how these types are used, so it is probably up to you to decide.

+2
source

I think you have this inheritance wrong ... why not just write

 abstract class Animal {abstract public AnimalVO createVO();} class Sheep extends Animal {public SheepVO createVO(){...}} class Goat extends Animal {public GoatVO createVO(){...}} 

Then if you want all VO for animals you just

 List<Animal> animals = ...; List<AnimalVO> vos = animals.stream().map(Animal::createVO).collect(toList()); 

If you can explain a little what you want to achieve, we can see how to achieve it.

+1
source

Source: https://habr.com/ru/post/1243441/


All Articles