Wildcard in a list - Java Generics [duplicate] - java

This question already has answers here:
Lists with wildcards cause Generic voodoo error
(5 answers)
Closed 8 years ago.
ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());
Why wouldnt this compile?

Relevant part from the Java tutorial (http://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html):
A list defined by List can be informally thought of as
read-only, but that is not a strict guarantee. Suppose you have the
following two classes:
class NaturalNumber {
private int i;
public NaturalNumber(int i) { this.i = i; }
// ...
}
class EvenNumber extends NaturalNumber {
public EvenNumber(int i) { super(i); }
// ...
}
Consider the following code:
List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35)); // compile-time error
Because List<EvenNumber> is a subtype of List<? extends
NaturalNumber>, you can assign le to ln. But you cannot use ln to add
a natural number to a list of even numbers.
The following operations on the list are possible:
You can add null.
You can invoke clear.
You can get the iterator and invoke remove.
You can capture the wildcard and write elements that you've read from the list.
You can see that the list defined by List is
not read-only in the strictest sense of the word, but you might think
of it that way because you cannot store a new element or change an
existing element in the list.
Another relevant explanation can be found here (his link also explains the issue of wildcards - http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html):
...
It isn't safe to add arbitrary objects to it however:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Since we don't know what the element type of c stands for, we cannot
add objects to it. The add() method takes arguments of type E, the
element type of the collection. When the actual type parameter is ?,
it stands for some unknown type. Any parameter we pass to add would
have to be a subtype of this unknown type. Since we don't know what
type that is, we cannot pass anything in. The sole exception is null,
which is a member of every type.
On the other hand, given a List, we can call get() and make use of
the result. The result type is an unknown type, but we always know
that it is an object. It is therefore safe to assign the result of
get() to a variable of type Object or pass it as a parameter where the
type Object is expected.

Since we don't know what the element type of array stands for, we cannot add objects to it.
Instead, use a temporary list:
ArrayList<A> tempArray = new ArrayList<A>();
tempArray.add(new A())
tempArray.add(new A())
ArrayList<? extends A> array = tempArray;
An example of how to use it
//Vehicle
public abstract class Vehicle {
}
//Car
public class Car extends Vehicle {
}
//HandlerVehicle
public class HandlerVehicle {
private List<? extends Vehicle> _vehicles;
public void addVehicles(List<? extends Vehicle> vehicles) {
_vehicles = vehicles;
//perform operations with Vehicle objects
}
}
//HandlerCar
public class HandlerCar {
private HandlerVehicle _handlerVehicle;
private List<Car> _cars;
public HandlerCar() {
_cars = getCars();
_handlerVehicle = new HandlerVehicle();
_handlerVehicle.addVehicles(_cars);
}
private List<Car> getCars() {
return new ArrayList<Car>();
}
}

You cannot add an A to a List<? extends A>. In order to fix your problem you should simply declare your list as
ArrayList<A> array = new ArrayList<A>();
Note that it is preferable to use an interface for the declaration:
List<A> array = new ArrayList<A>();
This allows you to easily change the concrete type at a later time because you only need to make a single change.

Related

Misunderstanding on Contravariance in Java with code example [duplicate]

This question already has answers here:
Generics <? super A> doesn't allow superTypes of A to be added to the list
(2 answers)
Closed 4 years ago.
I am trying out a easy to understand example about contravariance in Java and having a issue understanding.
In the below example I have List<? super CarBill> list1 . My understanding is i should be able to add an object of any superclass of CarBill. By that logic i should be able to add objects of Bill class to it too right ?
I get a compilation error.
package Generics;
import java.util.ArrayList;
import java.util.List;
public class VarianceTests {
static class Bill{
String vName;
String type;
Bill(String vName){
this.vName=vName;
}
Bill(String vName,String type){
this.vName=vName;
this.type=type;
}
}
static class CarBill extends Bill{
String name;
CarBill(String name)
{
super(name,"Car");
}
}
static class Car<T extends Bill> {
T car;
Car(T car){
this.car=car;
}
String getNameOfCar() {
return car.vName;
}
}
public static void main(String args[]) {
CarBill cBill = new CarBill("Baleno");
Bill bill=new Bill("Whatever");
Car car = new Car(bill); //cBill is valid too as Car accepts <? extends Bill>
List<? super CarBill> list1 = new ArrayList<>();
list1.add(cBill);
list1.add(bill);
}
public void acceptListOfCars(List<? extends Bill> list1) {
Bill b = list1.get(0); //Valid syntax
}
}
Your understanding is mistaken.
List<? super CarBill> means that the list can be a list of any super class of CarBill or CarBill itself. It could be List<Object>, it could be List<Bill>, it could even be List<CarBill>. Which one is it actually? We don't know.
Therefore, you can't add a Bill to a List<? super CarBill> because what if the list is actually a List<CarBill>? You can't add a Bill to a List<CarBill>.
In other words, you can only add CarBill or subclasses of CarBill into a List<? super CarBill>.
If your intention is to create a list that can store any type of Bill, you can create a List<Bill>.
This post might help as well.
Not quite.
Let's start with this code:
List<Integer> listOfInts = new ArrayList<Integer>();
List<Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
The above code won't in fact compile; the second line is an invalid assignment. Your line of thinking would say: But.. why? Number is a supertype of Integer, so, a list of integers is trivially also a list of numbers, no? but then the third line shows why this line of reasoning is incorrect. Java will NOT let you write the above code. What you CAN write is this: The same thing, but this time we tweak the second line:
List<Integer> listOfInts = new ArrayList<Integer>();
List<? extends Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
This time, you get a compiler error on the third line: You cannot add a double to this list. But, if you read from it, you'd get numbers out (not objects). This is all good: The above snippet of code should never compile no matter what we try because it tries to add doubles to a list of ints.
The point is: List<? extends Number> does not mean: "This list contains numbers, or any subtypes thereof". No; just like List x = new ArrayList() is legal java, List<Number> means 'this list contains numbers or any subtypes thereof' because any instance of any subtype of number can itself be used as a Number. List<? extends Number> means: This is a list restrained to contain only instances of some specific type, but which type is not known. What IS known, is that whatever that type is, it's either Number or some subtype thereof.
Hence, you can't add ANYTHING to a List<? extends Number>.
For super, a similar story:
List<? super CarBill> means: This is a list that is restricted to contain only instances of some specific type, but which type is not known. What IS known, is that, whatever type it is, it is either CarBill or some SUPERtype thereof.
The upside of doing this, is that you can add CarBill instances to a List<? super CarBill> variable. When you read from it, you'll get objects out.
My understanding is i should be able to add an object of any superclass of CarBill
No.
A List<? super CarBill> is not a list that will accept objects of any supertype of CarBill. It's a list that will accept objects of some particular supertype of CarBill, but which supertype it is is unknown.
You can add any object of type CarBill, because that is guaranteed be a subtype of type ?. But a supertype of CarBill is not guaranteed to be a subtype of ?.
For instance:
List<? super CarBill> myList = new ArrayList<Bill>();
Object o = "Anything";
Object is a supertype of CarBill. So if you could add any supertype of CarBill to the list, you would be able to add o to the list, which would mean you could add anything to the list.

Cast ArrayList in Java [duplicate]

This question already has answers here:
Java Generics: Cannot cast List<SubClass> to List<SuperClass>? [duplicate]
(10 answers)
Closed 4 years ago.
I am using two class
Class TestClassOne{
//Some methods
//Some methods
//Some methods
}
Class TestClassTwo extends TestClassOne{
//Some Variable
//Some Variable
//Some Variable
}
Now I am having TestClassOne object and can cast to TestClassTwo.
TestClassOne classOne = new TestClassOne();
TestClassTwo classTwo = (TestClassTwo)classOne;
Can I do this same for ArrayList?
For example
ArrayList<TestClassOne> testList1 = new ArrayList<>();
ArrayList<TestClassTwo> testList2 = (ArrayList<TestClassTwo>) testList1;
But I am getting error that can not cast. Is there any other way to cast the ArrayList?
Generic types can't be converted to each other like that. T<A> cannot be casted to T<B> even though B inherits from A.
In this particular case, what you could do is cast each individual element in the array list, and use these to create a new array list.
ArrayList<TestClassTwo> testList2 = testList1.stream().map(x -> (TestClassTwo)x)
.collect(Collectors.toCollection(ArrayList::new))
Obviously, this will only work when the objects stored in the array list are all actually instances of TestClassTwo.
Simple casting TestClassOne-> TestClassTwo will not work, unless your testList1 collection holds instances of TestClassTwo, and only them.
Casting that you want to achieve is impossible, because even though TestClassTwo extends TestClassOne, there is no gurantee, that class TestClassTwo would have a suficient constructor. TestClassTwo may have additional variables, that JVM would not know what to do with. So as you can see this would be problematic even for humans.
==========================================================================
Casting in other way is possible. Because TestClassTwo will have same methods as TestClassOne, because it extends it.
==========================================================================
If you want to hold elements of both TestClassOne and TestClassTwo in same collction, then you can use generic wildcards:
List<? extends TestClassOne> testList1 = new ArrayList<>();
But you will have to check if given element is of type TestClassTwo, before casting.
TestClassOne one = testLis.get(1);
if(one instanceOf TestClassTwo.class) {
(TestClassTwo) one; // handle me
}
one; // can't be casted, but can be adapted
==========================================================================
Other solution would be to use an adapter constructor in TestClassTwo. It will accept TestClassOne as an argument (and optionaly insert additional parameters, if needed). The creation of TestClassTwo objects should be managed by you.
class TestClassTwo extends TestClassOne{
TestClassTwo(TestClassOne in) {...}
}
Then you can use simply adapt:
List<TestClassOne> testList1 = new ArrayList<>();//add some arguments
List<TestClassTwo> testList2 = testList1.stream().map(TestClassTwo::new).collect(toList());
You can use generic class
class TestClassOne<T>
{
}
class TestClassTwo
{
}
Casting like this
List<TestClassOne<TestClassTwo>> list = new List<TestClassOne<TestClassTwo>>();
You can't. Inheritance with Type parameters are not supported.
Instead create a static helper method, which will iterate over one of the list, and cast each individual element, and return the new list. e.g.
public static convert(ArrayList<ClassOne>) {
ArrayList<ClassTwo> b = new ArrayList<ClassTwo>();
forEach(classOne) {
b.add((ClassTwo)classOne);
}
return b;
}

Java Generic ? extends String

While I was going through some generics question I came across this example. Will you please explain why list.add("foo") and list = new ArrayList<Object>() contain compailation issues?
In my understanding List of ? extends String means "List of Something which extends String", but String is final ? can only be String. In list.add() we are adding "foo" which is a string. Then why this compilation issue?
public class Generics {
public static void main(String[] args) {
}
public static void takelist(List<? extends String> list){
list.add("foo"); //-- > error
/*
* The method add(capture#1-of ? extends String) in the
* type List<capture#1-of ? extends String> is not applicable
* for the arguments (String)
*/
list = new ArrayList<Object>();
/*
* Type mismatch: cannot convert from ArrayList<Object> to List<? extends String>
*/
list = new ArrayList<String>();
Object o = list;
}
}
For starters, the java.lang.String class is final, meaning nothing can extend it. So there is no class which could satisfy the generic requirement ? extends String.
I believe this problem will cause all of the compiler errors/warnings which you are seeing.
list.add("foo"); // String "foo" does not extend String
list = new ArrayList<Object>(); // list cannot hold Object which does not extend String
It is true what you say. String is final. And so you can reason that List<? extends String> can only be list of string.
But the compiler isn't going to make that kind of analysis. That is to say, the compiler will not assess the final or non-final nature of String (see comments). The compiler will only let you put null into your list.
You can pull stuff out though.
String s = list.get(0);
While String is final, this information is not used.
And in fact, with Java 9, it may no longer be final (rumor has it that Java 9 may finally get different more efficient String types).
Without knowing it is final, List<? extends String> could be e.g. a List<EmptyString> of strings that must be empty.
void appendTo(List<? extends String> l) {
l.append("nonempty");
}
appendTo(new ArrayList<EmptyStrings>());
would yield a violation of the generic type.
As a rule of thumb always use:
? extends Type for input collections (get is safe)
? super Type for output collections (put is safe)
Type (or maybe a <T>) for input and output collections (get and put are safe, but the least permissive).
I.e. this is fine:
void appendTo(List<? super String> l) {
l.append("nonempty");
}
appendTo(new ArrayList<Object>());
You have already mentioned that String is a final type and therefore there is no point in repeating this fact. The point that is important to note is that none of the following Lists allows adding an element:
List<?> which is a List of anything.
List<? extends SomeType> which is a List of anything that extends SomeType.
Let's understand it with an example.
The List<? extends Number> could be List<Number> or List<Integer> or List<Double> etc. or even a List of some other type that hasn't been defined yet. Since you can not add any type of Number to a List<Integer> or any type of Number to a List<Double> etc., Java does not allow it.
Just for the sake of completeness, let's talk about List<? super Integer> which is List of anything that is a super/parent type of Integer. Will the following compile?
Object obj = 10.5;
list.add(obj);
As you can guess, of course NOT.
What about the following?
Object obj = 10.5;
list.add((Integer) obj);
Again, as you can guess, indeed it will compile but it will throw ClassCastException at runtime. The question is: why did not Java stop us in the first place by failing the compilation itself? The answer is Trust. When you cast something, the compiler trusts that you already understand the cast.
So, the following compiles and runs successfully:
Object obj = 10;
list.add((Integer) obj);
list.add(20);

Declaring generic methods, clarification needed

Consider the following 2 method declarations:
1. public abstract <T extends MetaData> List<T> execute();
2. public abstract List<? extends MetaData> execute();
Both seem to return back a list of objects that extend MetaData.
What is the difference between them please?
In the first case you will allow Java to use type inference and infer the type of T at each call site.
In the second case you will always get a List<? extends MetaData> and so won't be able to assign it to a variable of any narrower type like List<IntegerMetaData>.
If there are any subtypes of MetaData then the first version can only return an empty list or null. The second version may return a list containing instances of MetaData and its subtypes.
Example: Say A and B are subtypes of MetaData and execute returns a list containing an instance of A. The caller might have called execute like so:
List<B> list = execute(); // the second version does not allow this
The caller said he wanted a list of Bs, but got a list containing an A. Due to type erasure the implementation of execute has no way of knowing what the caller asked for. Thus the first version can't be implemented (except to return null or an empty list).
In example 1, you cannot must return a List whose generic type is T, e.g.:
#Override
public <T extends MetaData> List<T> execute() {
List<T> l = new ArrayList<T>();
return l;
}
In example 2, you can return a List whose generic type is just MetaData, e.g.:
#Override
public List<? extends MetaData> execute2() {
List<MetaData> l = new ArrayList<MetaData>();
return l;
}
What is the difference? In the first case, the method has a generic type, T and you must return something that relates to that type. In the second case, you just return a generic type, but the method itself does not have a generic type.

java generics bounds

I have the following code:
public <T extends SomeObject> long doSomething(T someObject){
List<? extends SomeObject> l = new LinkedList<>();
l.add(someObject);
}
this causes a compilation error - telling me that there is no suitable methods found: add(T),
why is that?
If l accept things that extends SomeObject shouldn't it accept someObject as it bounds to extend SomeObject?
List<? extends SomeObject> l
What do you mean by that? Of course it will generate an error.
Take this example :SomeObject is Fruit, you have 2 derived classes Apple and Orange
Your list what will it contain? Apples or Oranges? The compiler cannot tell. So it generates error.
If you replace List<? extends SomeObject> l with List<SomeObject> l. Then this will work because Apple and Orange are both Fruit.
I would advise you to use this statement:
List<T> l = new LinkedList<T>();
This is no less type-safe then
List<SomeObject> l = new LinkedList<SomeObject>();
and additionally gives you an opportunity to get objects of type T from the list without casting. T is already SomeObject so no casting required to call methods of SomeObject on T.
And all that with less typing!
Back to the problem.
First thing to note is that wildcard type "?" means unknown, this is important.
You may, however, specify an upper (? extends) or a lower (? super) constraint to it.
You declared a list as "List".
List is known to have objects of SomeObject inside. but! the exact type of objects is unknown.
Compiler can not say if there are instances of "class A extends SomeObject" or instances of "class B extends SomeObject" inside the list.
If you call list.get() it can only say that there will be an object of type SomeObject.
SomeObject obj = list.get(1); // Ok
But inserting an object of any(!) type is unsafe because the actual type of elements in the list is unknown.
You could wonder why wildcard type ever exists.
It is here to lower restriction in type casting that will be too strict otherwise.
Sample
class A { }
class A2 extends A { }
class B <T> {
void change(T a) { .. };
T read() { .. };
}
If there were no wildcards we would not be able to do this: B<A> b = new B<A2>(); - it does not work.
This is because type conversion from B<A> to B<A2> is unsafe.
Why? Let's look (copied from http://en.wikipedia.org/wiki/Generics_in_Java)
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number>
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
What is the solution? Sometimes, we want to do such assignments or pass parameters in a general way!
Wildcard type helps here: B<? extends A> b = new B<A2>();
Method B.void change(T a) is now disabled - this is what your question was about and explained in the first part.
Method B.T read() is still valid and returns A: A a = b.read();. Yes, it returns A2 actually but to the caller of b.read() it's visible as A.
Wildcard types are widely used in Collections Framework.

Categories