Consider the following API method taken from Shiro's org.apache.shiro.subject.PrincipalCollection interface but probably present in other libraries as well:
Collection fromRealm(String realmName);
Yes even nowadays there are still libraries that are using raw-types, probably to preserve pre Java 1.5 compatibility?!
If I now want to use this method together with streams or optionals like this:
principals.fromRealm(realmName).stream().collect(Collectors.toSet());
I get a warning about unchecked conversion and using raw types and that I should prefer using parameterized types.
Eclipse:
Type safety: The method collect(Collector) belongs to the raw type Stream. References to generic type Stream<T> should be parameterized
javac:
Note: GenericsTest.java uses unchecked or unsafe operations.
As I can't change the API method's signature to get rid of this warning I can either annotate with #SuppressWarnings("unchecked") or simply cast to Collection<?> like this:
((Collection<?>) principals.fromRealm(realmName)).stream().collect(Collectors.toSet());
As this cast of course always works I'm wondering why the compilers are not simply treating Collection as Collection<?> but warn about this situation. Adding the annotation or the cast doesn't improve the code a single bit, but decreases readability or might even shadow actual valid warnings about usage of unparameterized types.
The reason is quite simple:
You may read Objects from a Collection<?> the same way as from Collection. But you can't add Objects to a Collection<?> (The compiler forbids this) whereas to a Collection you can.
If after the release of Java 5 the compiler had translated every Collection to Collection<?>, then previously written code would not compile anymore and thus would destroy the backward compatibility.
The major difference between raw type and unbounded wildcard <?> is that the latter is type safe, that is, on a compile level, it checks whether the items in the collection are of the same type. Compiler won't allow you to add string and integer to the collection of wildcard type, but it will allow you to do this:
List raw = new ArrayList();
raw.add("");
raw.add(1);
Actually, in case of unbounded wildcard collections (List<?> wildcard = new ArrayList<String>()), you can't add anything at all to the list but null (from Oracle docs):
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.
A Collection<?> screams:
Please don't add anything to me. I have a strict content type, ... well uh, I just forgot what type it is.
While a Collection says:
It's all cool ! You can add whatever you like, I have no restrictions.
So, why shouldn't the compiler translate Collection to Collection<?> ?
Because it would put up a lot of restrictions.
A use-case that I can think of as to why Collection is not considered as Collection<?> is let say we have a instance of ArrayList
Now if the instance is of type ArrayList<Integer> or ArrayList<Double> or ArrayList<String>, you can add that type only(type checking). ArrayList<?> is not equivalent to ArrayList<Object>.
But with only ArrayList, you can add object of any type. This may be one of the reason why compiler is not considering ArrayList as ArrayList<?> (type checking).
One more reason could be backward compatibility with Java version that didn't have generics.
Related
This question already has answers here:
Difference between List, List<?>, List<T>, List<E>, and List<Object>
(10 answers)
Closed 9 years ago.
I was reading about unknown types and raw types in generics, and this question came to mind. In other words, is...
Set<?> s = new HashSet<String>();
and
Set s = new HashSet<String>();
... one and the same?
I tried it out, and they both seem to accomplish the same thing, but I would like to know if they are any different to the compiler.
No, they are not the same. Here's the basic difference:
Set<?> s = HashSet<String>();
s.add(2); // This is invalid
Set s = HashSet<String>();
s.add(2); // This is valid.
The point is, the first one is a unbounded parameterized type Set. Compiler will perform the check there, and since you can't add anything but null to such types, compiler will give you an error.
While the second one being a raw type, the compiler won't do any check while adding anything to it. Basically, you lose the type safety here.
And you can see the result of loosing type safety there. Adding 2 to the set will fail at compile time for Set<?>, but for a raw type Set, it will be successfully added, but it might throw exception at runtime, when you get the element from the set, and assign it to say String.
Differences apart, you should avoid using raw types in newer code. You would rarely find any places where you would use it. Few places where you use raw type is to access static fields of that type, or getting Class object for that type - you can do Set.class, but not Set<?>.class.
The first one create a Set<?>, which means: "a generic Set of some unknown class". You won't be able to add anything to this set (except null) because the compiler doesn't know what its generic type is.
The second creates a raw, non generic set, and you can add anything you want to it. It doesn't provide any type-safety.
I don't see why you would use any of them. Set<String> should be the declared type.
The first one uses generics and the second one uses the raw form of Set.
The first one uses a wildcard as the generic type parameter. It means, "a Set of some specific yet unknown type", so you won't be call methods such as add that take a generic parameter, because the compiler doesn't know which specific type it really is. It maintains type safety by disallowing such a call at compile time.
The raw form removes all generics and provides no strong typing. You can add anything to such a Set, even non-Strings, which makes the following code not type-safe:
Set<String> genericSet = new HashSet<String>();
Set rawSet = genericSet;
rawSet.add(1); // That's not a String!
// Runtime error here.
for (String s : genericSet)
{
// Do something here
}
This would result in a runtime ClassCastException when the Integer 1 is retrieved and a String is expected.
Maintaining as much generic type information as possible is the way to go.
Set<String> s = HashSet<String>();
Set<?> tells the compiler that the set contains a specific type, but the type is unknown. The compiler uses this information to provide errors when you attempt to invoke a method with a generic parameter, like add(T).
Set tells the compiler that the set is a "raw" type, where no generic type parameter is given. The compiler will raise warnings, rather than errors, when the object's generic methods are invoked.
In order to add elements to the set without warnings, you need to specify the generic type information on the variable. The compiler can infer the type parameters for the constructor. Like this:
Set<String> s = new HashSet<>();
This information allows the compiler to verify that the Set is used in a type safe way. If your code compiles without type safety warnings, and you don't use any explicit casts, you can be assured that there will be no ClassCastException raised at runtime. If you use generics, but ignore type safety warnings, you might see a ClassCastException thrown at a point where you don't have a cast in your source code.
by using generics, we detect any possible during compilation.
for example,
List<String> list = new ArrayList<String>();
//list.add(new Integer(45)); This will cause compilation error.
list.add("car");
list.add("bus");
list.add("bike");
String vehicle = list.get(0); //compiler-generated cast
when we use raw type instead of generics before Java 1.5, it needs explicit casting.
for example,
List list2 = new ArrayList();
list.add("car");
list.add("bus");
list.add("bike");
String vehicle = (String)list.get(0); //explicit casting is necessary
however with generics, type erasure occurs. that is the type information is lost in runtime.
if, that is so, how does the JVM know what object type it is retrieving during runtime, whether it is a string object or a person object (compiler generated cast above). but this valid with generics, which is can cause runtime errors.
List<Object> test = new ArrayList<Object>();
test.add("hello");
test.add(new Integer(34));
finally, Joshua Bloch mentions on page 115 (item 23, effective java) that
Set<Object> is parameterized type representing a set that can contain objects of any type,
Set<?> is a wild card type representing a set that can contain only objects of some unknown type
and Set is a raw type, which opts out of the generic type system.
I do understand what he means by the above statement. some clarifications will help
The compiler inserts cast operations when retrieving items from generic methods; this is the only way that the JVM knows to treat the result of list.get(0) as a String. This is why heap pollution (inserting the wrong type of object into a generic collection) can result in a ClassCastException at runtime.
Regarding the wildcards:
Set<Object>'s generic type is exactly Object. You can insert and retrieve Object instances from it, but you can't pass a Set<Integer> to a method expecting a Set<Object>, since the method might be planning to add a non-Integer object to the set.
Set<?> has an unspecified generic type. A method can retrieve anything from it as an Object (since everything is an Object) and can call universal methods on it like hashCode or toString, but it can't add anything to the set.
Set, as you mention, is the raw type and shouldn't be used in new code.
I am not very sure, but what I understand by type information is lost at runtime is that there is no way at runtime that a collection is of some specific type. If you add a String to a collection, it will be a String only but the collection does not enforce that all elements should be of type String
Generics are implemented by Java compiler as a front-end conversion
called erasure. Type erasure applies to the use of generics. When
generics are used, they're converted into compile time checks and run
time type casts.
Due to type erasure mechanism this code:
List<String> a = new ArrayList<String>();
a.add("foo");
String x = a.get(0);
gets compiled into this:
List a = new ArrayList();
a.add("foo");
String x = (String) a.get(0);
Notice extra cast inserted into compiled compiled-code after type erasure.
PS: #chrylis has already provided good explanation about your 2nd part of question.
Well, this stackoverflow question here can help you .
Eclipse might be using this method to find out the fields in a class and their generic type if any. Please have a look.
I just stumbled upon the compiler treating these two terms differently. when I type:
LinkedList<String> list = new LinkedList();
I get a compiler warning about a raw type. however:
LinkedList<String> list = new LinkedList<>();
removes the warning. It seems to me as though the two statements mean essentially the same thing (i.e. create a new LinkedList with no specified object type). Why then does the complier all ow the empty generics? What is the difference here?
The statements do not mean the same thing at all.
The first statement tries to fit an untyped LinkedList into a declared generic LinkedList<String> and appropriately throws a warning.
The second statement, valid in Java 1.7 onward, uses type inference to guess the type parameter by using the declaring type's type parameter. In addition, sometimes this can be used in method calls. It doesn't always work, however.
See this page for more info.
It's the diamond operator in Java 7, that helps you save writing the type again. In Java 7 this is equivalent to the same generic type argument that is used on the left side of the declaration. So the initialization is type safe and no warning is issued.
With LinkedList<>, you use the new Diamond Operator, from java 7.
The Diamod operator uses the generic value setted in the left side of the line.
In Java 6, this doesnt works!
The diamond operator, however, allows the right hand side of the
assignment to be defined as a true generic instance with the same type
parameters as the left side... without having to type those parameters
again. It allows you to keep the safety of generics with almost the
same effort as using the raw type.
I think the key thing to understand is that raw types (with no <>)
cannot be treated the same as generic types. When you declare a raw
type, you get none of the benefits and type checking of generics. You
also have to keep in mind that generics are a general purpose part of
the Java language... they don't just apply to the no-arg constructors
of Collections!
Extracted from: https://stackoverflow.com/a/10093701/1281306
Backword compatibility (Inter-operating with legacy code) is the reason why java allows above signature. Generics are compile time syntax only. At runtime "all generic" syntax will be removed. You will just see if you de-compile any class file. Read this documentation.
LinkedList list = new LinkedList();
I'm refatoring a home-grown DAO container, hoping to make the class generic. It internally uses an ArrayList to store the retrieved objects.
One usage of this class puts the container's list into a request scope, and due to a limitation of Websphere, I can't pass the generic List<Foo> to the request scope (Websphere doesn't handle generics out-of-the-box)
If I go ahead with my refactorings, I will need to convert/cast the List<Foo> into a non-generic List object..
// Boils down to this...
List<Foo> listFoo = new FooListing().findAllFoo();
List listThings = listFoo;
request.setAttribute("listThings", listThings);
What are the implications of reversing a generification like this? Should I avoid doing this kind of manipulation?
EDIT: The code snippet is verbose to explicitly demonstrate what I'm describing..
If the component type of the List does match the expected type, there is no problem.
Generics in Java are only used for type-checks by the compiler, they have not effect at runtime. If you are using an older library that does not support generics, you have no choice but to ignore the generic type.
Things should continue to work, as this system has been designed with backwards compatibility in mind.
So all you are losing is the compile-time type checking (it puts you back to where Java was at 1.4, which means, if the types match, everything will work, if not, you'll get ClassCastExceptions or other unwanted behaviour at runtime).
However, I think you can just write
request.setAttribute("listThings", listFoo);
This method takes any kind of Object. Even if it wanted a List, you could still pass a List<Foo> (which is still a List).
Java uses "type erasure" for generics -- essentially that means that the compiler checks the generics, but the runtime forgets all about it and just treats it as a list of objects.*
Whenever you treat a List<Foo> as just a List, you won't get compiler checks to make sure you don't put a Bla into your list. So you could get a ClassCastException if you call List<Foo>.get() and it turns out to be a Bla hiding in the list. But that can only happen if you some code puts a Bla in your list.
If you wan't to be cautious, then if you pass the List<Foo> as a List to anything that might add a non-Foo to the list, don't treat it as a List<Foo> whenever you access it, but treat it as a list of Objects and add instanceof checks.
*Some of the information is accessible at runtime, but let's not complicate matters.
A "non-generic" version of a generic type is called a "raw type".
Passing a generic type where the raw equivalent is requested is generally ok. This is actually the main reason generics in Java work the way they do (with erasure): to enable interoperability between "generified" code and pre-generics code.
The main thing you need to be careful about is that if you pass a List<Foo> to something that askes for a List, they may put non-Foo objects into the List. You won't get any compile time checking to help you here. You do get some runtime checks: a ClassCastException will be thrown when you use a method that returns a Foo on your List<Foo> and it has to return a non-Foo.
If you want more fail-fast behavior you can wrap your List<Foo> with Collections.checkedList() to get a List that'll check the type of elements on insertion.
Things get more complicated if Foo itself is a generic type. Runtime checks are only done on reified types (ie: the type with generic type parameters removed) so if you give them a List<Set<Bar>> and they insert a Set<Baz> or just a Set, you won't know since the runtime/reified type of the element is Set either way.
First, you can't cast a generic to a non-generic list so yeah you'd have to convert it.
Second, the two main advantages to a generic list are 1) it ensures that all objects are of the specified type and 2) it allows you to directly access methods of the object collection without needing to recast them. This allows you to write cleaner code and saves some processing cycles from having to cast back and fourth.
Neither one of these advantages is a dire need however. If you can't use them you won't notice a difference in performance. Your code may look a little messier though.
I have similar problems with Weblogic Portal. Just use none-generic type for this case.
I am quite new to Java ...
I wrote a class called DLPFile
which is basically a container of other objects like Strings, ints, floats, etc.
When putting my files into a List and then saving it in my session (which is from Map class) variable is easy;
DLPFile file = new DLPFile();
List <DLPFile >fileList = new ArrayList <DLPFile>();
fileList.add(file);
session.put("filesList", fileList);
but how do I retrieve the list from the session var?
When I do:
List <DLPFile files = (List) session.get("fileslist");
I got some warnings:
"List is a raw type.References to generic type List<E> should be parameterized."
I tried
List <DLPFile files = (List <DLPFile> ) session.get("fileslist");
List <DLPFile files = (List ) session.get("fileslist")<DLPFile>; and
List <DLPFile files = (List) <DLPFile> session.get("fileslist");
but none works
I suppose this is kind of a "casting" problem... (maybe?)
Thanks in advance ;)
This is because of the Generics Type erasure. The compiler has no way to determine the actual generic type argument when you get it from your session (except if you session.get takes a Class<T> argument to cast it accordingly), because the session probably only returns the type of object. You can make sure that your object is an instance of List, but the generic type information is lost (basically the compiler converts it to a List<Object> internally). That is why you get the warning, because only you as the programmer can know if the generic type parameter you want to cast it to is the right one.
If you don't like the warning you may add an
#SuppressWarnings("unchecked")
Annotation at the beginning of your method.
Are you aware that you are missing a > at the start? i.e. 'List <DLPFile files' should be 'List <DLPFile>' files'.
This option should be fine
List <DLPFile> files = (List <DLPFile>) session.get("fileslist");
Although you'll get an unchecked cast warning since I don't expect your session to be a Map<String, List<DLPFile>>.
Look through the Java Generics FAQ http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
The first option you tried is the one you need:
List<DLPFile> files = (List<DLPFile>) session.get("fileslist");
The first warning you get doesn't have anything to do with generics: You would get the warning when casting to, for example, String as well. The compiler just tells you it can't ensure the returned object is a List<DLPFile>.
The second one, about the raw type, does have to do with generics. If you use the option above, you shouldn't get it, but if you cast to just List, you will get it. It tells you you shouldn't use List without the type parameter, in your case <DLPFile>.
As others have said, in Java you can't safely cast generic types.
Rather than ignoring the compiler, a better way to fix this problem is to put something a bit more meaningful into the session. Wrap the List in a class that handles this sort of collection of the type. Heck, even add a few instance methods that do something meaningful with the list, rather than a getFiles method.