Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 368
Dev Null Avatar asked Mar 05 '20 00:03

Dev Null


People also ask

What is the difference between list <? Super T and list <? Extends T?

extends Number> represents a list of Number or its sub-types such as Integer and Double. Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object.

What does Super t mean?

super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .

What does <? Extends E mean in Java?

extends E means that it is also OK to add all members of a collection with elements of any type that is a subtype of E.

What is difference between extends and super?

Producer – If you want to only retrieve the elements from a generic collection, use extends . Consumer – If you want to only put elements into a generic collection, use super . If you do both retrieve and put operations with the same collection, you shouldn't use either extends or super .


2 Answers

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>>());     } } 
like image 174
Andronicus Avatar answered Sep 19 '22 12:09

Andronicus


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.

like image 43
Maurice Perry Avatar answered Sep 19 '22 12:09

Maurice Perry