Non-generic object of generic type - java

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

Related

Generics and Collections, don`t understand

Is there any difference between these 3 lines?
List list2 = new ArrayList<String>();
List list2 = new ArrayList<>();
List list2 = new ArrayList();
As I understand, all information about generics is erased in runtime. Consequently, only type of veriable is important. So the above lines of code mean the same thing to me. If I am wrong can anybody give me some exapmle that shows the difference?
p.s. sorry for my english
While it is true that generic data is erased at compile-time, that doesn't mean they are totally useless. The only line that would compile in your example is the last one, but you would get a warning that you are using RawTypes. A RawType is a generic class that does not have a generic object. The first two lines are only half-built.
The reason you pretty much have to use generics is that the list is type-safe. If you use generics, you could use something like this:
List<String> list = new ArrayList<>();
String out = list.get(0);
If you use raw types, you would have to do this:
List list = new ArrayList();
String out = (String) list.get(0);
This may seem OK, but what if you add a value to the list that is not a String? The program crashes. For example:
List unsafe = new ArrayList();
List<String> safe = new ArrayList<>();
unsafe.add("hi");
unsafe.add(new Tree());
safe.add("hi");
safe.add(new Tree()); // This line would throw an exception.
String out = (String) unsafe.get(0);
String out1 = safe.get(0);
String out2 = (String) unsafe.get(1); // This line would throw an exception.
If you still don't quite see why to use generic types, the final nail in the coffin for raw types is this: If my (String) unsafe.get(1) is in a separate class, on the hundredth line, I know that that line is the problematic line. I, however, don't know where the non-string object is being added, only where it's being accessed.
If you use generic types, you know exactly where the problematic addition is made, and you can prevent it.

Why don't you get a compiler error when assign raw types to generic types?

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.

Another way to convert ArrayList containing Strings to an array of Strings in Java?

I have checked the following stackoverflow source to solve my problem:
StackOverflow link
but, when applying the top solution, I still get an error, as follows:
My list is defined here:
List linhaInicial = new ArrayList();
My convertion is made here:
String[] convertido = linhaInicial.toArray(new String[linhaInicial.size()]);
The problem is I still get the following error:
Error:(86, 59) java: incompatible types
required: java.lang.String[]
found: java.lang.Object[]
My conversion is somehow still returning Object[] when it should now be returning String[].
Any solutions?
Thanks!
Do not use raw types!
List linhaInicial = new ArrayList();
should be
List<String> linhaInicial = new ArrayList<>();
Change :
List linhaInicial = new ArrayList(); // can contain any Object.
//So compiler throws an error while converting an Object to String.
to
List<String> linhaInicial = new ArrayList<String>(); // using Generics ensures
//that your List can contain only Strings, so, compiler
//will not throw "incompatible types" error
Always try to include the type of the list element in your declaration:
List<String> linhaInicial = new ArrayList<String>();
Avoid raw types as much as possible.
You have to change
List linhaInicial = new ArrayList(); // you are using raw type List
To
List<String> linhaInicial = new ArrayList();// you have to use String type List

Need clarification on addAll methods behaviour

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 );

How can an Integer be added to a String ArrayList?

List list = new ArrayList<String>() ;
list.add(1) ;
Integer hello = (Integer) list.get(0) ;
System.out.println(hello);
The above code has a reference of type List referring to an instance of ArrayList of type String. When the line list.add(1) is executed, isn't the 1 added to the ArrayList (of type String) ? If yes, then why is this allowed?
You have used type erasure, which means you have ignored previously set generic checks. You can get away with this as this as generics are a compile time feature which isn't checked at runtime.
What you have the same as
List list = new ArrayList() ;
list.add(1) ;
Integer hello = (Integer) list.get(0) ;
System.out.println(hello);
or
List<Integer> list = new ArrayList<Integer>() ;
list.add(1) ;
Integer hello = list.get(0); // generics add an implicit cast here
System.out.println(hello);
If you look at the byte code generated by the compiler, there is no way to tell the difference.
Interestingly, you can do this
List<String> strings = new ArrayList<String>();
#SuppressWarnings("unchecked");
List<Integer> ints = (List) strings;
ints.add(1);
System.out.println(strings); // ok
String s= strings.get(0); // throws a ClassCastException
The problem is that your list variable has a raw type and you can add objects of any type to this list. To solve the problem just declare it as a List of String's:
List<String> list = new ArrayList<String>() ;
It compiles because the declaration List list uses the raw type List, not the bound type List<String>. The second line compiles because your list variable can accept anything. The third line compiles because list.get(0) returns Object. The fourth line has no reason to fail.
By accident, nothing in the compiled code caused the Integer 1 to be cast to a String. But you must not depend on that. Had the first line been the proper
List<String> list = new ArrayList<>();
your mistake would have been caught at compile time.
When you declare a list like this:
List list = new ArrayList<String>() ;
You are using what's called a Raw Type. It's a type that has a generic type parameter, like List, but you failed to supply one. If you check the compiler warnings you will see it is telling you about this. Mixing parameterized types with raw types is generally considered a programming error. This is ok:
List<String> list = new ArrayList<String>() ;
and this is ok (but obsolete):
List list = new ArrayList();
But the way you wrote it should be avoided.
The problem is generics are checked only at compiler time, and you told the compiler List, not List<String>, so it will let you put anything you want in there! The fact that the right side has the parameter doesn't really mean anything, it's the type of the actual reference that matters to the compiler.
List list = new ArrayList<String>() ; // WARNING!!!
An unsafe List refers to a safe ArrayList
list.add(1) ; // WARNING!!!
An Integer (int 1 converted to Integer is Autoboxing) not a String added to the List
Integer hello = (Integer) list.get(0) ;
Casting is required because unsafe List can contain anything.
System.out.println(hello);
Integer.toString() called
When the line list.add(1) is executed, isn't the 1 added to the ArrayList (of type String) ?
Yes. Integer was added to an ArrayList that is supposed to contain String.
If yes, then why is this allowed?
Backward Compatibility. Java has to support pre-generic codes.
List list; means it is unsafe so you can do anything with it and the referred ArrayList() has to tolerate that.
Moral: Don't ignore the compiler warnings otherwise Integer might enter into an ArrayList that is supposed to contain Strings.
Yes... the 1 is addeded to Array, because Java, like other languages, implements implicit (automatic) conversion (casting) of primitives data types.
More information here Conversions and Promotions
Try this
List.add(Integer.toString(1));
You also can try this
List list = new ArrayList<Object>() ;
If there is no need that the arraylist must be a Arraylist of String
No, because Java sees the 1 as an Integer. You will need to convert your number as a String first with Integer.toString(n)

Categories

Resources