Here is the code I have written?
Set keys = map.keySet();
SortedSet s = new TreeSet(keys);
The warning I'm getting is:
warning: [unchecked] unchecked call to TreeSet(java.util.Collection<? extends E>) as a
member of the raw type java.util.TreeSet
How do I get rid of the compiler warning?
Ideally, start using generics fully. You haven't shown what the type of map is, but ideally you should be able to write something like:
Set<String> keys = map.keySet();
SortedSet<String> s = new TreeSet<String>(keys);
That would be in the case where map was something like a Map<String, Integer>.
If map itself is a raw type, it's harder - again, the best fix would be to start adding generics throughout your code base, getting rid of raw types. That's not always possible if the map is returned from third party code, of course. In that case, you may need to suppress warnings on one line as you convert from raw types to generic types - possibly via Collections.checkedCollection - but after that, you should be able to work with the generic type "properly". For example:
#SuppressWarnings("unchecked") // Just for this one statement
Collection<String> keys = Collections.checkedCollection(map.keySet(),
String.class);
// Now this statement is fully generic with no warnings
SortedSet<String> s = new TreeSet<String>(keys);
As far as this problem is concerned, you should use parameterized type of keys e.g
Set<TypeOfKeyObject> keys = map.keySet();
SortedSet<TypeOfKeyObject> s = new TreeSet<TypeOfKeyObject>(keys);
where TypeOfKeyObject is object type of Key in your map object.
you may force supress the warnings (as already correctly suggested) but not advisable.
At the risk of sounding condescending, I would suggest you to study generics. A good starting point would be this: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Related
I came across the following code, a simple example of adding elements to List
List list = new ArrayList<Integer>();
ListIterator<Integer> litr = null;
list.add("A");
list.add("1");
list.add(5);
litr = list.listIterator();
while(litr.hasNext()){
System.out.println("UIterating " + litr.next());
}
I expected it to throw an ClassCastException, but rather it wrote this to the console
A
1
5
which looks weird. When i tried:
List<Integer> list = new ArrayList<Integer>();
I got a compile time error.
I would be grateful if someone could explain how the String objects are added to the ArrayList
You assigned the new ArrayList to an untyped List. Generic type restrictions don't apply to an untyped List, it will let you put whatever you want in it. The compiler does not keep track that your untyped List refers to something that was declared with a generic type.
In any case this wouldn't produce a ClassCastException, generics only affect compilation. At runtime
The case where you put the type on the list variable:
List<Integer> list = new ArrayList<Integer>();
is preferred, it should generate a compiler error telling you you're putting the wrong type in the collection.
There's a description of how legacy, non-generic code and generic code interoperate in this article:
In proper generic code, Collection would always be accompanied by a type parameter. When a generic type like Collection is used without a type parameter, it's called a raw type.
Most people's first instinct is that Collection really means Collection<Object>. However, as we saw earlier, it isn't safe to pass a Collection<Part> in a place where a Collection<Object> is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just like Collection<?>.
But wait, that can't be right either! Consider the call to getParts(), which returns a Collection. This is then assigned to k, which is a Collection<Part>. If the result of the call is a Collection<?>, the assignment would be an error.
In reality, the assignment is legal, but it generates an unchecked warning. The warning is needed, because the fact is that the compiler can't guarantee its correctness. We have no way of checking the legacy code in getAssembly() to ensure that indeed the collection being returned is a collection of Parts. The type used in the code is Collection, and one could legally insert all kinds of objects into such a collection.
So, shouldn't this be an error? Theoretically speaking, yes; but practically speaking, if generic code is going to call legacy code, this has to be allowed. It's up to you, the programmer, to satisfy yourself that in this case, the assignment is safe because the contract of getAssembly() says it returns a collection of Parts, even though the type signature doesn't show this.
This is possible because of how generics are implemented in Java - using type erasure, and because Java supports raw types for backward compatibility with old versions of Java (1.4 and older).
Generics only exist in your source code. The compiler uses them to check the types at compile-time, but then throws away the generics. At runtime, a List<Integer> is just a List of objects, and it doesn't know that it's a list that should contain only Integer objects.
Java supports the use of raw types such as List instead of List<Integer> for backward compatibility with old versions. When you use a raw type, as you are doing in your code above, you get a compiler warning. You should not use raw types in new code - only ever use them when you need to deal with old code that you can't change.
The combination of raw types and type erasure allows you to put types of objects in lists that you shouldn't be putting in there.
Because the List at runtime doesn't know anything about the type that its elements are supposed to have, it doesn't check anything so you will not get a ClassCastException.
recently I read a piece of code which seems weird to me. As we know, we need to initialize the generic type in collections when we need to use them. Also, we know Collections can contain Collections as their elements.
The code:
public class Solution {
public static void main(String args[]) {
ArrayList res = returnlist();
System.out.print(res.get(0));
}
public static ArrayList<ArrayList<Integer>> returnlist() {
ArrayList result = new ArrayList();
ArrayList<Integer> content = new ArrayList<Integer>();
content.add(1);
result.add(content);
return result;
}}
My question is
why can we use ArrayList result = new ArrayList(); to create an object, since we have not gave the collection the actual type of element.
why can we use result.add(content); to add a collection to a collection with collection "result" is just a plain collection. We have not defined it as a ArrayList of ArrayList
Java generic collections are not stored with a type to ensure backwards compatibility with pre J2SE 5.0. Type information is removed when added to a generic collection. This is called Type Erasure.
This means that a generic collection can be assigned to a non generic reference and objects in a generic typed collection can be placed in an non generic, nontyped collection.
All Java generics really does is make sure you can't add the wrong type to a generic list and saves you from doing an explicit cast on retrieval; even though it is still done implicitly.
Further to this
the Java section of this answer goes a little deeper into what I just said
this article also covers basically what you were asking in a more complete way
other things to watch out for with Type Erasure
Just adding up to provide summarized answer
Old way :
(A) ArrayList result = new ArrayList();
will create an Arraylist to hold "Object"
New Way :
ArrayList<Integer> content = new ArrayList<Integer>();
this represents an Arraylist which will hold "Integer" objects. This was introduced for compile-time type check purposes.
why ?
Consider the first case. Its input type is Object. We know that Object is the super class of all classes. We can pass in an Integer object, String object and so on. When fetching the data the developer has to perform proper type casting. Say if the developer initially thinks the code will accept Integer objects so he adds the following typecast while fetching the data
Integer integer=(Integer) content.get(0);
This is supposed to work. But if mistakenly he passes a String it will result in run-time error.
How it can be avoided ?
By introducing compile time checks
How it works ?
when we specify parameterized type only Integer objects can be added to the ArrayList collection. Else it will show error.
content.add(3); // will work
content.add("HARSHAD"); // error shown
If parameterized generic types are for type checking purposes how correct data can be retrieved from the list ?
The compiler implicitly performs type conversion. See the sample code
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
Integer integer=list.get(0);
System.out.println(integer);
What the compiler actually does when you perform compilation ?
//perform type erasure
(B) List list=new ArrayList();
list.add(1);
list.add(2);
// the arraylist inturn accepts Object when you run the code
//add casting
Integer integer=(Integer)list.get(0);
Conclusion
If you see the codes (A) and (B) both are the same. Only difference is that in the second case the compiler implicitly does the same operation.
Finally to answer your question ...
ArrayList result = new ArrayList();
is allowed for backward compatibility purposes. Although this is not recommended.
Official link from Oracle docs explaining the same concept.
Generics were added to Java only in Java 5. Before that, when you use a collection, it always meant collection of objects. The old syntax is left as is for backward compatibility. So ArrayList result = new ArrayList() is actually creating an ArrayList<Object>. Since ArrayList is also an object, you can add content to the variable result.
why can we use ArrayList result = new ArrayList(); to create an object, since we have not give the collection the actual type of element.
Because java wants to it backward compatible. Generics is more of compiler feature for ensure type safety, collections can store any type of object at runtime.
Java compiler will not give you compiler error for this but it must have given you compiler warning that it is unsafe to use generic classes without type.
It may be a remnant from before generics came along to java (Java 4 or 5 I think).
I am converting old java code to support generics and came across this line of code which was trying to cast an object retrieved from the session into a TreeMap:
TreeMap allTransactions = (TreeMap) pageContext.getSession()
.getAttribute("allTransactions");
When I tried to convert it to specific type:
TreeMap<String, MyDataBean> allTransactions = (TreeMap<String, MyDataBean>)
pageContext.getSession().getAttribute("allTransactions");
It gave me a warning:
Type safety: Unchecked cast from Object to TreeMap<String,MyDataBean>
In an effort to get rid of the warning completely, I wrote a method to cast it to Map:
public static <K,V> Map<K,V> castToMap(Class<? extends K> clazz1,
Class<? extends V> clazz2, Map<?,?> c) {
Map<K,V> map = new TreeMap<K,V>();
for (Map.Entry<?,?> entry : c.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
map.put(clazz1.cast(key), clazz2.cast(value));
}
return map;
}
Goodnews: This time I did not get any error when I modified the initial code to call this method:
Map<String, MyDataBean> allTransactions = MyUtilityClass.castToMap(String.class,
MyDataBean.class,
(Map<?,?>)pageContext.getSession().getAttribute("allTransactions"));
But I still had to cast it ^^^here to call my function.
Question 1:
Why it does not show any errors now when I am still using the cast (Map<?,?>) versus what I tried before (TreeMap<String,MyDataBean>)?
Question 2:
My app is broken as of now since other developers have not committed the code yet, and so I am in no position to run the App and verify its correctness. Can anyone tell just by looking at it, if this casting will behave the same way as before?
If it is a Map then it is implicitly a Map<?,?> - as in, a map of anything. It considers that to be a safe cast because, since it is a map, it cannot fail. At this point, it's no different than casting it to any other object, which they're assuming is safe.
If it is a bad cast (say it's a List, not a Map) then it will explode there before anything else goes wrong. But because those types are lost at compile time, it will happily continue if the problem is in the generic types. So Map<?,?> is considered safe because it will explode because of being not being a Map if that's the problem.
As far as your second issue, it looks fine, but without more context it's hard to say. That being said, you should be able to use the history in your source control to grab an earlier version to compare against. If you can't obtain earlier versions of your code, your source control isn't very useful, now is it?
Ad 1.: The cast itself is safe, because you don't specify any types which the compiler can not match against the method signature. Java 4 List can be safely assigned to Java 5 List<?>, but when you specify a type, you assume something, the compile can not safely check (therefore the warning).
Ad 2.: You have substituted a "hard" language cast with a "soft" method call cast, which will fail equally, if the classes are not castable into one another - I prefer the less verbose version of language casting, because one should rely one one's API.
List<Object> myList = new ArrayList<String>(); //(hint: no)
Map<Integer> myMap = new HashMap<int>(); // (hint: also no)
Why are the statements wrong in the above declarations?
Let's look at the first example. Think of the operations you should be able to do on a List<Object>: Add, Remove, Retrieve any Object.
You're trying to fill those requirements with a collection that you can Add, Remove, and Retrieve only strings.
List<Object> myList = new ArrayList<String>();
// This should be valid based on the List<Object> interface but obviously
// MyClass isn't String...so what would the statement do?
myList.add(new MyClass());
The second is simply because Generics in Java don't support primitive types. For more information, you could check out:
java - Why don't Generics support primitive types?
For the first one, because Java generics are not covariant. So you can't even do:
ArrayList<Object> myList = new ArrayList<String>();
For more info, see this article: Java theory and practice: Generics gotchas.
For the second one, you cannot use primitives as the generic type.
1) What would happen if anyone added a Long to the list that is referenced by myList?
2) You can't have a primitive there.
The first is impossible. You can use a wildcard:
List<?> myList = new ArrayList<String>();
But now, your list is completely unusable. Adding, removing, etc doesn't compile anymore:
myList.add(new Object()); // Error
myList.add("error"); // Error
You can't use primitives (int, double, ....) with generics. You have to use the wrapper class:
HashMap<Integer> myHap = new HashMap<Integer>();
for the second example Map has 2 generic parameters a Key and Value but the following still wont work
Map<Integer,Object> myMap = new HashMap<int,Object>();
that because as said before primitive types don't work with java generics (besides generic types always derive from Object and type erasure translates all generic variables to type Object)
For all things Java-Generics, refer to Angelika Langer. In this case, Is List<Object> a supertype of List<String>?
Even though Java does support auto-boxing, it doesn't automatically convert <int> to <Integer>. IMO this is partly to remind users that you can get nulls.
How remove the:
Type safety: The expression of type
List[] needs unchecked conversion to conform to List<Object>[]
compiler warning in the following expression:
List<Object>[] arrayOfList = new List[10];
Afaik the only way is to use #SuppressWarnings("unchecked"). At least if you want to avoid the raw type warning that occurs in Matthew's answer.
But you should rethink your design. An Array of Lists? Is that really necessary? Why not a List of Lists? And if it gets too complicated (a List of List of Map of List to...), use custom data types. This really makes the code much more readable.
And as an unrelated side note: You should write List<Object>[] arrayOfList. The brackets are part of the type, not the variable.
You cannot do any variation of this without a compiler warning. Generics and arrays do not play nice. Though you can suppress it with
#SuppressWarnings("unchecked")
final List<Object> arrayOfList[] = new List[10];
List arrayOfList[] = new List[10];
or
List[] arrayOfList = new List[10];
You can't have generic arrays in Java, so there is no reason to have a reference to one. It would not be type-safe, hence the warning. Of course, you're using <Object>, which means your lists are intended to contain any object. But the inability to have generic arrays still applies.
Also, note that the second version above (with [] attached to the type) is usually considered better style in Java. They are semantically equivalent.
There are a number of ways:
You can add `#SuppressWarnings("unchecked") before the assignment to tell the compiler to ignore the warning
Use raw types List[] arrayOfList = new List[10]; notice that in Java you usually put the [] after the type not the variable when declaring it - though using raw types is discouraged by Sun since they might be removed in a future version.
Don't use arrays, it's usually a bad idea to mix collections with arrays: List<List<Object>> listOfList = new ArrayList<List<Object>>;
Unfortunately, due to the fact that generics are implemented in Java using type erasure, you cannot create an array of a type with type parameters:
// This will give you a compiler error
List<Object>[] arrayOfList = new ArrayList<Object>[10];
See this in Angelika Langer's Java Generics FAQ for a detailed explanation.
You could remove the generics and use raw types, as Matthew Flaschen shows, or use a collection class instead of an array:
List<List<Object>> data = new ArrayList<List<Object>>();