Recently I’ve rediscovered Class.cast(object) that can be used to cast objects to a type. In Java 8 streams the method allows to do a nice Java casting trick.
Old C-style casting
I don’t cast types very often. But sometimes it is the only way, especially when working with legacy libraries and/or badly designed API. This way or another, I when I wanted to cast types I did use C-style casting:
Object o = ...; String name = (String) o;
But it turns out that since Java 5 there is T Class<T>.cast(Object) method that do the same thing:
Object o = ...; String name = String.cast(o);
I leave it for you to decide which one is better, but with advent of Java 8, method references, and streams the later got a new life!
Casting mapper function
Unlike C-style cast, the method version can be passed as method reference into a stream to cast objects to correct type. Together with Class.isAssignableFrom(Class) we can create a nice object filter function with correct types:
// Bag of different objects: Collection<Object> items = ...; List<String> strings = items.stream() // Select only Strings: .filter(o -> String.isAssignableFrom(o.getClass())) // Object -> String .map(String::cast) // Collect to correctly typed collection: .collect(Collectors.toList());
I like that. Method References and Streams bring new life into old methods.
Complete program
Here’s a complete program, which selects objects by their type and returns correctly typed collection:
package com.farenda.java.lang; import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; public class CastingTrick { public static void main(String[] args) { Collection<Object> bag = Arrays.asList( "string 1", 1, "string 2", 2, 42d, Instant.EPOCH, Instant.now() ); System.out.println("Strings: " + selectByType(bag, String.class)); System.out.println("Numbers: " + selectByType(bag, Number.class)); System.out.println("Ints: " + selectByType(bag, Integer.class)); System.out.println("Times: " + selectByType(bag, Instant.class)); } static <T> List<T> selectByType(Collection<Object> bag, Class<T> type) { return bag.stream() .filter(o -> type.isAssignableFrom(o.getClass())) .map(type::cast) .collect(Collectors.toList()); } }
The program works as works as advertised:
Strings: [string 1, string 2] Numbers: [1, 2, 42.0] Ints: [1, 2] Times: [1970-01-01T00:00:00Z, 2017-07-02T10:27:10.962Z]