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\" me
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;
}
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.
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());
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 ) ){
//...
}