问题
Context: I have chained objects generated by wsdl2java so none of them contains java.util.Optional. There is an already created method that call the soap web services, receives the xml and unmarshalling it in cascaded objects.
Desire: I want to use Optional in order to avoid null tests.
I have already posted my dificults to accomplish it (how to use optional and filter along to retrieve an object from chained/cascaded objects generated by wsdl2java) but, after 3 days searching and reading I really got stuck and I realised that I have some gap in my knowlodge so I decided take a step back and try a more simple solution before moving forward.
I am wondering if I am really trying something that is possible to do: use Optional with a cascaded objects that weren't created with Optional pattern in mind. Most of the example I found in internet either use cascaded objects coded with "private Optional myObj" or they are limited to use Optional.of to an object, not to a tree of objects like generated by wsdl2java.
Here is what I tried so far to figure out if it is possible and I got stuck as well: I followed http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html and I tryed to apply same idea imagining the objects were coded originally with Java 7 (no private Optional myObj at all).
Firstly, exactly as showed by Oracle article (from parent to child dependency):
USB
public class USB {
String version;
public USB() {
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
OptionalSoundcard
public class OptionalSoundcard {
private Optional<USB> usb;
public OptionalSoundcard() {
// TODO Auto-generated constructor stub
}
public Optional<USB> getUsb() {
return usb;
}
public void setUsb(Optional<USB> usb) {
this.usb = usb;
}
}
OptionalComputer
public class OptionalComputer {
private Optional<OptionalSoundcard> soundcard;
public OptionalComputer() {
}
public Optional<OptionalSoundcard> getSoundcard() {
return soundcard;
}
public void setSoundcard(Optional<OptionalSoundcard> soundcard) {
this.soundcard = soundcard;
}
}
And the well-successed test
@Test
public void runOptionalClassicOracleExample() throws Exception {
USB usb = new USB();
usb.setVersion("1");
OptionalSoundcard soundcard = new OptionalSoundcard();
soundcard.setUsb(Optional.ofNullable(usb));
OptionalComputer computer = new OptionalComputer();
computer.setSoundcard(Optional.ofNullable(soundcard));
Optional<OptionalComputer> sc = Optional.of(computer);
// Optional<Computer> sc = Optional.ofNullable(computer);
String v1 = sc.flatMap(OptionalComputer::getSoundcard).flatMap(OptionalSoundcard::getUsb).map(USB::getVersion)
.orElse("UNKNOWN");
assertThat(v1, is(equalTo("1")));
}
Now, imagine same Computer and Soundcar created with Java 7 pattern in mind (USB class is the same as above)
Soundcard
public class Soundcard {
private USB usb;
public Soundcard() {
// TODO Auto-generated constructor stub
}
public USB getUsb() {
return usb;
}
public void setUsb(USB usb) {
this.usb = usb;
}
}
Computer
public class Computer {
private Soundcard soundcard;
public Computer() {
// TODO Auto-generated constructor stub
}
public Soundcard getSoundcard() {
return soundcard;
}
public void setSoundcard(Soundcard soundcard) {
this.soundcard = soundcard;
}
}
And the test that even doesn't compile
@Test
public void runClassicOracleExample() throws Exception {
USB usb = new USB();
usb.setVersion("2");
Soundcard soundcard = new Soundcard();
soundcard.setUsb(usb);
Computer computer = new Computer();
computer.setSoundcard(soundcard);
Optional<Computer> sc = Optional.ofNullable(computer);
String v1 = sc.flatMap(Computer::getSoundcard).flatMap(Soundcard::getUsb).map(USB::getVersion)
.orElse("UNKNOWN");
assertThat(v1, is(equalTo("2")));
}
The error is:
The method flatMap(Function>) in the type Optional is not applicable for the arguments (Computer::getSoundcard) - The type of getSoundcard() from the type Computer is Soundcard, this is incompatible with the descriptor's return type: Optional
*** edited
in real world I have
NamenOndernemingType 1 X 1 NaamOndernemingLijstCommercieelType 1 X List<> NaamOndernemingType
I understand that all these three will produce the same results, except for one detail: null safety
// no Optional at all and no map() at all
NaamOndernemingLijstCommercieelType naamOndernemingLijstCommercieel = onderneming.getNamen()
.getCommercieleNamen();
NaamOndernemingType naamOnderneming1 = naamOndernemingLijstCommercieel.getCommercieleNaam().stream()
.filter(x -> x.getTaalcode().getValue() == "nl").findFirst().get();
// Optional.ofNullable wrapped only the list and flatMap the list
Optional<List<NaamOndernemingType>> optionalList = Optional
.ofNullable(onderneming.getNamen().getCommercieleNamen().getCommercieleNaam());
NaamOndernemingType naamOnderneming2 = optionalList
.flatMap(list -> list.stream().filter(s -> "nl".equals(s.getTaalcode().getValue())).findFirst()).get();
// Optional.ofNUllable on root element and map all other "levels" until get the
// list and strem()
Optional<NamenOndernemingType> optionalNamenOnderneming = Optional.ofNullable(onderneming.getNamen());
NaamOndernemingType naamOnderneminge = optionalNamenOnderneming.map(NamenOndernemingType::getCommercieleNamen)
.map(NaamOndernemingLijstCommercieelType::getCommercieleNaam).get().stream().filter(Objects::nonNull)
.filter(x -> x.getTaalcode().getValue() == "nl").findFirst().get();
*** Future readers may find worth to read how to use optional and filter along to retrieve an object from chained/cascaded objects generated by wsdl2java
My final solution become:
Optional.ofNullable(onderneming.getNamen()).map(NamenOndernemingType::getCommercieleNamen)
.map(NaamOndernemingLijstCommercieelType::getCommercieleNaam).get().stream().filter(Objects::nonNull)
.filter(x -> x.getTaalcode().getValue() == "nl").findFirst()
.ifPresent(o -> myMethod("onderneming_commerciele_naam", o.getNaam().getValue()));
回答1:
Why not just use .map()
?
Because according to java doc of Optional#flatMap()
:
If a value is present, apply the provided
Optional
-bearing mapping function to it, return that result, otherwise return an emptyOptional
. This method is similar to#map(Function)
, but the provided mapper is one whose result is already anOptional
, and if invoked,flatMap
does not wrap it with an additionalOptional
.
The mapper used in flatMap must return an Optional
. I think map
will suit your needs better. because it wraps the result from Computer#getSoundcard()
in an Optional.
来源:https://stackoverflow.com/questions/47262770/how-use-optional-with-cascaded-objects-not-created-with-optional