简体   繁体   中英

Java 8 Lambdas - equivalent of c# OfType

I am learning the new java 8 features now, after 4 years exclusively in C# world, so lambdas are on top for me. I am now struggling to find an equivalent for C#'s "OfType" method.

What I have is a List myNodes , I want to get a List out of it, where Node is an interface, and SpecificNode is implementing it.

In C# it would be

IList<INode> myNodes = new List<INodes>(){new SpecificNode(), new OtherNode()}
IList<SpecificNode> specificNodes = myNodes.OfType<SpecificNode>()

There is no exact match in Java for the .OfType<T>() method, but you can use the Java8's filtering features:

IList<INode> myNodes = new ArrayList<INode>();
myNodes.add(new SpecificNode());
myNodes.add(new OtherNode());

List<SpecificNode> filteredList = myNodes.stream()
                                         .filter(x -> x instanceof SpecificNode)
                                         .map(n -> (SpecificNode) n)
                                         .collect(Collectors.toList());

If you want to get of the explicit cast, you can do:

List<SpecificNode> filteredList = myNodes.stream()
                                             .filter(SpecificNode.class::isInstance)
                                             .map(SpecificNode.class::cast)
                                             .collect(Collectors.toList());

I was having the same issue. This is what I came up with, but since java doesn't do extension methods (maybe in 10 more years?), it is a static method. This is using the stream API, though there isn't a particular reason you must do that. The same basic checks would work just fine in a for loop with a preallocated ArrayList.

@SuppressWarnings("unchecked")
private static <T> List<T> ofType(Class<?> out, List<Object> list) {
    return list.stream().filter(x -> out.isAssignableFrom(x.getClass()))
               .map(x -> (T) x) // unchecked
               .collect(Collectors.toList());
}

// fyi this code uses "boon" library
List<Object> objlist = list("ABC", 3, "def", -30.39); 
puts("strings=", ofType(String.class, objlist));  // strings= [ABC, def] 
puts("integers=", ofType(Integer.class, objlist)); // integers= [3]

Here is the version that doesn't use streams. It works just the same, but some of the fun with streams is that ... you might be able to stream them, if thats your kind of thing. I don't find it useful very often except for helpers like this.

private static <T> List<T> ofType(Class<?> out, List<Object> list) {
    List<T> outList = new ArrayList<T>(list.size());
    for(Object o : list) {
        if ( out.isAssignableFrom(o.getClass())) {
            outList.add((T)o);
        }
    }
    return outList;
}

You can create a Function containing the Stream pipeline, which reduces its invocation.

    Function<List<INode>,List<SpecificNode>> ofSub =
       bl -> bl.stream()
               .filter(x -> x instanceof SpecificNode)
               .map(n -> (SpecificNode) n)
               .collect(Collectors.toList());

For instance:

    for( SpecificNode s: ofSub.apply( myNodes ) ){
         //...
    }

Instead of first filtering and then mapping the stream to the desired target type, it is possible to do both in a single call to the stream via flatMap and this small helper function:

private static <Target extends Base, Base> Function<Base, Stream<Target>> ofType(Class<Target> targetType) {
    return value -> targetType.isInstance(value) ? Stream.of(targetType.cast(value)) : Stream.empty();
}

This Function basically checks for a element if a cast is possible and then casts it, returning a stream with the single casted element or an empty stream if the cast was not possible.

Stream.of(1, 2, 3, "Hallo", 4, 5, "Welt")
    .flatMap(ofType(String.class))
    .forEach(System.out::println);

With the help of a flatMap operation all returned streams can be concatenated.

I assume that a separated check and cast are easier to understand and maybe even faster in execution, this is just a prove of concept for a single stream operation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM