- Applying Pattern matching for Instanceof in the Stream
- Applying Pattern matching for Instanceof in the Stream
- Solution – 1
- Pattern matching + mapMulti
- Pattern matching + flatMap
- Solution – 2
- Check instanceof in stream
- Solution 2
- Solution 3
- Solution 4
- How to write instanceof with Java 8?
- How to write instanceof with Java 8?
- Check instanceof in stream
- Simplify code and avoid instanceOf and null check
Applying Pattern matching for Instanceof in the Stream
Applying Pattern matching for Instanceof in the Stream
Suppose we have a Stream of Animals.
We have different Subclasses of Animals, and we want to apply a filter on the stream to only have the Zebras of the Stream. We now still have a Stream of Animals, but only containing Zebras. To get a stream of Zebras we still need to cast.
Stream zebraStream = animalStream .filter(Zebra.class::isInstance) .map(Zebra.class::cast);
Java 14 introduced pattern matching for instanceof, so we can now use:
if (animal instanceof Zebra zebra)
Is there a way to use pattern matching in stream pipes?
Of course you could do something like this:
Stream zebraStream = animalStream.map(animal -> < if (animal instanceof Zebra zebra) < return zebra; >return null; >) .filter(Objects::nonNull);
But IMHO this is really ugly.
Solution – 1
Pattern matching + mapMulti
To coerce a Stream of supertype to a Stream of one of its subtypes, you can make use of the Pattern matching for instanceof in conjunction with Java 16 mapMulti() , which expects a stream element and a Consumer of the resulting type:
Stream animalStream = Stream.of(); Stream zebraStream = animalStream .mapMulti((animal, consumer) -> < if (animal instanceof Zebra zebra) consumer.accept(zebra); >);
Pattern matching + flatMap
To use Pattern matching for instanceof you can also employ a classic stream operation flatMap() , which is like mapMulti() is meant to perform one-to-many transformations.
The important distinction between the two is that mapMulti() replace the initial stream element with zero or more elements via its Consumer , meanwhile flatMap() require a producing a new Stream to flatten the data. And in this case utilizing mapMulti() would be more advantages because if the list is large generating singleton-streams for every element might be costful.
Stream zebraStream = animalStream .flatMap(animal -> animal instanceof Zebra zebra ? Stream.of(zebra) : null );
Note that according to the documentation instead of an empty stream, we can also return null (which is handy because Stream.empty() doesn’t return constant but spawns a new object):
If a mapped stream is null an empty stream is used, instead.
Solution – 2
I think you are almost done it! Just use filter instead of map :
Stream zebraStream = animalStream.stream() .filter(animal -> animal instanceof Zebra) .map(Zebra.class::cast);
Check instanceof in stream
You can apply another filter in order to keep only the ScheduleIntervalContainer instances, and adding a map will save you the later casts :
scheduleIntervalContainers.stream() .filter(sc -> sc instanceof ScheduleIntervalContainer) .map (sc -> (ScheduleIntervalContainer) sc) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList());
Or, as Holger commented, you can replace the lambda expressions with method references if you prefer that style:
scheduleIntervalContainers.stream() .filter(ScheduleIntervalContainer.class::isInstance) .map (ScheduleIntervalContainer.class::cast) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList());
Solution 2
A pretty elegant option is to use method reference of class:
scheduleIntervalContainers .stream() .filter( ScheduleIntervalContainer.class::isInstance ) .map( ScheduleIntervalContainer.class::cast ) .filter( sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList() );
Solution 3
There is a small problem with @Eran solution — typing class name in both filter and map is error-prone — it is easy to forget to change the name of the class in both places. An improved solution would be something like this:
private static Function> select(Class clazz) < return e ->clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null; > scheduleIntervalContainers .stream() .flatMap(select(ScheduleIntervalContainer.class)) .filter( sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList());
However there might be a performance penalty in creating a Stream for every matching element. Be careful to use it on huge data sets. I’ve learned this solution from @Tagir Vailev
Solution 4
Instead of a filter + map like other answers suggest, I would recommend this utility method:
public static Function> filterType(Class clz) < return obj ->clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty(); >
Stream.of(dog, cat fish) .flatMap(filterType(Dog.class));
Compared to filter + map it has the following advantages:
- If the class does not extend your class you will get a compile error
- Single place, you can never forget to change a class in either filter or map
How to write instanceof with Java 8?
Solution 1: You can apply another in order to keep only the instances, and adding a will save you the later casts : Or, as Holger commented, you can replace the lambda expressions with method references if you prefer that style: Solution 2: A pretty elegant option is to use method reference of class: Solution 3: There is a small problem with @Eran solution — typing class name in both and is error-prone — it is easy to forget to change the name of the class in both places. I’ve learned this solution from @Tagir Vailev Solution 4: Instead of a filter + map like other answers suggest, I would recommend this utility method: Use it as: Compared to filter + map it has the following advantages: If the class does not extend your class you will get a compile error Single place, you can never forget to change a class in either filter or map Question: I want to simplify the code (e.g. by using Optionals) value can be: , , Solution 1: I’d handle it with overloading: . and fix any code passing it .
How to write instanceof with Java 8?
I am new in Java 8 Optional. I have to change the following code :
@RequestMapping(value = "/account", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getAccount() < return Optional.ofNullable(userService.getUserWithAuthorities()) .map(user ->new ResponseEntity<>( new UserDTO( user.getLogin(), null, user.getFirstName(), user.getLastName(), user.getEmail(), "", user.getLangKey(), user.getAuthorities() .stream() .map(Authority::getName) .collect(Collectors.toList()) ), HttpStatus.OK ) ) .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); >
I want to create a different ResponseEntity according to the instance of user . How i can write the equivalent of the following code:
if(user instanceof Admin ) < // my logic >else if(user instanceof NormalUser) < // my logic >
You would do it like this :
@RequestMapping(value = "/account", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getAccount() < return Optional.ofNullable(userService.getUserWithAuthorities()) .map(user -> < if (user instanceof Admin) < //. >return new ResponseEntity<>(new UserDTO(. ), HttpStatus.OK); >) .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); >
However, if you can, avoid the instanceof operator. Add a isAdmin method to your User class : in class Admin , it would return true and in class NormalUser , it would return false .
The way you did it. However, you will have to cast u to the type you want.
Is java 8’s stream preferable for a loop with instanceof checks?, As Holger said, your stream would be a pipeline with side effects, so it’s clean to use a traditional for loop.
Check instanceof in stream
I have the following expression:
scheduleIntervalContainers.stream() .filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime()) .collect(Collectors.toList());
. where scheduleIntervalContainers has element type ScheduleContainer :
final List scheduleIntervalContainers
Is it possible to check the type before the filter?
You can apply another filter in order to keep only the ScheduleIntervalContainer instances, and adding a map will save you the later casts :
scheduleIntervalContainers.stream() .filter(sc -> sc instanceof ScheduleIntervalContainer) .map (sc -> (ScheduleIntervalContainer) sc) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList());
Or, as Holger commented, you can replace the lambda expressions with method references if you prefer that style:
scheduleIntervalContainers.stream() .filter(ScheduleIntervalContainer.class::isInstance) .map (ScheduleIntervalContainer.class::cast) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList());
A pretty elegant option is to use method reference of class:
scheduleIntervalContainers .stream() .filter( ScheduleIntervalContainer.class::isInstance ) .map( ScheduleIntervalContainer.class::cast ) .filter( sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList() );
There is a small problem with @Eran solution — typing class name in both filter and map is error-prone — it is easy to forget to change the name of the class in both places. An improved solution would be something like this:
private static Function> select(Class clazz) < return e ->clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null; > scheduleIntervalContainers .stream() .flatMap(select(ScheduleIntervalContainer.class)) .filter( sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList());
However there might be a performance penalty in creating a Stream for every matching element. Be careful to use it on huge data sets. I’ve learned this solution from @Tagir Vailev
Instead of a filter + map like other answers suggest, I would recommend this utility method:
public static Function> filterType(Class clz) < return obj ->clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty(); >
Stream.of(dog, cat fish) .flatMap(filterType(Dog.class));
Compared to filter + map it has the following advantages:
- If the class does not extend your class you will get a compile error
- Single place, you can never forget to change a class in either filter or map
Using instanceof and casting with generics, The type parameter of your Animal class has no relevance to the code you’ve posted. You never check that an object matches the actual type of T
Simplify code and avoid instanceOf and null check
I want to simplify the code (e.g. by using Optionals)
private String formatValue(Object value) < if (value == null) < return null; >else if (value instanceof Date) < return MyUtils.formatDate((Date)value, Constant.DATE_FORMAT_YYYYMMDD_HHMMSS); >return value.toString(); >
value can be: null , String , Date
I’d handle it with overloading:
private String formatValue(String value) < return value; >private String formatValue(Date value)
. and fix any code passing it null .
If you really have to handle null , then:
private String formatValue(String value) < return value; >private String formatValue(Date value)
The above works provided the type of what you’re passing in is known at compile-time, which normally would be the case. But if you have to handle the possibility that the only type known at compile-time is Object , you can add an Object version as well:
private string formatValue(Object value) < if (value instanceof Date) < return this.formatValue((Date)value); >return value == null ? null : value.toString(); >
That starts feeling like more code, but it lets the normal path (where the type is known) go straight to the appropriate method, and you’d only supply this if you were dealing with something dynamic where you were stuck with Object .
Or you can just have the Object version. It doesn’t have fewer checks, but it seems a bit simpler and clearer to me.
If you really want a functional version of your code, you can do this (using 2 Optionals):
private String formatValue(Object value) < return Optional.ofNullable(value) .map(v ->Optional.of(v) .filter(d -> d instanceof Date) .map(d -> MyUtils.formatDate((Date) d, Constant.DATE_FORMAT_YYYYMMDD_HHMMSS)) .orElseGet(() -> v.toString())) .orElse(null); >
This is hard to read, though; so I’d personally keep your original version.
You can use Optional from java 8:
return Optional.ofNullable(value).filter(v -> v instanceof Date || v instanceof String) .map(v -> v instanceof Date ? MyUtils.formatDate((Date)v, Constant.DATE_FORMAT_YYYYMMDD_HHMMSS : v) .orElse(null);
For Date as one of Date or String, one cannot avoid instanceOf. As null is not a Date:
if (value instanceof Date) < value = MyUtils.formatDate((Date)value, Constant.DATE_FORMAT_YYYYMMDD_HHMMSS); >return Optional.ofNullable(value).map(String.class::cast);
A Object::toString would be safer, but now a fail-fast cast exception would be thrown on erroneous values.
Pattern Matching for instanceof in Java 14, At some point, we’ve probably all written or seen code that includes some kind of conditional logic to test if an object has a specific type.