Here's my sample code:
public class MyList<T extends Number> {
private List<T> items;
public void func() {
items.add(Integer.valueOf(1));
}
}
I think I should be able to add integer to items, but compilation fails:
Required type: T
Provided: Integer
Anyone knows what's wrong here?
Let us consider a more complete version of your example:
public class MyList<T extends Number> {
private List<T> items = new ArrayList<>();
public void func() {
items.add(Integer.valueOf(1));
}
}
Suppose for the sake of argument that the compiler says that is OK.
And now we will create an instance and call the func method:
MyList<Double> myDoubles = new MyList<>();
myDoubles.func();
Here is what happens.
We create a MyList instance where T is Double. That's OK: the Double class implements the Number interface.
The items has a notional type of List<Double> and we initialize it with an ArrayList. So we now have a list what should only contain Double values.
In the call to func we attempt to add an Integer to the List<Double>. That is wrong!
That is what the compilation error is saying with the Required type: T Provided: Integer message.
To spell it out, the compiler expects a value whose type is the type that T is going to be at runtime. But you have given it Integer. While Integer implements the Number interface, it is not necessary the same as what T will be at runtime. That is the root cause of your compilation error.
So what is the solution?
Well it depends on what the (actual) problem that this example is intended to solve. If you want item to be able to hold any Number, you should change
private List<T> items = new ArrayList<>();
to
private List<Number> items = new ArrayList<>();
and items.add(Integer.valueOf(1)) should work.
On the other hand, if you want to add 1 to items as an instance of the runtime type of T, that is much more difficult. The problem is that the code of MyList (as written) does not and cannot know what that type is! So, you need to EITHER pass the T instance representing 1 as a parameter to func OR pass a Class<T> parameter to func or the constructor and use reflection to create the instance of that class to represent 1.
But if you want something to auto-magically convert the Integer to what ever the actual runtime type of T is ... that is not possible.
When you're using a so-called bounded type parameter <T extends Number> means that type T is restricted by an upper bound expected to be a Number or one of its subtypes.
There are plenty of options if you would think about subtypes of Number: BigDecimal, AtomicLong, etc. That mean that your List<T> at runtime might appear to be a List<AtomicLong> and since behavior of generic types is invariant we would not be able to add anything that is not of type AtomicLong into such list (no Strings, no Integers, etc.).
Therefore, compiler would disallow to add an Integer into a List<T>, where T can be anything that extends Number (or the Number itself), because it can't be sure that it's type-safe.
Related
I have a method, that maps elements of a collection to other object and returns a collection containing the mapped elements. I want the returned Collection to be of the same Collection-type as the input Collection, but with a different element type.
My method would look something like this:
<E extends OriginalElement, T extends TargetElement,
C extends Collection<E>, R extends C<T>> R map(C elementsToMap) {
// snip
}
Obviously the part R extends C<T> doesn't work.
How can I specify that return type to be the same subclass of Collection as Type C, but with element type T instead of E?
You can't, I don't think, because e.g. ArrayList<String> and ArrayList<Integer> are essentially unrelated types.
Plus, when you say "same generic Collection-type", do you mean:
"if I give you some subclass of ArrayList, you'll give me back an instance of java.util.ArrayList"; or
"if I give you a specific subclass of ArrayList, you'll give me back an instance of the same specific subclass of ArrayList"?
Not only is that hard, because in general you don't know how to instantiate arbitrary subclasses, you might not be able to create such an instance, for example if the input is an IntegerArrayList (extends ArrayList<Integer>), and you want to map the elements to Strings. So, whilst you could return a java.util.ArrayList<String> in that case, you can't have a generic solution because you need some knowledge of "which type to instantiate in this specific case".
I am going to make an unquantified assertion that a small handful of collection types can handle most cases. So, provide overloads for these specific types:
</* type variables */> ArrayList<T> map(ArrayList<E> input) { ... }
</* type variables */> HashSet<T> map(HashSet<E> input) { ... }
</* type variables */> ImmutableList<T> map(ImmutableList<E> input) { ... }
// etc.
and then provide a general method for the other cases, and leave it up to callers to specify the collection type they want:
</* type variables */> Stream<T> map(Collection<E> input) { ... }
and then call the general method from the specific methods:
</* type variables */> ArrayList<T> map(ArrayList<E> input) {
return map((Collection<E>) input).collect(toCollection(ArrayList::new));
}
// etc.
I am new to generics and read in a article "A parameterized type, such as ArrayList<T>, is not instantiable — we cannot create instances of them".
Full quote, from Java in a Nutshell:
A parameterized type, such as ArrayList<T>, is not instantiable - we
cannot create instances of them. This is because <T> is just a type
parameter - merely a place-holder for a genuine type. It is only when
we provide a concrete value for the type parameter, (e.g.,
ArrayList<String>), that the type becomes fully formed and we can
create objects of that type.
This poses a problem if the type that we want to work with is unknown
at compile time. Fortunately, the Java type system is able to
accommodate this concept. It does so by having an explicit concept of
the unknown type which is represented as <?>.
I understand that it should not be instantiable since the concrete (actual) type is not known. If so, why does the below code compiles without an error?
public class SampleTest {
public static <T> List<T> getList(T... elements) {
List<T> lst = new ArrayList<>(); // shouldn't this line return an error?
return lst;
}
}
I know there is a gap in my understanding of generics here. Can someone point out what am i missing here?
Because T is given as another generic type argument.
It's the whole purpose of generics to make the type parameterizeable. So the caller can specify the type. This can be done in multiple layers: the caller may also be generic and let its caller specify the type.
public static void main(String[] args)
{
foo(7);
}
public static <T> void foo(T value)
{
bar(value);
}
public static <U> void bar(U value)
{
baz(value);
}
public static <V> void baz(V value)
{
System.out.println(value.getClass().getSimpleName());
}
It prints out
Integer
A parameterized type, such as ArrayList<T>, is not instantiable
Means: You cannot create ArrayList of an unknown T. It must be specified at compile time. But it can be done indirectly, by another generic. In your case, it's another T, which will be specified again by the caller of your generic getList.
The wildcard <?> is something different. It is used to specify compatibility. <?> is the syntax to avoid specification of the type. You can use extends to require a basetype or interface. However, you cannot create instances with wildcards.
List<?> list = new ArrayList<String>();
list = new ArrayList<Integer>();
This wouldn't be possible otherwise. It makes most sense when using it in parameter specifications, for instance:
public static int foo(List<? extends Comparable> list)
{
return list.get(1).compareTo(list.get(2));
}
It's very confusing of this book. It assumes that <?> somehow solves the problem that a List with unknown T cannot be instantiated. IMHO, this is rubbish. T must be specified to create an instance.
The code that you mention can compile because the Object "lst" is not actually initialized until the method is called. Since the method knows that it will be getting a var-args argument of type T, it can compile in this scenario. Take the example Wrapper class below for example:
public class Wrapper<T> {
public static <T> List<T> getList(T... elements){
List<T> lst = new ArrayList<>();
for(T element: elements) {
lst.add(element);
}
return lst;
}
}
This code can compile because the method hasn't been called. When the method is called, Type T will be the type that we pass as the var-args argument and the code will have no issue compiling. Lets test this in our main method:
public static void main( String[] args ){
System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));
}
And the output is:
[Hi, Hello, Yo]
However, lets generate a compile-time error to see what the article is talking about within our main method:
Wrapper<T> myWrap = new Wrapper<>();
We are actually trying initialize a generic Object of the Wrapper class in the code above, but is unknown. Since the value for the placeholder will be unknown even when we call the method, it results in a compile-time error, whereas creating a List of type T within the getList method does not cause a compile-time error because it will be initialized with a type when the method is called.
once you call the method -> you are using a concrete value.
the method defines T and later you use it in the return type and the parameter list.
public static <T> List<T> getList(T... elements)
once you will send the first parameter from specific type -> the contract will force you for the next parameters.
List<? extends Object> list = getList("", 1); -> in this case java doesnt find common between string and integer so it uses the most basic connection "Object"
List<String> list2 = getList("test", "test2"); -> here you can see that because all of the parameters are Strings - java find that in common and use it as the T.
The specific passage from the book doesn't make any sense and is wrong. new ArrayList<T>() is perfectly fine provided that we are in the scope of a type parameter named T (either a type parameter of a generic class that we are in, or a type parameter of the generic method we are in).
new ArrayList<T>() can no less be instantiated than new ArrayList<String>() -- both compile to the same bytecode and both just instantiate an ArrayList object at runtime. The object doesn't know anything about its type parameter at runtime, and therefore no knowledge of T at runtime is needed to instantiate it. The type parameter in an instantiation expression of a parameterized type (new ArrayList<T>) is just used by the compiler to type-check the parameters passed to the constructor (there are none in this case) and to figure out the type returned by the expression; it is not used in any other way.
And by the way the method does not need to receive any parameters of type T, or of any type containing T, in order for this to work. A method that receives no arguments can still instantiate and return an ArrayList<T> perfectly fine:
public static <T> List<T> emptyList() {
List<T> lst = new ArrayList<T>();
return lst;
}
Also, the section in the book where this statement appears in doesn't really have anything to do with instantiation -- the section is about wildcards, and wildcards don't really have anything to do with object instantiation at all. So I am not really sure why they are mentioning it (incorrectly) there.
I am reading on Generics in Java atm, and things go a little slow would love some help. I read from Oracles own database:
https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
At the bottom we can see List<Integer> is a subtype of List<? extends Number>
I also came across this stackoverflow question:
Java Generic type : difference between List <? extends Number> and List <T extends Number>
Which says in one answer: this is true:
((List<Integer>)list).add((int) s);
I have verified it, so that's ok. But I don't understand It completely.
What if the Wildcard is the Short class and I add a number higher than 2^15-1(=32767) Shouldn't it give error?
I even tried something like this and it works fine:
import java.util.*;
class CastingWildcard{
public static void main(String[] args){
List<? extends Number> list = new ArrayList<Short>();
int s=32770;
((List<Integer>)list).add((int) s);
System.out.println(list.get(0));
}
}
To sum up: Why Can I cast List<? extends Number> to List<Integer> when the wildcard could be Short, and even Byte, which also extends Number?
The cast makes the compiler ignore the fact, that the types may not be assignable.
At runtime the type parameters are unimportant, see type erasure.
The ArrayList internally stores the content in a Object[] array, which means you can add any reference type to the list object, if you "abuse" casting.
You may get a exception when you retrieve a Object though, since there's a cast hidden in the get statement.
Example:
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
List<String> list2 = (List) list;
list2.add("Hello World");
Integer i = list.get(0); // works
String s = list2.get(3); // works
s = list2.get(1); // ClassCastException
i = list.get(3); // ClassCastException
You can cast an object to anything you want, but it might fail at runtime. However since generics information isn't present during runtime, your code becomes essentially ((List)list).add(s);. At that point list will take any object, not just a Number. Generics can help you avoid casts and keep type safety during compile time, but during runtime they don't matter anymore.
I have written below code
class Student {}
class Student1 extends Student {}
class Student2 extends Student {}
List<? extends Student> emp = new ArrayList<>();
emp.add(new Student()); // I do not want this to happen. at compile time it should give me error
emp.add(new Student1()); // this should happen
emp.add(new Student2()); // this should happen
But in above code its not working throwing compile error in all 3 adds.
Any pointers?
No, there is no such option in Java to constrain the type parameter to just subtypes of a given type. It looks like you have taken the meaning of the syntax ? extends Type a bit too literally. Type there is the upper bound of the wildcard, and the bounds are always inclusive.
If your idea was the way it really worked, there would be no type by which you could refer to a general item of your list: the narrowest such type is Student.
In java Generics are not co-variant. So List<T> is not a sub-type of List<S> if T is a sub-type of S. This was done to ensure static, compile time type safety. If it were allowed you could do things like following:
List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList;
numberList.add(1.0); // Breaks the contract that intList will contain only Integers
// (or Objects of classes derived from Integer class)
Now to allow functions which could work on Lists containing anything which extends some base class, Bounded wildcards were introduced. So for example, if you wish to write a generic add method which returns the sum of all elements in a List (irrespective of whether the list if of Type Integer, Double, Float), you can write the following code
double add(List<? extends Number> numberList) {
double ans = 0;
for (Number num : numberList) {
ans += num.doubleValue();
}
return ans;
}
The argument can be List of any Object which extends Number such as List<Double>, List<Float>, List<Short>, List<Integer>.
Now coming to your question, when you say a List is of Type <? extends Student> and add Student1() to it, the compiler is not able to verify whether the type is correct or not and it breaks the compile time safety. (Because ? is an unknown type). Normally you cannot add anything to a List having a bounded wildcard as its type (except null and except if you follow a rather complicated process). So either you can declare your list as of type Student but that will mean that your first add statement would not throw an error.
In most practical scenarios, this kind of case is handled by making Student abstract and implementing all the common functionality in the abstract class while declaring your list as of type Student.
Also if you ever want to add to a bounded wildcard type, you can write a helper function like this and call it in your method (This also accepts Student as valid type. There's no way to force a Type to extend something because type resolution is always inclusive in Java):
private <Student, S extends Student> void addd(List<Student> l, S element) {
l.add(element);
}
and call it as:
addd(emp, new Student());
addd(emp, new Student1());
addd(emp, new Student3());
Use "List<? extends Student>" when you want to access the existing elements of List.
Use "List<? super Student>" when you want to set the elements to the List.
This is a follow-up to my previous question but since the previous thread was a long one, i decided to start another thread pertaining to the almost same topic.
public class GenericMethodInference {
static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}
static <T> void test4(List <T> t1, List <T> t2) {}
public static void main(String [] args) {
List <Object> c = new LinkedList<Object>();
List <? extends Object> d = new ArrayList<Integer>();
List e = new ArrayList<Integer>();
test1("Hello", new Integer(1)); // ok clause (1)
GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
test3("Hello", c); // ok clause (3)
test4(d,d) // clause (4) Error due to different type capture generated
}
Note: If you move your cursor over each clause, you will see the inference being generated and displayed on Eclipse:
a. Clause (1) will produce <? extends Object> test1 <? extends Object, ? extends Object>
b. Clause (2) will produce exactly what's defined in the actual type parameter
c. Clause (3) will produce <Object> test3 <Object, List <Object>>
Questions:
Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?
why clause (3) produce <Object> instead of <? extends Object>?
Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?
Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?
This is the best question out of the three. My thinking is that the compiler/Eclipse doesn't want to assume that Object is necessarily the type T that is inferred between String and Integer, so it plays it safe. As #bringer128 pointed out, String and Integer also both implement Serializable and Comparable - so these types are also candidates for the inferred type of the method.
It's worth noting that the following code gives the compiler error "illegal start of type":
GenericMethodInference.<? extends Object>test1("Hello", new Integer(1));
This is because it's invalid to specify a wildcard as a method's type parameter. So the fact you're seeing that in the tooltip has to do with a subtlety of the compiler's/Eclipse's facility to report this information - it has determined only that T is within its bounds, not what it is.
Remember that Java's implementation of generics is solely for the convenience/sanity of programmers. Once compiled into bytecode, type erasure will have gotten rid of any notion of T. So in its checking, the compiler only needs to ensure that a valid T can be inferred, but not necessarily what it is.
why clause (3) produce <Object> instead of <? extends Object>?
Because in this case, the fact that a List<Object> is passed in where a List<T> is expected tells the compiler that T is exactly Object.
Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?
It isn't safe for the compiler to assume that d actually refers to the same object, even between evaluating parameters. For example:
test4(d,(d = new ArrayList<String>()));
In this case, a List<Integer> would be passed into the first parameter, and an List<String> into the second - both from d. Since this scenario is possible, it's easier for the compiler to play it safe.
The test1() case is actually quite sinister. see JLS3 15.12.2.7.
We are not supposed to know the details of type inference - in most cases intuition coincides with the algorithm. Alas that's not always the case, as in the seemingly trivial test1() example.
The constraints we have is T :> String and T :> Integer ( ":>" means super type)
This leads to T=lub(String,Integer), lub means "least upper bound".
Since String <: Comparable<String> and Integer <: Comparable<Integer>, this leads to lci({Comparable<String>, Comparable<Integer>}), which yields Comparable<? extends lub(String,Integer)>, i.e. Compable<? extends T>
In the end, we have T = Serializable & Compable<? extends T>, a self referenced definition! Spec calls it "infinite type":
It is possible that the process above yields an infinite type. This is permissible, and Java compilers must recognize such situations and represent them appropriately using cyclic data structures.
Let's find out how javac represents it: (javac 7)
static <T> T test1(T t1, T t2) {}
public static void main(String[] args)
{
Void x = test1("Hello", new Integer(1));
}
error: incompatible types
required: Void
found: INT#1
where INT#1,INT#2 are intersection types:
INT#1 extends Object,Serializable,Comparable<? extends INT#2>
INT#2 extends Object,Serializable,Comparable<?>
That doesn't seems right; it's not really recursive; it appears that javac detects recursion in lub() and gives up, resulting a less specific type Comparable<?>