Given a simple generic class:
private static class Container<T> {
private List<T> aList;
private T aValue;
private Container(List<T> aList, T aValue) {
this.aList = aList;
this.aValue = aValue;
}
}
Initialize a list of that class:
List<Container<?>> container = new ArrayList<>();
// Add some elements...
Not possible (The method toArray(IntFunction<A[]>) in the type Stream<List<capture#1-of ?>> is not applicable for the arguments (List<?>[])):
container.stream().map(value -> value.aList).toArray(new List<?>[0]);
Possible:
container.stream().map(value -> value.aList).collect(Collectors.toList()).toArray(new List<?>[0]);
Why?
Stream's toArray takes a IntFunction<A[]> - i.e. a function that accepts an int and returns an array.
You tried to pass an array to it.
It should be used as follows:
container.stream().map(value -> value.aList).toArray(List<?>[]::new)
Related
So I am currenty trying to implement a method which does some filtering on lists regardless of their actual type. Here is the actual method:
public static <T extends List<String>> T filterList(T list, Predicate <String> predicate) {
T newList = ???
list.forEach(s -> {
if (predicate.test(s)) newList.add(s);
});
return newList;
}
So the generic type T is basically the some implementation of List such as ArrayList or LinkedList and regardless of their actual implementation I want to do some filtering through a Predicate passed as parameter. The return type of the method is the same as the list which is passed as a parameter. But how is it possible to instanciate an empty List based on T (see line 2)?
To show you how the method is intended to be used i provided an example. The following example would filter an ArrayList based on the length of the containing Strings:
ArrayList<String> listOfNames = new ArrayList<>();
listOfNames.add("stackoverflowuser");
listOfNames.add("sitaguptana");
listOfNames.add("nyan cat");
listOfNames.add("pedro");
Predicate<String> lengthUnderTen = (string) -> string.length() < 10;
ArrayList <String> result = filterList(listOfNames,lengthUnderTen);
If I am understanding your question correctly, then I don't see why you need to use generics at all here.
The following function will accept any class that extends List as a parameter e.g. an ArrayList, LinkedList etc.:
public static List<String> filterList(List<String> list, Predicate<String> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
Full example:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class Example {
public static void main(String[] args) {
ArrayList<String> example1 = new ArrayList<>();
example1.add("abc");
example1.add("ghe");
LinkedList<String> example2 = new LinkedList<>();
example2.add("foo");
example2.add("bar");
List<String> result1 = filterList(example1, s -> s.contains("a"));
List<String> result2 = filterList(example2, s -> s.contains("f"));
}
public static List<String> filterList(List<String> list, Predicate<String> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
}
If you could modify your method as
public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
It looks clean because it works for any type of List, not only for List<String>. This method will be more generic.
Have the caller pass in a Supplier<T> as well.
public static <T extends List<String>> T filterList(T list, Predicate <String> predicate, Supplier<T> listCreator) {
T newList = listCreator.get();
list.forEach(s -> {
// ...
You have a base class that already allows you to cleanly program to interface. You could design this API method to return a List object, keeping the generic type at element level.
Please note that this pattern is already provided by the standard API (List/Collection + Stream), so you shouldn't need to recreate it. - see note at bottom.
If you do not have any constraint regarding the type of list returned by the method, then it's this implementation's choice which list type it returns (using array list below):
public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {
List<T> newList = new ArrayList<>(); //You can choose a different type here
list.forEach(s -> {
if (predicate.test(s)) newList.add(s);
});
return newList;
}
If you leave it to your caller to choose what type of list is created, then perhaps you should take a factory:
public static <U, T extends List<U>> T filterList(T list,
Predicate<U> predicate, Supplier<T> newListFactory) {
T newList = newListFactory.get(); //You can choose a different type here
list.forEach(s -> {
if (predicate.test(s))
newList.add(s);
});
return newList;
}
Note: this pattern is already provided by the collections API:
java.util.stream.Stream.filter(Predicate<? super T>)
This allows you to do exactly the same thing, except that the creation of the returned list (say, you ran collect(Collectors.toList()))
You could create the class by reflection by passing a List<String> as parameter.
In fact you don't need to specify any wildcard for your list.
public static List<String> filterList(List<String> list, Predicate<String> predicate) throws InstantiationException, IllegalAccessException {
List<String> newList = list.getClass()
.newInstance();
list.forEach(s -> {
if (predicate.test(s)) newList.add(s);
});
return newList;
}
But note that a cleaner way would be to use a stream to collect the new List if the implementation doesn't matter :
public static List<String> filterList(List<String> list, Predicate<String> predicate) throws InstantiationException, IllegalAccessException {
return list.stream()
.filter(predicate)
.collect(Collectors.toList());
}
public class Test {
static List<Object> listA = new ArrayList<>();
public static void main(final String[] args) {
final List<TestClass> listB = new ArrayList<>();
listB.add(new TestClass());
// not working
setListA(listB);
// working
setListA(listB.stream().collect(Collectors.toList()));
System.out.println();
}
private static void setListA(final List<Object> list) {
listA = list;
}
}
why does it work with streams and does not work for the simple set?
For the first case, it fails because List<TestClass> is not a subtype of List<Object>.1
For the second case, we have the following method declarations:
interface Stream<T> {
// ...
<R, A> R collect(Collector<? super T, A, R> collector)
}
and:
class Collectors {
// ...
public static <T> Collector<T, ?, List<T>> toList()
}
This allows Java to infer the generic type parameters from the context.2 In this case List<Object> is inferred for R, and Object for T.
Thus your code is equivalent to this:
Collector<Object, ?, List<Object>> tmpCollector = Collectors.toList();
List<Object> tmpList = listB.stream().collect(tmpCollector);
setListA(tmpList);
1. See e.g. here.
2. See e.g. here or here.
This line
setListA(listB);
doesn't work because List in Java is invariant, meaning List<TestClass> doesn't extends List<Object> when TestClass extends Object. More details here
This line
setListA(listB.stream().collect(Collectors.toList()));
works because Java infer Object for Collector's generic type from this method signature setListA(final List<Object> list) and so you actually pass List<Object> there
the type parameters of Java Generic is invariance which means it can't be inherited as type parameters class hierarchy. The common parent of List<TestClass> and List<Object> is List<?>.
you can see detailed answer about java generic wildcard from kotlin & java. for example:
List<String> strings = new ArrayList<String>();
List<CharSequence> sequences = strings; // can't work
List<? extends CharSequence> parent1 = strings; // works fine
List<?> parent2 = strings; // works fine
// ^--- is equaivlent to List<? extends Object>
the streams approach is transform a List<TestClass> to List<Object>. if you want it works without transform a List to another List by stream. your methods signature should be as below, and the Collection#addAll also does it in java:
List<?> listA = new ArrayList<>();
private static void setListA(List<?> list) {
listA = list;
}
Why does the following
public class ListBox {
private Random random = new Random();
private List<? extends Collection<Object>> box;
public ListBox() {
box = new ArrayList<>();
}
public void addTwoForks() {
int sizeOne = random.nextInt(1000);
int sizeTwo = random.nextInt(1000);
ArrayList<Object> one = new ArrayList<>(sizeOne);
ArrayList<Object> two = new ArrayList<>(sizeTwo);
box.add(one);
box.add(two);
}
public static void main(String[] args) {
new ListBox().addTwoForks();
}
}
Not work? Just toying around with generics for the purpose of learning and I expected that I would be able to insert anything that extends Collection in there but I get this error:
The method add(capture#2-of ? extends Collection<Object>) in the type List<capture#2-of ? extends Collection<Object>> is not applicable for the arguments (ArrayList<Object>)
The method add(capture#3-of ? extends Collection<Object>) in the type List<capture#3-of ? extends Collection<Object>> is not applicable for the arguments (ArrayList<Object>)
at ListBox.addTwoForks(ListBox.java:23)
at ListBox.main(ListBox.java:28)
You've declared box to be a List of something that extends Collection of Object. But according to the Java compiler, it could be anything that extends Collection, i.e. List<Vector<Object>>. So it must disallow add operations that take the generic type parameter for this reason. It can't let you add an ArrayList<Object> to a List that could be List<Vector<Object>>.
Try removing the wildcard:
private List<Collection<Object>> box;
This should work because you can certainly add an ArrayList<Object> to a List of Collection<Object>.
I have a method that takes a generic parameter type. The scenario I have is this method will be called with different parameter types.
class something{
public void someMethod(){
List<A> listA = ....; //Class A have a field String Id;
List<B> listB = ....; //Class B haave a field String Id;
testMethod(listA);
testMethod(listB);
}
private <T> void testMethod( List<T> list ){
for( T event : list ){
//TODO (something like): event.getId();
}
}
}
In the above code all the parameters will be be a List<someObjectType>. All the object types have a common field and need to use the getter to fetch its value. Now since the method definition is generic, how do I achieve this?
Have A and B implement a common interface that has a method getID:
interface SomeInterface {
String getID();
}
then you could have:
private <T extends SomeInterface> void testMethod(List<T> list) {
for (T event : list) {
// now you can use `event.getID()` here
}
}
There is no point in creating such a generic method without bounded type. Since T isn't bounded to any type, you can't use specific interface on the objects inside the list. So if you want testMethod to get list of objects of any type, you should use List<?> instead.
This cannot be done. You can't handle two different lists with incompatible interfaces the same way in your method, unless you do something with instanceof, i.e.
public void testMethod(List<? extends Object> list) {
if(list.get(0) == null) return;
if(list.get(0) instanceof A) {
// Do the A stuff
} else {
// Do the B stuff
}
}
Define your method like this with T extending your common class/interface BaseType:
private <T extends BaseType> void testMethod( List<T> list ){
for( T event : list ){
//TODO (something like): event.getId();
}
}
Example:
public void someMethod() {
List<Integer> listA = Arrays.asList( new Integer[] {1, 4, 9} );
List<Double> listB = Arrays.asList( new Double[] {1.5, 4.2, 9.3} );;
testMethod(listA);
testMethod(listB);
}
private <T extends Number> void testMethod( List<T> list ){
for( T event : list ) {
// Note intValue() method is being called here which is provided
// in the base class Number that is being extended by T
System.out.println(event.intValue());
}
}
As other answers said, you need to bound the type parameter by some interface. But for what you're using it for, you don't actually need to have a T:
private void testMethod(List<? extends SomeInterface> list) {
for (SomeInterface event : list) {
// now you can use `event.getID()` here
}
}
I don't know if i really understand what you want.
But if you know, you will store for example Strings into your List and want to use the toUpperCase() method, how about just casting it?
Do you think it is possible to create something similar to this?
private ArrayList increaseSizeArray(ArrayList array_test, GenericClass) {
array_test.add(new GenericObject()); // instance of GenericClass
return array_test;
}
Yes, you can.
private static <T> List<T> pushBack(List<T> list, Class<T> typeKey) throws Exception {
list.add(typeKey.getConstructor().newInstance());
return list;
}
Usage example:
List<String> strings = new ArrayList<String>();
pushBack(strings, String.class);
Old question but I would imagine this is the preferred way of doing it in java8+
public <T> ArrayList<T> dynamicAdd(ArrayList<T> list, Supplier<T> supplier) {
list.add(supplier.get());
return list;
}
and it could be used like this:
AtomicInteger counter = ...;
ArrayList<Integer> list = ...;
dynamicAdd(list, counter::incrementAndGet);
this will add a number to the list, getting the value from AtomicInteger's incrementAndGet method
Also possible to use constructors as method references like this: MyType::new
simple solution!
private <GenericType> ArrayList increaseSizeArray(ArrayList array_test, GenericType genericObject)
{
array_test.add(new genericObject());
return ArrayList;
}