Using wildcards with nested generics in java - java

I have a class that can a list of take both a generic Number and a Double to perform a certain calculation.
This works fine:
public class Test<T extends Number> {
public void testGeneric(List<T> list){
doTest(list);
}
public void testExplicit(List<Double> list){
doTest(list);
}
public void doTest(List<? extends Number> testList){}
}
However, if the argument in question is a nested type, it doesn't compile anymore:
public class Test<T extends Number> {
public void testGeneric(List<List<T>> list){
doTest(list);
}
public void testExplicit(List<List<Double>> list){
doTest(list);
}
public void doTest(List<List<? extends Number>> testList){}
}
Note that the functionality of the list doesn't matter, the second List type can be anything generic, for instance a wrapper around the T type. I don't really see why there should be any difference. Is there a way around this?
Thanks!

As you apparently have realized, a List<Double> is not a subtype of List<Number>; you need the wildcards to make the inheritance "propagate" into the generic types: a List<Double> is a subtype of List<? extends Number>. However, the wildcards must go all the way from the outermost level: a List<X> is a subtype of List<List<? extends Number>> only if X is exactly a List<? extends Number>. If you want to accept other subtypes of List<? extends Number>, such as List<Double>, you need List<? extends List<? extends Number>>.

Related

Questions about the upper bound of generic in java

please see the following code:
import java.util.ArrayList;
public class Animal{...}
public class Dog{...}
public class TestAnimal{
public static void killAll(ArrayList <T extends Animal> animals){
System.out.println("animals are dead");
}
public static void main(String[] args){
ArrayList<Animal> simonAnimal = new ArrayList<>();
ArrayList<Dog> simonDog = new ArrayList<>();
killAll(simonAnimal);
killAll(simonDog);
}
}
the line that causes the problem is:
public static void killAll(ArrayList <T extends Animal> animals){
so what I want to do is to be able to use killAll() method on the any ArrayList that contains objects that are the sub class of Animal, in this case - the Dog class. I don't know what's wrong with my code. please help!
the error message is:
Incorrect number of arguments for type ArrayList; it cannot be parameterized with arguments <T, Animal>
I just replaced
<T extends Animal>
as
<? extends Animal>
it works, but can someone tell me why doesn't work?
You're trying to declare a type variable T and its bounds, and you're trying to use it all at once, which you can't do in Java generics. I don't get that error message; I get "unexpected bound".
First, declare the type variable and its bounds, in angle brackets before the return type, and refer to it as the type argument to the method parameter. This will remove the compiler error.
public static <T extends Animal> void killAll(ArrayList<T> animals) { ... }
But we can do better. First, program to the interface and use List<T>. Also, if you aren't using the specific type of T in the actual body, then you can use an upper-bounded wildcard instead of an explicit type variable.
public static void killAll(List<? extends Animal> animals) { ... }
The correct syntax for declaring a type variable on a method looks like this:
public static <T extends Animal> void killAll(ArrayList<T> animals){
System.out.println("animals are dead");
}
I know it might seem unintuitive, but even though Dog is a subtype of Animal, ArrayList is not a subtype of ArrayList. As pointed out by others, you can use a wildcard type.
ArrayList<? extends Animal> denotes any generic ArrayList type whose type parameter is a subclass of Animal such as ArrayList.
public static void killAll(List <? extends Animal> animals){
It is also better to use a List instead of a concrete implementation like ArrayList
The correct way to make generic methods like your example is this:
public static <T extends Animal> void killAll(List<T extends Animal> animals){#code}
As other colleagues told you, it is better to receive a list than an arraylist.

What is the difference between <? extends Base> and <T extends Base>?

In this example:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile() fails to compile with:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
while compiles() is accepted by the compiler.
This answer explains that the only difference is that unlike <? ...>, <T ...> lets you reference the type later, which doesn't seem to be the case.
What is the difference between <? extends Number> and <T extends Number> in this case and why doesn't the first compile?
By defining the method with the following signature:
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
and invoking it like:
compiles(new HashMap<Integer, List<Integer>>());
you're matching T against the type you're providing.
In the jls §8.1.2 we find, that (interesting part bolded by me):
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments. All of these parameterized types share the same class at run time.
In other words, the type T is matched against the input type and assigned Integer. The signature will effectively become static void compiles(Map<Integer, List<Integer>> map).
When it comes to doesntCompile method, jls defines rules of subtyping (§4.5.1, bolded by me):
A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):
? extends T <= ? extends S if T <: S
? extends T <= ?
? super T <= ? super S if S <: T
? super T <= ?
? super T <= ? extends Object
T <= T
T <= ? extends T
T <= ? super T
This means, that ? extends Number indeed contains Integer or even List<? extends Number> contains List<Integer>, but it's not the case for Map<Integer, List<? extends Number>> and Map<Integer, List<Integer>>. More on that topic can be found in this SO thread. You can still make the version with ? wildcard work by declaring, that you expect a subtype of List<? extends Number>:
public class Example {
// now it compiles
static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
public static void main(String[] args) {
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
In the call:
compiles(new HashMap<Integer, List<Integer>>());
T is matched to Integer, so the type of the argument is a Map<Integer,List<Integer>>. It's not the case for the method doesntCompile: the type of the argument stays Map<Integer, List<? extends Number>> whatever the actual argument in the call; and that is not assignable from HashMap<Integer, List<Integer>>.
UPDATE
In the doesntCompile method, nothing prevents you to do something like this:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
map.put(1, new ArrayList<Double>());
}
So obviously, it cannot accept a HashMap<Integer, List<Integer>> as the argument.
Simplied example of demonstration. Same example can be visualize like below.
static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too
public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}
List<Pair<? extends Number>> is a multi-level wildcards type whereas List<? extends Number> is a standard wildcard type .
Valid concrete instantiations of the wild card type List<? extends Number> include Number and any subtypes of Number whereas in case of List<Pair<? extends Number>> which is a type argument of type argument and itself has a concrete instantiation of the generic type.
Generics are invariant so Pair<? extends Number> wild card type can only accept Pair<? extends Number>>. Inner type ? extends Number is already covariant. You have to make the enclosing type as covariant to allow covariance.
I'd recommend you to look in documentation of generic wildcards especially guidelines for wildcard use
Frankly speaking your method #doesntCompile
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
and call like
doesntCompile(new HashMap<Integer, List<Integer>>());
Is fundamentally incorrect
Let's add legal implementation:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
List<Double> list = new ArrayList<>();
list.add(0.);
map.put(0, list);
}
It is really fine, because Double extends Number, so put List<Double> is absolutely fine as well as List<Integer>, right?
However, do you still suppose it's legal to pass here new HashMap<Integer, List<Integer>>() from your example?
Compiler does not think so, and is doing his (its?) best to avoid such situations.
Try to do the same implementation with method #compile and compiler will obviously does not allow you to put a list of doubles into map.
static <T extends Number> void compiles(Map<Integer, List<T>> map) {
List<Double> list = new ArrayList<>();
list.add(10.);
map.put(10, list); // does not compile
}
Basically you can put nothing but List<T> that's why it's safe to call that method with new HashMap<Integer, List<Integer>>() or new HashMap<Integer, List<Double>>() or new HashMap<Integer, List<Long>>() or new HashMap<Integer, List<Number>>().
So in a nutshell, you are trying to cheat with compiler and it fairly defends against such cheating.
NB: answer posted by Maurice Perry is absolutely correct. I'm just not sure it's clear enough, so tried (really hope I managed to) to add more extensive post.

Unexpected generic behavior with TypeToken nesting generic types

I have TypeToken class used to represent some generic type like this:
TypeToken<List<String>> listOfStrings = new TypeToken<List<String>> {}
And this works fine, TypeToken is just class TypeToken<T> {} with simple method to get that type.
Now I wanted to create simple methods for common type like List for more dynamic usage:
TypeToken<List<? extends Number>> numbers = list(extendsType(Number.class))
using:
public static <T> TypeToken<? extends T> extendsType(Class<T> type) {return null;}
public static <T> TypeToken<List<T>> list(TypeToken<T> type) {return null;}
(return nulls as I'm only asking about compiler not logic)
But for some reason this does not work how I would expect: (as code that I expected to be valid does not compile, and code that I expected to be invalid does compile)
class TypeToken<X> {
static <T> TypeToken<? extends T> extendsType(Class<T> type) {return null;}
static <T> TypeToken<List<T>> list(TypeToken<T> type) {return null;}
static void wat() {
TypeToken<List<? extends Number>> a = new TypeToken<List<? extends Number>>() {}; // valid
TypeToken<List<? extends Number>> b = list(extendsType(Number.class)); // invalid, why?
TypeToken<? extends List<? extends Number>> c = list(extendsType(Number.class)); // valid, why?
}
}
What I'm doing wrong here? And what is causing generics to behave like this?
I'm using JDK 11, but I also tested this on JDK 8
Compiler error:
error: incompatible types: no instance(s) of type variable(s) T#1,CAP#1,T#2 exist so that TypeToken<List<T#1>> conforms to TypeToken<List<? extends Number>>
TypeToken<List<? extends Number>> b = list(extendsType(Number.class)); // invalid, why?
^
where T#1,T#2 are type-variables:
T#1 extends Object declared in method <T#1>list(TypeToken<T#1>)
T#2 extends Object declared in method <T#2>extendsType(Class<T#2>)
where CAP#1 is a fresh type-variable:
CAP#1 extends T#2 from capture of ? extends T#2
I think that at the core this question that has already been asked in a similar form several times. But I'm not sure, because this is one of the constellations where it is particularly hard to wrap one's head around the concept.
Maybe one can imagine it like that:
The extendsType method here returns a TypeToken<? extends Number>
In the call to the list method, the ? is captured in the type parameter T. This capture can (roughly speaking) be imagined as a new type variable - something like X extends Number, and the method returns a TypeToken<List<X>>
Now the TypeToken<List<X>> is not assignable to TypeToken<List<? extends Number>>, as of the usual constraints. Maybe this table, with <=== indicating assignability and <=/= indicating non-assignability, will help:
Number <=== Integer
TypeToken< Number> <=/= TypeToken<Integer>
TypeToken<? extends Number> <=== TypeToken<Integer>
List<? extends Number> <=== List<X>
TypeToken< List<? extends Number>> <=/= TypeToken<List<X>>
TypeToken<? extends List<? extends Number>> <=== TypeToken<List<X>>
So in the end, the answer to the question which super-subtype relationships exist among instantiations of generic types in the famous Generics FAQ by Angelika Langer is probably once more the most relevant here:
Super-subtype relationships among instantiations of generic types are determined by two orthogonal aspects.
On the one hand, there is the inheritance relationship between a supertype and a subtype.
...
On the other hand, there is a relationship based on the type arguments. The prerequisite is that at least one of the involved type arguments is a wildcard. For example, Collection<? extends Number> is a supertype of Collection<Long>, because the type Long is a member of the type family that the wildcard " ? extends Number " denotes.
I think that the idea of the capture being a "new type X" sounds convincing, even though there is some handwaving involved: This happens somewhat "implicitly" in the resolution process, and not visible in the code, but I found it helpful, to some extent, when I wrote a library for types where all these questions came up...
Updated to elaborate this further, referring to the comment:
I mentioned that the types are not assignable "as of the usual constraints". Now one could argue about where these "constraints" come from. But they basically always have the same reason: The types are not assignable, because if they were assignable, the program would not be type safe. (Meaning that it would be possible to provoke a ClassCastException one way or the other).
Explaining where the type safety is lost here (and why a ClassCastException could be caused) involves some contortions. I'll try to show it here, based on the example that was referred to in the comment. Scroll down to tl;dr for a an example that has the same structure, but is much simpler.
The example from the comment was this:
import java.util.*;
import java.io.*;
class Ideone
{
public static class LinkTypeToken<T> extends TypeToken<List<T>> {}
public static class TypeToken<T> {}
public static void main (String[] args) throws java.lang.Exception {
LinkTypeToken<Number> listA = null;
TypeToken<List<Number>> listB = listA;
LinkTypeToken<? extends Number> listC = null;
TypeToken<? extends List<? extends Number>> listD = listC;
// error: incompatible types: LinkTypeToken<CAP#1> cannot be
// converted to TypeToken<List<? extends Number>>
// TypeToken<List<? extends Number>> listE = listC;
// ^
// where CAP#1 is a fresh type-variable:
// CAP#1 extends Number from capture of ? extends Number
TypeToken<List<? extends Number>> listE = listC;
}
}
The line that causes the compilation error here does so because if it was possible to assign these types, then one could do the following:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
class IdeoneWhy
{
public static class ArrayListTypeToken<T> extends TypeToken<ArrayList<T>>
{
ArrayList<T> element = null;
void setElement(ArrayList<T> element)
{
this.element = element;
}
}
public static abstract class TypeToken<T>
{
abstract void setElement(T element);
}
public static void main(String[] args)
{
ArrayListTypeToken<? extends Number> listC = new ArrayListTypeToken<Integer>();
TypeToken<? extends List<? extends Number>> listD = listC;
// This is not possible:
//TypeToken<List<? extends Number>> listE = listC;
// But let's enforce it with a brutal cast:
TypeToken<List<? extends Number>> listE =
(TypeToken<List<? extends Number>>)(Object)listC;
// This throws a ClassCastException
listE.setElement(new LinkedList<Integer>());
}
}
So the fact that a TypeToken<List<? extends Number>> is not assignable from a TypeToken<? extends List<? extends Number>> indeed only serves the purpose of preventing a ClassCastException.
tl;dr :
The simpler variant is this:
import java.util.ArrayList;
import java.util.List;
public class WhySimpler
{
public static void main(String[] args)
{
List<Float> floats = new ArrayList<Float>();
// This is not possible
//List<Number> numbers = floats;
// Let's enforce it with a brutal cast:
List<Number> numbers = (List<Number>)(Object)floats;
Integer integer = 123;
// This is possible, because Integer is a Number:
numbers.add(integer);
// Now, we ended up placing an Integer into a list that
// may only contain Float values.
// So this will cause a ClassCastException:
Float f = floats.get(0);
}
}
Conversely, when the type is declared as List<? extends Number>, then the assignment is possible, because it is not possible to sneak an invalid type into such a list:
List<Float> floats = new ArrayList<Float>();
List<? extends Number> numbers = floats;
numbers.add(someInteger); // This is not possible
Long story short: It's all about type safety.

What is the difference between generics

Ok, i read this question in this web-site and it did not answer me the main point.
Assume we have like this:
public <T extends Number> void someMethod(List<T> list)
AND
public void someMethod(List<? extends Number> list)
Consider that I do not need any lower bound or upper list bound. So rejection of these things, what is the difference between it in this situation? Because ? is List of unknown and T is List of T, but from one side they are similar in this situation (methods above).
(P.s. I know differences between it, but in this situation it looks for me very similar and as I consider it does not matter what to use.)
Here:
public <T extends Number> void someMethod(List<T> list)
You can make use of the unknown type T, in your code. Eg:
T obj = list.get(0);
list.add(obj);
This is not possible here:
public void someMethod(List<? extends Number> list)
List <? extends Number> means anything that extends Number here.
So assume you have classes like
public class Example<T extends Number> {
public <T extends Number> void someMethod(List<T> list) ...
public <T extends Number> void someOtherMethod(List<T> list) ...
}
public class SomeClass extends Number {
}
public class SomeClass2 extends Number {
}
and you created an instance like
Example<SomeClass> myExample = new Example<SomeClass>();
so when you want to use your methods here, the input parameters must be insances exactly of the same class SomeClass, we cant use SomeClass2 here. For example the following is invalid:
List<SomeClass2> someClass2 = new ArrayList<SomeClass2>();
example.someMethod(someClass2);
but if you use <? extends Number> as an input parameter to the someMethod then it is possible. You can pass anything that extends Number, someClass2 or 3 , 4 or any other class.

Raw types inside of generic definition

I wonder why the following generic definition does not produce a compiler warning:
class MyClass<T extends List> { }
and how the above definition is different to
class MyClass<T extends List<?>> { }
Whenever you read about generics, you read about how raw types should be avoided and consequently, whenever you handle generic types, you get a compiler warning. The raw type inside of the first definition does however not create such a warning.
Secondly, I wonder how the exact subtyping definition between raw types and generic types are. According to this summary, raw types are kind of an "opt-out" of type checking such that type checking is simply inactive whenever a raw type is involved. Is this assumption correct? And how does this effect the above "raw" generic definitions?
Thank you for your help!
UPDATE: I understand what you are saying. However, this is not what I am confused about. Look at this scenario:
class MyClass<T extends MyClass<T>> {}
public void callWildcard1(MyClass<?> node) {
callBound1(node);
}
public <T extends MyClass<T>> void callBound1(T node) {
// Do something
}
public void callWildcard2(MyClass<?> node) {
callBound2(node);
}
public <T extends MyClass> void callBound2(T node) {
// Do something
}
The first call from callWildcard1 to callBound1 is not allowed because of the generic constraint. The second is however allowed. How can I perform the first call without "inner raw types"? I don't see why the compiler would forbid the first. Shouln't any parameter valid wildcard parameter imply ? extends MyClass<?>?
UPDATE 2: I found out by trial and error, that I can solve the problem by defining:
public <T extends MyClass<? extends T> void callBound2(T node) {
// Do something
}
even though I do not quite understand why. But there is even more confusion, when looking at this example: (this is a very simple version of what I am actually trying to do.)
public void call() {
genericCall1(new MyFilter<MyClass<?>>(), MyClass.class);
genericCall2(new MyFilter<MyClass<?>>(), MyClass.class);
}
public <T extends MyClass<? extends T>> void genericCall1(MyFilter<T> filter, Class<? extends T> filterBoundary) {
// Do something.
}
public <T extends MyClass<? extends T>, U extends T> void genericCall2(MyFilter<T> filter, Class<? extends U> filterBoundary) {
// Do something.
}
class MyClass<T extends MyClass<T>> { }
class MyFilter<T extends MyClass<? extends T>> { }
Why is genericCall1 prohibited and genericCall2 is not? Again, I found the solution by an academic guess instead of true understanding. Sometimes, when working with Java and its generics I want to cry...
The difference is that when you use class MyClass<T extends List> { } inside MyClass you lose type safety.
for example:
class A <T extends List<?>>{
void someFunc(T t) {
t.add(new Object());//compilation error
}
}
class B <T extends List>{
void someFunc(T t) {
//compiles fine
t.add(new Object());
t.add("string");
t.add(new Integer(3));
}
}

Categories

Resources