How to get all elements of a list by instance?
I have a list that can have any class implementation of an interface Foo:
interface Foo;
class Bar implements Foo;
I want to use the java8 stream api to provide a utility method for extracting all elements of a specific class type:
public static <T extends Foo> List<T> getFromList(List<Foo> list, Class<T> type) {
return (List<T>) list.stream().filter(entry -> type.isInstance(entry)).collect(Collectors.toList());
}
using:
List<Foo> list;
List<Bar> bars = Util.getFromList(list, Bar.class);
Result: It works, but I have to add #SuppressWarnings due to the unchecked cast of (List<T>). How can I avoid this?
Introducing another type parameter that extends S is correct, however, in order to have the result as List<S>, but not as List<T>, you have to .map() the entries that pass the type::isInstance predicate to S.
public static <T extends Foo, S extends T> List<S> getFromList(List<T> list, Class<S> type) {
return list.stream()
.filter(type::isInstance)
.map(type::cast)
.collect(Collectors.toList());
}
As suggested by #Eran, this can be even simplified to work with just one type parameter:
public static <T extends Foo> List<T> getFromList(List<Foo> list, Class<T> type) {
return list.stream()
.filter(type::isInstance)
.map(type::cast)
.collect(Collectors.toList());
}
This seems to work without warnings :
public static <T extends Foo> List<T> getFromList(List<Foo> list, Class<T> type) {
return list.stream()
.filter(entry -> type.isInstance(entry))
.map(entry->type.cast(entry))
.collect(Collectors.toList());
}
Tested with Number replacing Foo and Integer replacing Bar :
public static <T extends Number> List<T> getFromList(List<Number> list, Class<T> type) {
return list.stream().filter(entry -> type.isInstance(entry)).map(entry->type.cast(entry)).collect(Collectors.toList());
}
public static void main(String[] args)
{
List<Number> list = new ArrayList<>();
list.add(5);
list.add(3.4);
list.add(7);
List<Integer> bars = getFromList(list, Integer.class);
System.out.println(bars);
}
Output:
[5, 7]
As list and type are not of the same type but rather in a inheritance hierarchy relation, you will likely to add another type argument similar to the following:
public static <T extends Foo, S extends T> List<T> getFromList(List<T> list, Class<S> type) {
return list.stream().filter(type::isInstance).collect(Collectors.toList());
}
Related
I'd like to create the following:
//infer the type from parameter but restrict it to one of
// Proxy's subtype. return a list of this sub type
public static List<T> process(<T extends Proxy> proxy)
{
return new ArrayList<T>(); //just for example
}
Usage:
List<ConcreteTypeOfProxy> list = process(new ConcreteTypeOfProxy());
The above example has compilation issues. I think this should logically be available in java, just not sure what the syntax is
//Any array that extends T
public static <T extends Proxy> List<T> process(T proxy)
{
return new ArrayList<T>(); //just for example
}
//Returns array that has T type of parameter
public static <T> List<T> process(T proxy)
{
return new ArrayList<T>(); //just for example
}
//Returns a map of generic type which you define in the method
public static <T, E extends Proxy> Map<T, E> process(T key, E value)
{
Map<T, E> map = new HashMap<T, E>();
map.put(key, value);
return map;
}
You don't need any method, and consequently you don't need any parameters, to do this:
List<ConcreteTypeOfProxy> list = new ArrayList<>();
Remember: there is no difference between ArrayList<ConcreteTypeOfProxy> and ArrayList<AnyOtherType>: it's just an ArrayList.
The type parameter is merely an instruction to the compiler to check the type of what is added - at compile time only - and to automatically cast values obtained from the list.
I have a common Transformer that simply filters a list using a given Predicate:
public class ListFilter<T> implements Observable.Transformer<List<T>, List<T>> {
private final Predicate<T> predicate;
private ListFilter(Predicate<T> predicate) {
this.predicate = predicate;
}
public static <T> Observable.Transformer<List<T>, List<T>> create(Predicate<T> predicate) {
return new ListFilter<>(predicate);
}
#Override
public Observable<List<T>> call(Observable<List<T>> listObservable) {
return listObservable
.flatMap(list -> Observable.from(list)
.filter(predicate::test)
.toList());
}
}
Now I can filter a list of items with a minimum boilerplate, like this:
repository.observeItems() // returns Observable<List<SomeItem>>
.compose(ListFilter.create(item -> /* some filter logic */)
But there's something I can't explain:
List<? extends Dummy> list = SomeDummyFactory.create();
BehaviorSubject<List<? extends Dummy>> subject = BehaviorSubject.create(list);
// #1 -> ok
BehaviorSubject.create(list)
.compose(ListFilter.create(item -> /* `item` is `Dummy` */)
...
// #2 -> error
subject
.compose(ListFilter.create(item -> /* `item` is `Object` */))
...
// #3 -> ok
subject
.map(any -> any) // do nothing
.compose(ListFilter.create(item -> /* `item` is `Dummy` */))
...
The only difference I can see between #2 and #3 is the expected wildcards of a Transformer<T, R>:
_#2: Transformer<? super List<? extends Dummy>, ? extends R>
_#3: Transformer<? super List<capture of ? extends Dummy>, ? extends List<capture of ? extends Dummy>
So there're a few questions:
Why Object is the type of an item in the 2nd case (and not Dummy)?
Why can't Java infer the return type of a Transformer in the 2nd case?
How does map operator help with it?
Why does the 1st case work fine?
It is because compiler can't re-capture wildcard type.
In your first case, you can try to add explicit generic. Both <Dummy> and <Object> will lead error. The fact geneirc type is (captrue#1 ?) extends Dummy which is infered by the create's generic return value.
In your second case, your subject generic type is List<? extends Dummy>. The compose must accept Transformer<? super List<? extends Dummy>,? extends R>>, so the create generic must be ? extends Dummy(same as case 1). But in current compile context (current statement), the wildcard type is not captured. Compiler can't infer the generic type, it simply use Object, so error happens.
In your third case, the map capture the wildcard type back. You can try any method with a generic Observable<R> return type, all of them will work. Such as .flatMap(d -> Observable.just(d)).
But note, your case 3 is just a workaround. The recommanded solution is let your ListFilter's generic more flexible.
You should define it as ObservableTransformer<List<? extends T>, List<? extends T>>
class ListFilter<T> implements ObservableTransformer<List<? extends T>, List<? extends T>> {
public static <T> ListFilter<T> create(Predicate<T> predicate) {
return new ListFilter<>(predicate);
}
private final Predicate<T> predicate;
private ListFilter(Predicate<T> predicate) {
this.predicate = predicate;
}
#Override
public ObservableSource<List<? extends T>> apply(Observable<List<? extends T>> upstream) {
return upstream
.flatMapSingle(list -> Observable.fromIterable(list)
.filter(predicate::test)
.toList());
}
}
i have an enum implementing a supplier, e.g.:
public enum QUERY_FIELD implements Supplier<String> {
PRODUCT_TYPE("ProductType"),
MIN_NUMBER_OF_PARTS("MinNumberOfParts"),
MAX_NUMBER_OF_PARTS("MaxNumberOfParts");
private final String id;
QUERY_FIELD(final String id) {
this.id = id;
}
#Override
public final String get() {
return id;
}
}
i have different utility methods which can find the enum i search for depending on the query
public static <T extends Enum<T> & Supplier<String>> Optional<T> findById(final Class<T> enumClass, final String id) {
return Arrays.stream(enumClass.getEnumConstants()).filter(p -> id.equalsIgnoreCase(p.get())).findFirst();
}
public static <T extends Enum<T> & Supplier<? extends Number>> Optional<T> findById(final Class<T> enumClass, final Number id) {
return Arrays.stream(enumClass.getEnumConstants()).filter(p -> id.equals(p.get())).findFirst();
}
now i want to adapt this idea to create a utility method that just returns the list of all values depending on the suppliers type.
i tried it with:
public static <T extends Enum<T> & Supplier<? extends String>> List<String> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants()).map(Supplier::get).collect(Collectors.toList());
}
or
public static <U, T extends Enum<T> & Supplier<? extends U>> List<U> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants()).map(Supplier::get).collect(Collectors.toList());
}
which both compile but do not work,
how should i build this method?
I guess that the problem you are experiencing in the runtime issue
described in Enum, interfaces and (Java 8) lambdas: code compiles but fails at runtime; is this expected? and is cause by a known bug in Java-8 https://bugs.openjdk.java.net/browse/JDK-8141508.
The problem is with the intersection bound Enum<T> & Supplier<? extends String>. It seems that the compiler generates code where the second element in the intersection is lost and so in run-time it is as if you tried to use for the lambda an object of a class that is not guaranteed to implement Supplier but just Enum.
Since getEnumConstant is available for all Class<X> regardless wether X is an enum or not you could simply discard that part from the param-type bound and leave it as Supplier<String>:
import java.util.function.*;
import java.util.*;
import java.util.stream.*;
enum Test implements Supplier<String> {
A, B, C;
public String get() { return name(); }
}
class Main {
public static <T extends Supplier<String>> List<String> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.map(Supplier::get)
.collect(Collectors.toList());
}
public static final void main(String[] args) {
System.out.println(getValueList(Test.class).stream()
.collect(Collectors.joining(",")));
}
}
However the downside of this is that the compiler won't fail if someone provide a class that is not an enum.... in order to prevent that you can keep the intersection bound and add an extra map that explicitly cast the enum constants into suppliers:
public static <T extends Enum<T> & Supplier<String>> List<String> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.map(x -> (Supplier<String>) x) // not needed to compile but needed for run-time.
.map(Supplier::get)
.collect(Collectors.toList());
}
UPDATE
There is a even better solution for the second alternative. You can do the cast just one outside the stream, that should save you compute:
public static <T extends Enum<T> & Supplier<String>> List<String> getValueList(final Class<T> enumClass) {
final Class<Supplier<String>> asSupplierClass = enumClass;
return Arrays.stream(asSupplierClass.getEnumConstants())
.map(Supplier::get)
.collect(Collectors.toList());
}
I have not tested it, let me know whether it works.
Use Case
I currently have this pattern in many adapters:
entries.stream()
.filter(Entry.class::isInstance)
.map(Entry.class::cast)
.map(Entry::getFooBar)
.collect(Collectors.toList());
Where entries is a List of objects that implement a particular interface. Unfortunately, the interface - which is a part of a 3rd party library - does not define the common getters. To create a list of the objects I want, I need to search for them, cast them, and call the appropriate getter method.
I was going to refactor it into a helper class as such:
public static <T, O> List<O> entriesToBeans(List<T> entries,
Class<T> entryClass, Supplier<O> supplier) {
return entries.stream()
.filter(entryClass::isInstance)
.map(entryClass::cast)
.map(supplier) // <- This line is invalid
.collect(Collectors.toList());
}
I would then call this method to do the conversion:
Helper.entriesToBeans(entries,
Entry_7Bean.class,
Entry_7Bean::getFooBar);
Unfortunately, I can't pass the getter into the refactored function and have the map call it because the map is expecting a function.
Question
How can the getter be called in the refactored version?
A method like:
class T {
public O get() { return new O(); }
}
will map to a Function<T, O>.
So you can simply need to change your method signature to:
public static <T, O> List<O> entriesToBeans(List<T> entries,
Class<T> entryClass, Function<T, O> converter) {
Update: the reason for the cast, I suspect, is that your original list may contain elements that are not Ts. So you could also change the signature to:
public static <T, O> List<O> entriesToBeans(List<?> entries,
Class<T> entryClass, Function<T, O> converter) {
You can then pass a List<Object>, for example, and only keep the Ts in the list, cast and convert.
For reference, here is a working example (prints John, Fred):
static class Person {
private final String name;
Person(String name) { this.name = name; }
String name() { return name; }
}
public static void main(String[] args) {
List<String> result = entriesToBeans(Arrays.asList(new Person("John"), new Person("Fred")),
Person.class, Person::name);
System.out.println("result = " + result);
}
public static <T, O> List<O> entriesToBeans(List<?> entries,
Class<T> entryClass, Function<T, O> converter) {
return entries.stream()
.filter(entryClass::isInstance)
.map(entryClass::cast)
.map(converter)
.collect(Collectors.toList());
}
You should pass a Function<T, O> instead:
public static <T, O> List<O> entriesToBeans(List<T> entries, Class<T> entryClass,
Function<T, O> mapper) {
return entries.stream().filter(entryClass::isInstance)
.map(entryClass::cast).map(mapper)
.collect(Collectors.toList());
}
I have this static method
public static List<? extends A> myMethod(List<? extends A> a) {
// …
}
which I'm calling using
List<A> oldAList;
List<A> newAList = (List<A>) MyClass.myMethod(oldAList);
This gives a warning because of the unchecked cast to List<A>. Is there any way of avoiding the cast?
You need to define the type returned matches the argument (and extends A)
public static <T extends A> List<T> myMethod(List<T> a) {
// …
}
Then you can write
List<E> list1 = .... some list ....
List<E> list2 = myMethod(list1); // assuming you have an import static or it's in the same class.
or
List<E> list2 = SomeClass.myMethod(list1);
You are casting it to the parent A, if you want to avoid that then change your return type for myMethod:
public static List<T> myMethod(List<T> a) {
// …
}
if you define:
public static <T extends A> List<T> myMethod(List<T> a) {
// …
}
then you can call:
List = MyClass.myMethod(List a){}
it is generic method, is`nt it?
Jirka
This is how you can avoid the cast with static methods:
public class MyClass {
public static List<? extends A> myMethod(List<? extends A> a) {
return a;
}
public static void main(String[] args) {
List newList = new ArrayList<A>();
List<?> newList2 = new ArrayList<A>();
List<B> oldList = new ArrayList<B>();
newList = MyClass.myMethod(oldList);
newList2 = MyClass.myMethod(oldList);
}
}
In the code above, B extends A. When newList variable is defined as List without generics or as List with wildcard type (List< ? >) cast is not necessary. On the other hand if you only want to get rid the warning you can use '#SuppressWarning' annotation. Check this link for more info What is SuppressWarnings ("unchecked") in Java?
Here is simple example for #SuppressWarnings ("unchecked"):
public static List<? extends A> myMethod(List<? extends A> a) {
// …
}
#SuppressWarnings ("unchecked")
newAList = (List<A>) MyClass.myMethod(oldAList);