Cannot add an object to List<?> instantiated as ArrayList<Object> - java

I ask this question because of a discussion about one answer here on Stack. The statement is the following:
Given the following code:
List<?> list =new ArrayList<Integer>();
Why can't we do:
Integer e = 2;
list.add(e);
This throws a compiler error, despite the fact that we instantiated the list as an ArrayList<Integer>.
Why is that ?

Because a List<?> could be any sort of List (List<String> for example). And the compiler should not permit adding the wrong type to a list.
However, if you know the actual class then you can do a class cast at runtime:
((List<Integer>)list).add(e);
Code like this should be avoided since it can generate a ClassCastException if an unexpected type is encountered at runtime. To make matters worse (as noted by luk2302), our ClassCastException might only occur in an entirely different area of the code-- namely, when we are retrieving something from the list.
A better approach
If you know that the list will be of a specific type or a superclass of that type, then you could define the variable using a bounded wildcard:
List<? super Integer> list;
Integer e = 2;
list = new ArrayList<Integer>();
list.add(e);
list = new ArrayList<Number>();
list.add(e);
list = new ArrayList<Object>();
list.add(e);
This approach, as noted by M. Prokhorov, allows us to avoid the need for an inadvisable cast.

Just create an Arraylist of and they will let you add all, because Integer, String and Boolean are child or in other words Object class is their parent.

Related

Adding Object to Generic List with two types

I read a couple of posts such as here but I was unable to find the solution for my problem.
Why I am unable to add d? It is a subtype of Object...
Type of d: A<B<X>>
List<A<B<? extends Object>>> rv=new LinkedList<>();
rv.add(d); //not working
EDIT
I tried to simplify the problem. When I do:
A<B<?>> abcv=new A<B<String>>();
I get the error: Type mismatch: cannot convert from A<B<String>> to A<B<?>>
However, String is compatible with "?" - so why is it not working? I want to add elements to a list where the last type can by anything, something like this:
List<A<B<?>>> rv=new LinkedList<>();
rv.add(new A<B<X>>());
rv.add(new A<B<String>>());
rv.add(new A<B<Integer>>());
List<SubClaz> is not a subtype of List<SuperClaz> in Java. That's why the wildcards are used: List<SubClaz> is a subtype of List<? extends SuperClaz>.
Now for your A<B<?>> abcv=new A<B<String>>(); example:
By adding the wildcard, you're making B<String> a subtype of B<?>, but since these are also wrapped by another type A, we're back to the first problem:
A<B<String>> is not a subtype of A<B<?>>
(Notice B<?> is the SuperClaz and B<String> is the SubClaz in this case).
You can fix this the same way; by adding another wildcard:
A<B<String>>() is a subtype of A<? extends B<?>>.
Keep in mind that this doesn't allow you to read or manipulate the list as you want. Search for covariance and contravariance for more detail. Here is a good one: http://bayou.io/draft/Capturing_Wildcards.html
d should have type A<B<? extends Object>> or compatible.
List<A<B<?>>> rv = new LinkedList<>();
? in generic means any type.
For example:
You can assign any type of generic list to List<?> but you can not add any type of object into list because compiler does not know what type it is because of wildcard(?)
List<?> genericList1 = new ArrayList<String>();
List<?> genericList2 = new ArrayList<Integer>();
List<?> genericList3 = new ArrayList<X>();
/**
* Compiler will allow to assign any type of generic list to List<?>
* but it will not allow to add.
*/
genericList1.add("xyz"); // It will give compiler error
The <?> wildcard allows a list of ANY type to be assigned, but the add() method is not valid on that list because you could put the wrong kind of thing into the collection. Compiler does not know which type you would pass because of wildcard(?).
If you want to add any type of object to your list than you can take list like this.
List<Object> rv = new LinkedList<>();
rv.add(new A<B<X>>());
rv.add(new A<B<String>>());
rv.add(new A<B<Integer>>());
Now compiler knows that rv is the list which accept only Object type, so compiler will not complain anything.

Understanding use of wild card in java generics?

I have asked a similar question and this is a more specific version in the hope of getting a clear answer.
While trying to understand java generic types and usage of wild card "?", I tried the following:
List<Integer> li4 = new ArrayList<Integer>();
li4.add(new Integer(5));
Integer myInt4 = li4.get(0);
Now I replace with the more generic type '
List<? extends Integer> li = new ArrayList<Integer>();
Integer myInt = li.get(0);
The above compiles fine.
It seems that 'li' above is being treated as a list where each element is an Integer (look at the li.get(0) call).
I can also do the following:
li = li4;
The above compiles and runs fine.
But when I try:
li.add(new Integer(5));
I get following compilation error (using Oracle JDeveloper as IDE):
Error(24,9): cannot find method add(java.lang.Integer)
'? extends Integer' should allow any types that extend Integer. It behaves like that for the 'get' method where it returns an Integer. Similarly it does not complain when ArrayList is assigned to it. So for example 'li = new ArrayList<String>()' does not compile. So why am I allowed to assign another ArrayList<Integer>(), get an Integer back but not add an Integer?
? extends Integer does not mean "should allow any types that extend Integer". It means that this is a List<T> for some specific T that extends Integer.
So anything you get out of a List<? extends Integer> will extend Integer, but you can't just put anything in -- we don't know that the type you're putting in matches T.
To give a concrete example, Integer extends Number. So you could write
List<? extends Number> list1 = new ArrayList<Integer>();
List<? extends Number> list2 = new ArrayList<Double>();
But you shouldn't be allowed to write list2.add(new MyNumber()), or list2.add(new Integer(3)), because list2 is actually a List<Double>.
Following code helped me understand the answer:
List<Exception> exL = new ArrayList<Exception>();
exL.add(new Exception());
Exception ex = exL.get(0);
Throwable th = exL.get(0);
exL.add(new NullPointerException());
/** Error(153,7): cannot find method add(java.lang.Exception)
exL.add(new Throwable());
**/
So when we declare List, the elements must at least have behavior of Exception which means Exception, RuntimeException and its sub-classes. It is ok to assign an element returned from such a list to either Exception or any of its subclasses. "Exception e = exL.get(0)" Or "Throwable th = exL.get(0)". It is also ok to add an instance of Exception or say NullPointerException to List.
List genExcepList, can be a List, List or List as "? extends Exception" represents Exception and all its sub-classes. In fact, List is a super class of List, List etc. For any of these list types, the returned element from 'get' call is guaranteed to be of type Exception. So it is ok to do: Exception e = genExcepList.get(0). But it is not ok to call genExcepList.add(exception) as List must satisfy the restrictions on every type it allows which are List, List, List etc. The only thing that works is genExcepList.add(null) as 'null' would work for every type.
On the other hand if we have something like:
List rtgs = new ArrayList();
"? super Exception" represents Exception and any its super-classes. So List represents List, List and List. On any of these list types, one can call "add(new Exception());" so it is ok to call rtgs.add(new Exception()) as an Exception is also a Throwable and Object. However, it is not ok to call "Exception ex = rtgs.get(0)" as the element returned from List can be any of Exception, Throwable and Object. So the only thing that would safely work is "Object ex = rtgs.get(0)".

What Benefit Generics provide over legacy Code:

i have read on docs.oracle site that The following code snippet without generics requires casting:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
but if i write code with Generics then it is still prone to Error:
List<Object>= new List<Object>;
list.add("hello");
String s=(String)list.get(0);
what is then the real use of generics....:( thnx in advance..
List<Object>= new List<Object>;
list.add("hello");
String s=(String)list.get(0);
Should be
List<String>= new ArrayList<String>(); // this is now a list of String, not a list of object
^^^^^^ ^^^^^^
list.add("hello");
String s=list.get(0); // no casting needed
^
You parameterize by the type you want. Your example are 2 ways to do the same thing, since you parameterize by the most basic class.
The advantage of generics is that you can write classes that are more specific to one class, String here. This gives you better type safety to catch bugs early during compilation. This prevents issues arising from the casting approach.
Using generics makes your code Type Safe. You can prevent ClassCastException.
Suppose you want to store a list of names(string)
List listNames = new ArrayList();
listNames.add("Durgesh");//ok
But I could also add an integer to it
listNames.add(5000);//storing int instead of string
Now do this
String name2=listNames.get(1);//throws exception{int->string}
Without generics you could add invalid types to collection which could break your code.
With generics you could solve the problem
List<String> listNames = new ArrayList();
listNames.add("Durgesh");
listNames.add(3000);//would through error at compile time
So,generics provides typesafety
With List<Object> you intend to add any kind of Object.Due to Object parameter,it would allow you to add any kind of object(string,int).
Also List<x> cannot be assinged(=) to List<y> or vice versa if x can be converted to y or y can be converted to x..They both should be x or y thus providing type safety
So,you wont be able to assign(=) List<String> to List<Object> or vice versa..
Generics are used to detect runtime exceptions at compile-time itself.
Assume that you created a List to store Strings and passed it to a method.. enhanceList(List).. and after the execution, you will iterate through the list and get all strings
before genercis, it could have been possible that enhanceList(List) method will add other type of objects into the list creating possible ClassCastException
void someMethod() {
List listOfStrings = new List();
enhanceList(listOfStrings);
for(Iterator i : listOfStrings.iterator(); i.hasNext();) {
String s = (String) i.next(); //RuntimeException here
}
}
void enhanceList(List l) {
l.add(new Integer(1)); //error code
}
with generics, you can very well "bind" the type of objects the list contains
void someMethod() {
List<String> listOfStrings = new List<String>();
enhanceList(listOfStrings);
for(String s : listOfStrings) {
//no error here
}
}
void enhanceList(List<String> l) {
l.add(new Integer(1)); //compile-time error
}
However, generics should be used with caution, List<Object> doesn't help much with binding types because, it can hold any objects (since Object is super class of all the java classes). I recommend to create List of Specific type always.

Collection in Java

Quick Question...
Can collections in Java hold more than one type? Or do they all have to be the same type?
thanks
Simple answer
Yes.
More detailed answer
You can either use generic collection, without <T> value, for example:
ArrayList a = new ArrayList();
a.add(2);
a.add("String");
Using collections without <T> is a bad habit and most IDEs / compilers give a warning here. You can circumvent it by using a collection of Object, i.e.:
ArrayList<Object> a = new ArrayList<Object>();
Or you can find some common interface or supertype that these element must have in, for example ArrayList<Number> - and you can store various objects that have common Number superclass, i.e. BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short:
ArrayList<Number> a = new ArrayList<Number>();
a.add(2); // integer
a.add(42L); // long
a.add(123.45d); // double
System.out.println(a.toString()); // => [2, 42, 123.45]
Note that it essentially means that a elements are of Number class — i.e. you can't ask to execute subclass-specific methods (for example, Double#isInfinite(), which doesn't exist in Number superclass), although you can typecast in run-time if you somehow know it's safe to typecast:
a.get(2).isInfinite() // compile-time error
((Double) a.get(2)).isInfinite() // => false
((Double) a.get(1)).isInfinite() // run-time error (ClassCastException)
Run-time typecasting is also generally frowned upon, as it effectively circumvents proper compile-time type safety.
Also note that it's impossible to assign (or use) ArrayList<Number> in place of ArrayList<Integer> and vice-versa, i.e. this will fail to compile:
public void printNumbers(ArrayList<Number> list) {
list.forEach(System.out::println);
}
ArrayList<Integer> a = new ArrayList<Integer>();
printNumbers(a); // "incompatible types"
as well as this:
public void printIntegers(ArrayList<Integer> list) {
list.forEach(System.out::println);
}
ArrayList<Number> a = new ArrayList<Number>();
printIntegers(a); // "incompatible types"
To declare a variable to be able to accept both ArrayList<Number> or any of its subclasses, one can use ArrayList<? extends Number> or ArrayList<? super Number> syntax. extends is generally used when you're going to consume (i.e. read) from the object in your method, super is used when you're going to produce (i.e. write). Given that printout is consuming, it's safe to use extends:
public void printNumbers(ArrayList<? extends Number> list) {
list.forEach(System.out::println);
}
ArrayList<Integer> listInt = new ArrayList<Integer>();
printNumbers(listInt); // works
ArrayList<Double> listDbl = new ArrayList<Double>();
printNumbers(listDbl); // also works
There is a good answer in
Difference between <? super T> and <? extends T> in Java for more in-depth explanation.
If you want them to hold any more than one type, use Collection<Object>. However, you won't know what you're getting without doing some if (x instanceof MyType) calls, which are rather inefficient.
They have to be of the same Supertype. So if you have objects of type A, then a Collection<A> can store objects of type A and of every subtype of A.
If you want to allow arbitrary types, then use Collection<Object>, otherwise take the most general appropriate super-class.
However, you will then have to manually cast from the most general type (Object) to the specific type you have in mind. You can use the typeof operator to find out what the type is.
Every Collection classes can contains heterogeneous objects except TreeSet and TreeMap. Since TreeSet and TreeMap stores elements according to some sorting order. so, if objects are of different type it will not be able to sort it because comparison between the objects will not be possible for sorting.
Yes they can but they should not (that's why generics have been put in place since 5th version of jdk) in general store different types, as this is the straight way to errors.
Yes collections in java can hold more than one type as below. But it will throw an exception if done using the following way.
ArrayList al = new ArrayList();
al.add(1);
al.add("name");
al.add(1.2f);
Iterator itr =al.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
Hence it's better to mention the type that you're using. To get rid of the exception the above program can be modified as below.
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
al.add(3);
Iterator itr =al.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
ArrayList<String> al1 = new ArrayList<String>();
al1.add("Words");
al1.add("Names");
al1.add("Characters");
Iterator itr1 =al1.iterator();
while(itr1.hasNext())
{
System.out.println(itr1.next());
}
You can also use more than these types.
Yes,
My mistake the correct code is this one and
ArrayList<Elements>()=new ArrayList();
or
ArrayList<E>()=new ArrayList();
should be the correct declaration if you want to use Generics in Collection.
class Test
{
public static void main(String[] args)
{
// For Generic class of List
ArrayList<E> arrL1 = new ArrayList<E>();
arrL1.add("stackoverflow");
arrL1.add(1);
Iterator itr1=list.iterator();
while(itr1.hasNext())
{
System.out.println(itr1.next());
}
// for Particular datatype in List
ArrayList<String> list=new ArrayList<String>(); // Creating arraylist
list.add("Ravi"); // Adding object in arraylist
list.add("Vijay");
list.add("Ravi");
list.add("Ajay");
// transversing the values
Iterator itr=list.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
}
}
Output 1
stackoverflow
1
Output 2
Ravi
Vijay
Ravi
Ajay
I believe you can also use Collection<?>.
Yes, you can have more than one datatype in ArrayList of Collection.
class Test
{
public static void main(String[] args)
{
// For Generic class of List
ArrayList<> arrL1 = new ArrayList<>();
arrL1.add("stackoverflow");
arrL1.add(1);
// for Particular datatype in List
ArrayList<String> list=new ArrayList<String>(); // Creating arraylist
list.add("Ravi"); // Adding object in arraylist
list.add("Vijay");
list.add("Ravi");
list.add("Ajay");
// transversing the values
Iterator itr=list.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
}
}
Output 1:
stackoverflow
1
Output 2:
Ravi
Vijay
Ravi
Ajay

What are the risks of explicitly casting from a list of type List<? extends MyObject> to a list of type List<MyObject> in Java?

I think the title should explain it all but just in case...
I want to know what risks and potential issues relating to casting can arise from the following snippet of Java code:
List<? extends MyObject> wildcardList = someAPI.getList();
List<MyObject> typedList = (List<MyObject>) wildcardList;
My thoughts are that all objects in the wildcardList should be an instance of MyObject (exact type or subclass) and so whenever objects are retrieved from typedList then there should never be a ClassCastException. Is this correct? If so why does the compiler generate a warning?
There should be no problem as long as just you retrieve objects from the list. But it could result in runtime exception if you invoke some other methods on it like the following code demonstrate:
List<Integer> intList = new ArrayList<Integer>();
intList.add(2);
List<? extends Number> numList = intList;
List<Number> strictNumList = (List<Number>) numList;
strictNumList.add(3.5f);
int num = intList.get(1); //java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.Integer
You are correct about retrieving objects from typedList, this should work.
The problem is when you later add more objects to typedList. If, for instance, MyObject has two subclasses A and B, and wildcardList was of type A, then you can't add a B to it. But since typedList is of the parent type MyObject, this error will only be caught at runtime.
Consider you do something like:
List<ChildClassOne> childClassList = new ArrayList<ChildClassOne>();
childClassList.add(childClassOneInstanceOne);
childClassList.add(childClassOneInstanceTwo);
List<? extends MyObject> wildcardList = childClasslist; // works fine - imagine that you get this from a method that only returns List<ChildClassOne>
List<MyObject> typedList = (List<MyObject>) wildcardList; // warning
typedList.add(childClassTwoInstanceOne); // oops my childClassList now contains a childClassTwo instance
ChildClassOne a = childClassList.get(2); // ClassCastException - can't cast ChildClassTwo to ChildClassOne
This is the only major problem. But if you only read from your list it should be ok.
This is similar to
List<Object> obj = (List<Object>) new ArrayList<String>();
I hope the problem is evident. List of subtypes can't be cast to Lists of supertypes.
In addition to the answers already posted, take a look at the following
what-is-the-meaning-of-the-type-safety-warning-in-certain-java-generics-casts
confused-by-java-generics-requiring-a-cast
The compiler generates a warning incase an element from the type is accessed later in your code and that is not the generic type defined previously. Also the type information at runtime is not available.

Categories

Resources