Hi i am using addAll method of Collection framework. Please find below my code. It is working fine for code 1. For code 2 it is giving me compilation error. I dont know why it didnt give me error for code 1. Kindly give the reason for this.
code 1
public static void main(String[] args) {
List<Integer> firstList=new ArrayList<Integer>();
List secondList=new ArrayList(); //without generic
secondList.add("string value");
firstList.addAll(secondList);
System.out.println(firstList);
}
Output:
[string value]
Code 2
public static void main(String[] args) {
List<Integer> firstList=new ArrayList<Integer>();
List<String> secondList=new ArrayList<String>(); //with generic
secondList.add("string value");
firstList.addAll(secondList);
System.out.println(firstList);
}
Output
compilation error
Java Generics are checked on compile time. means compiler can check the generic list and can show an error if String List is to Integer. While in the first case . it is a non-generic, which compiler cannot judge at compile time.
Also read about Type Erasure
firstList.addAll(secondList);
firstList is type of string
secondList is type of numbers
In the first example you are using raw type but in the second you are using generics(specified list is for strings)
SEE HERE
If you use generics checking donet at compile time .If you use raw list it will done at runtime
List secondList=new ArrayList(); //without generic
It means List<Object> secondList=new ArrayList<Object>(); so you can add any object to this.
But if you explicitly mention the type it is clear that you can't add string to integer list, in your second case
You are trying to add all values from String bucket to a bucket which is specially allocated for Integer.
You can do like this
ArrayList commonList =new ArrayList(); // for all objects
List<String> stringList =new ArrayList<String>();
List<Integer> integerList =new ArrayList<Integer>();
stringList.add("string value");
integerList.add(1);
commonList .addAll(stringList);
commonList .addAll(integerList);
System.out.println(commonList );
Related
I have been looking at OJCPA code snippets and I am confused why the compiler does not throw an error at the following code.
List l = new ArrayList();
l.add("a");
l.add("b");
l.add(1);
List<String> strList = new ArrayList<>();
strList = l; //#1 - Why does the assignment compile?
for(String s: strList) {
System.out.println(s); //#2 - It makes sense that it then throws a ClassCastException
}
I thought that the compiler would see List l as a raw type and because generics are invariant it would produce a compiler error, as it is not of type List< String >.
Thanks for you help.
It is allowed for backwards compatibility.
Suppose that you are calling a legacy method that returns a List of Strings, but it was written before generics were added to Java, so it returns a raw List.
You'd want this line to pass compilation :
List<String> strList = someOldMethodThatReturnsRawList();
Otherwise you'll have to keep using the raw List type in your new code in order to call that method.
For following codes:
ArrayList<String> ar = new ArrayList<String>();
ar.add(45);
And
ArrayList<String> ar = new ArrayList();
ar.add(45);
I am getting compile time error at line ar.add(45) as:
cannot find symbol
symbol : method add(int)
location: class java.util.ArrayList<java.lang.String>
al.add(45);
^
Both piece of code is failing for invalid input . Then why compiler is raising warning of unchecked or unsafe operation for second piece of code?
Then why compiler is raising warning of unchecked or unsafe operation for second piece of code?
Because you're assigning an ArrayList to a variable with type ArrayList<String>. That means that while the compiler will enforce the expectation that the array list will only contain strings when you reference that list through ar, it can't be sure that you don't have other references to the non-parameterized ArrayList that you'll use to add non-strings to it, like this:
ArrayList anythingGoes = new ArrayList();
ArrayList<String> onlyStrings = anythingGoes; // Unchecked/unsafe op
anythingGoes.add(new Date());
for (String s : onlyStrings) { // Blows up
// ...
}
Because in the second code you do not specify the type parameter of the ArrayList. You could write it in Java 7 as:
ArrayList<String> ar = new ArrayList<>();
There are two distinct issues here.
Firstly -
ArrayList<String> ar = new ArrayList();
You're telling the compiler that ar is a list of strings, but you're assigning it to a list of raw types (i.e. unbounded). Hence the compiler will warn you of an unchecked or unsafe operation. You should use something like either option below:
ArrayList<String> ar = new ArrayList<String>();
or
ArrayList<String> ar = new ArrayList<>();
(the second option is a Java 7 example and simply reduces the amount of typing you have to do. The result is the same).
Secondly -
ar.add(45);
You're adding an integer (45) into a list of strings. The compiler won't allow you to do this.
Change your generic data type as Integer
if you want to add integers
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.
Why can't I create an array of List ?
List<String>[] nav = new List<String>[] { new ArrayList<String>() };
Eclipse says "Cannot create a generic array of List"
or
ArrayList<String>[] nav = new ArrayList<String>[] { new ArrayList<String>() };
Eclipse says "Cannot create a generic array of ArrayList"
or
List<String>[] getListsOfStrings() {
List<String> groupA = new ArrayList<String>();
List<String> groupB = new ArrayList<String>();
return new List<String>[] { groupA, groupB };
}
But I can do this:
List[] getLists() {
return new List[] { new ArrayList(), new ArrayList() };
}
Eclipse says that List and ArrayList are raw types but it compiles...
Seems pretty simple, why won't it work?
Well, generics tutorial give the answer to your question.
The component type of an array object
may not be a type variable or a
parameterized type, unless it is an
(unbounded) wildcard type.You can
declare array types whose element type
is a type variable or a parameterized
type, but not array objects.
This is
annoying, to be sure. This restriction
is necessary to avoid situations like:
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;
// Run-time error: ClassCastException.
String s = lsa[1].get(0);
If arrays of parameterized type were
allowed, the previous example would
compile without any unchecked
warnings, and yet fail at run-time.
We've had type-safety as a primary
design goal of generics.
You can't create arrays of generic types, generally.
The reason is that the JVM has no way to check that only the right objects are put into it (with ArrayStoreExceptions), since the difference between List<String> and List<Integer> are nonexistent at runtime.
Of course, you can trick the compiler by using the raw type List or the unbound wildcard type List<?>, and then cast it (with a unchecked cast) to List<String>. But then it is your responsibility to put only List<String> in it and no other lists.
No exact answer, but a tip:
Last example has a raw type warning because you omitted the typization of the list; it is generally a better (type safe) approach to specify which object types are contained in the list, which you already did in the previous examples (List<String> instead of List).
Using arrays is not best practice, since their use contains errors most times; Using Collection classes (List, Set, Map,...) enables use of typization and of convenient methods for handling their content; just take a look at the static methods of the Collections class.
Thus, just use the example of the previous answer.
Another solution is to extend LinkedList<String> (or ArrayList<String>, etc.), then create an array of the subclass.
private static class StringList extends LinkedList<String> {}
public static void main(String[] args)
{
StringList[] strings = new StringList[2];
strings[0] = new StringList();
strings[1] = new StringList();
strings[0].add("Test 1");
strings[0].add("Test 2");
strings[1].add("Test 3");
strings[1].add("Test 4");
System.out.println(strings[0]);
System.out.println(strings[1]);
}
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