Generics and static methods - java

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);

Related

Generic Types ("List<? extends> " ) in HashMap's Value

I did a generic type test.
class A {}
class B extends A {}
there are two methods:
private static void extendTest(List<? extends A> superList) {
System.out.println("extendTest over.");
}
private static void extendMapTest(HashMap<String, List<? extends A>> superMap) {
System.out.println("extendMapTest over.");
}
and I create a list and a map:
List<B> childList = new ArrayList<>();
childList.add(new B());
extendTest(childList); // TAG1: it is OK.
HashMap<String, List<B>> childMap = new HashMap<>();
childMap.put("hello", childList);
extendMapTest(childMap); // TAG2: ERROR!
why TAG1 is OK, and TAG2 is error?
how can I correct TAG2?
List<? extends A> is not a super type of List<B>. See the offical generics tutorial on upper bounding.
To fix, define childList like this:
List<A> childList = new ArrayList<>();
You can still add a B to a List<A>, because a
B is an A.
And define your map method like this:
private static void extendMapTest(HashMap<String, List<A>> superMap) {...
One thing to consider.
You can create another class
class C extends A {}
and inside your extendMapTest(HashMap<String, List<? extends A>> superMap)
you are free to do:
List<C> list = new ArrayList<>();
list.add(new C());
superMap.put("A", list);
but that is in contradiction with the declaration of the childMap object you're passing to extendMapTest
HashMap<String, List<B>> childMap
I guess the compilation error prevents this or similar situation
it is as simple like List<Integer> are not subtype of List<Number> even though Number and Integer are related and in the same way AnyClass<A> is not related with AnyClass<B> as shown in the docs too
The simple option is simply define your map with wild card too , no code change required ,like
HashMap<String, List<? extends A>> childMap = new HashMap<>();
e.g with modified code to demonstration
class A {}
class B extends A {
public int a=9;
}
private static void extendTest(List<? extends A> superList) {
System.out.println("extendTest over.");
}
private static void extendMapTest(HashMap<String, List<? extends A>> superMap) {
System.out.println("extendMapTest over."+((B)(superMap.get("hello").get(0))).a);
}
List<B> childList = new ArrayList<>();
childList.add(new B());
extendTest(childList);
HashMap<String, List<? extends A>> childMap = new HashMap<>();
childMap.put("hello", childList);
extendMapTest(childMap);
Output:
extendTest over.
extendMapTest over.9

How to get all elements of a list by instance?

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());
}

How to use wildcard generics with Sets.powerSet

I have some code like this
import com.google.common.collect.Sets;
public void handleInput(Set<Object> conditions){
Set<Set<Object>> powerSet = Sets.powerSet(conditions);
...
}
This works fine. But I want to do this:
public void handleInput(Set<? extends Object> conditions){
Set<Set<? extends Object>> powerSet = Sets.powerSet(conditions);
...
}
so I can get the powerset of objects that are subclasses of object. But this won't compile and I get the error:
Type mismatch: cannot convert from Set<Set<capture#1-of
? extends Object>> to Set<Set<? extends Object>>
How can I achieve this goal?
EDIT: I guess it has something to do with the generic type getting erased at compile time, so that the compiler can't know that powerSet won't add something illegal to the sets it's creating. I've reworked the client, by casting all the inputs to Object, and removing the wildcard altogether. Is this the best way? Thanks!
In this case it doesn't make any sense - since all Java classes extend java.lang.Object at some point.
So ? extends Object is redundant.
But speaking of Sets.powerSet, this works like a charm:
public class TestClass {
public static class A {}
public static class B extends A {}
public static class C extends B {}
public Set<? extends Set<? extends A>> exampleMethod(Set<? extends A> input) {
return Sets.powerSet(input);
}
public static void main(String[] args) {
final TestClass testClass = new TestClass();
final A a = new A();
final B b = new B();
final C c = new C();
System.out.println(
testClass.exampleMethod(
ImmutableSet.of(a, b, c)
)
);
}
}
as #slnowak notes, when you are extending Object, the code is really redundant.
However, to understand the Exception and avoid it...
public void handleInput(Set<? extends Object> conditions){
Set<? extends Set<? extends Object>> powerSet = Sets.powerSet(conditions);
...
}
this will compile and, more usefully, you can restrict the types in your conditions argument using this method, for instance - you could have:
public void handleInput(Set<? extends Number> conditions){
Set<? extends Set<? extends Number>> powerSet = Sets.powerSet(conditions);
...
}
and this would prevent you passing in sets that had non-numeric types and warn you of this at compile time.

generics in constructors in Java?

Consider this hypothetical class (which I found in a online video):
public class Contrived<T extends Number> extends ArrayList<T> {
List <? extends T> values;
......
}
Here the type variables that Contrived can accept is Number or some sub-type of Number. But the class itself inherits from ArrayList, so what-ever type the class gets, will determine the type of ArrayList.
Now there is a field called values, which is List<? extends T values>. so what does this mean here?
Does the list can hold anything that extends T (which in turn extend Number).
by PECS (producer extends, consumer super) rule, can I only use this List to read elements but not add to the list.
how will the constructor look like, if I need to pass a list of doubles or some type T?
You can have a constructor that takes a List<T> or a List<? extends T>. Continuing to contrive classes:
class B extends Number{
public double doubleValue() { return 0; }
public float floatValue() { return 0; }
public long longValue() { return 0; }
public int intValue() { return 0; }
}
class C extends B{}
I add a contrived, legal constructor to the Contrived class, taking a List<? extends T>:
public Contrived(List<? extends T> values)
{
this.values = values;
}
This allows me to write a main method such as the following:
public static void main(String[] args)
{
List<C> bList = Arrays.asList(new C(), new C(), new C());
Contrived<B> ci = new Contrived<B>(bList);
}
I can pass a List<C> into the constructor for a Contrived<B>.
Another design for the contrived constructor that is legal for Contrived could be, taking a List<T>:
public Contrived(List<T> values)
{
this.values = values;
}
Such a constructor doesn't allow a List<C> for a Contrived<B>, because now the types must match, as in the following two Contrived examples:
public static void main(String[] args)
{
List<C> cList = Arrays.asList(new C(), new C(), new C());
Contrived<C> ci = new Contrived<C>(cList); // must match now
}
And
public static void main(String[] args)
{
List<B> bList = Arrays.asList(new B(), new B(), new B());
Contrived<B> ci = new Contrived<B>(bList); // must match now
}

inheritance & collections related compile error

I'm trying to figure out why this code won't compile.
I have interface A extended by interface B.
Class C which implements interface B.
When I call a method that takes in a single object of type A, I can pass in an object of type C and it's fine.
When I call a method that takes in a java.util.List of type A, I cannot pass in a java.util.List of objects of type C. Eclipse generates the following error:
The method addAList(List) in the type Test1 is not applicable for the arguments (List)
Source code example is below.
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public void addASingle(A a) {
return;
}
public void addAList(List<A> aList) {
return;
}
// **********************************
public static void main(String[] args) {
Test1 t = new Test1();
C c1 = new C();
List<C> cList = new ArrayList<C>();
cList.add(c1);
t.addASingle(c1); // allowed
t.addAList(cList); // The method addAList(List<Test1.A>)
// in the type Test1 is not applicable for the arguments (List<Test1.C>)
}
// **********************************
public static interface A {
}
public static interface B extends A {
}
public static class C implements B {
}
}
A List<Car> is not a List<Vehicle>. If it was, you could do the following:
List<Car> cars = new ArrayList<>();
List<Vehicle> vehicles = cars;
vehicles.add(new Bicycle());
and you would end up with a list of cars which contains a bicycle. It would ruin the type-safety of generic collections.
You probably should used a List<? extends A> instead of List<A>. List<? extends A> means: a List<some class which is A or which extends A>.
It expects List and you are passing List,
Change it to
public void addAList(List<? extends A> aList) {
return;
}
it expects List of type A....write it in your method signature.
public void addAList(List<? extends A> aList) {
return;
}
by writing this you declare that..your method expects any List which contains any subtype of A...This is called wildcard.

Categories

Resources