Please explain what are the differences between List - raw type and List<Object>.
The below code gives run time error:
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42));
String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List list, Object o) {
list.add(o);
}
And this gives compile time error:
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42));
String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List<Object> list, Object o) {
list.add(o);
}
In the second case, you are doing something the compiler can workout is not safe. In the first case, you are using raw types so the compiler doesn't perform the same checks.
Java has not inheritance for parametric types. So List<Integer> is not a subclass of List<Object>, then you can't use List<Integer> or List<String> as parameter for the unsafeAdd method. But you can write:
private static <T> void unsafeAdd(List<T> list, T o) {
list.add(o);
}
and safelly call it:
List<String> strings = ...
unsafeAdd(string, "42");
and get error while:
List<String> strings = ...
unsafeAdd(strings, 42); // error!
You can see more information in the Oracle Generics Tutorial, Generics, Inheritance, and Subtypes
In the first case you pass unparametrized List to unsafeAdd, so compiler has no way to figure out something is wrong.
Passing List<String> to method which expects List is ok. Adding object to List is ok.
In the second case, you are passing List<String> to method which expects List<Object> - and that's not ok. Because this way you are implicitely allowing to add non-String to List<String> - compiler can figure it out in this case and raises an error.
As Peter already said, in the first example you're telling the compiler to use raw types and thus not to perform any checks on the list. Thus it will allow you to add any object to the passed list, even if it is defined to just allow for strings.
In the second example you tell the compiler that it should assume the list to allow any object, thus the add operation would compile. However, passing a List<String> as a List<Object> is not allowed, since the list of strings has more specific restrictions than the contents just being objects and hence the compiler knows that this is unsafe and error-prone.
If you'd define the parameter to be List<? extends Object> list instead, the compiler would accept passing a List<String> since you tell it that the minimum requirement is that the list must accept objects, but it could also impose harder constraints. However, the add operation wouldn't compile now, since the compiler doesn't know if there are harder constraints and if so what these constraints are. Hence it can't assure that the add operation is safe and refuses to compile that line.
However it looks the same, it is not.
List (raw type) does not care what you insert into it.
List<String> is not compatible with List<Object>. You may do this:
String s = "Hello";
Object o = s;
but you must not do this:
List<String> ls = ...;
List<Object> lo = ls; // Compilation error!
Your example is a good illustration why the Java language does not allow it. By allowing such an assignment a malicious method would be able to put anything into a list which a client consider as a list of Strings.
Consider the following code:
public void method changeObject(Object o) {
o = 42;
}
public void method changeList(List<Object> lo) {
lo.add(42);
}
...
String str = "Hello";
changeObject(str);
// Here it is still safe to use str, its value has not been modified
String str2 = str; // OK
List<String> list = new ArrayList<>();
changeList(list); // it is not allowed, but let's assume it is
// here the list variable would violate its contract - everybody has a right
// to expect it is a list of Strings
String str3 = list.get(0); // Ouch! :(
Related
Specifically, if I return a filled ArrayList do I have to return the type with it such as ArrayList<modNode>? To add onto this, if i'm using a generic typing for a custom link list that uses the <T> tag would I have to return ArrayList<modNode<T>>? Just a little curious on the properties of ArrayLists containing generic objects... Thanks in advance!
Let's say you have a method that returns an ArrayList of String objects:
public ArrayList<String> foo() {
ArrayList<String> list = new ArrayList<>();
// ... fill list ...
return list;
}
This is how you would normally1 declare the method in Java 5+ (since the addition of generics). But you don't have to do this. You could declare the method as:
public ArrayList foo() {
// ... code ...
return list;
}
This would mean you are using raw types. Unless you are interacting with a legacy (pre Java 5) library/application you never want to use raw types. The reason is because a raw type is (nearly?) equivalent to returning ArrayList<Object>. You've just lost all type safety given by generics. The reason for generics is to provide compile-time2 type checks so you don't accidentally use the wrong types. For instance, you could now add a Dog to the ArrayList returned by foo even though you intended it to only contain String objects. Also, code using the returned ArrayList has no guarantee that there will only be String objects inside the ArrayList which can result in all sorts of headaches.
You could get around raw types by casting:
String element = (String) foo().get(0);
However, that's basically what generic code compiles down to anyway; except you no longer have compile-time safety.
If the element type is also generic then yes you would want to return that information as well. Let's say you return an ArrayList of Supplier objects instead. It will be each Supplier that returns the needed String objects.
public ArrayList<Supplier<String>> foo() {
// ... code ...
}
It's important you give the Supplier's generic signature here so you can do things like:
for (Supplier<String> sup : foo()) {
String str = sup.get();
// .. do something with "str" ...
}
If you returned ArrayList<Supplier> then each Supplier.get() would return an Object. You've lost all type safety again.
1. You would actually, in virtually all cases, want to return List rather than ArrayList. It's best to program to an interface.
2. It only works at compile-time due to type erasure. Also see this.
The type parameter <T> depends on the actual Type Parameter you supply to Generic Type. For example:-
List<String> names = new ArrayList<>();
String is the actual type parameter of parameterized type List<String>. Behind the scene compiler did casting on each elements automatically. So you can safely expect get method will return String type.
The modNode class is generic type. Then caller has to declare what is the actual type parameter. It could be String, type that extends Node or whatever. Example below:-
List<modeNode<String>> modeNodes = new ArrayList<>();
However your ArrayList<modNode<T>> actual type parameter is already modeNode. Hence get method will probably returns some kind parameterized type modeNode<T>. Ex:-
List<modeNode<String>> modeNodes = new ArrayList<>();
....
modeNode<String> mn = modeNodes.get(0);
Notes:-
** Rename modNode type class name to ModNode to follow Java convention. Class name must start with Capital letter.
ModeNode<Node> mn = ModeNode.getInstance();
//More readable due to following naming convention.
List<ModeNode<Node>> mns = new ArrayList<>();
** It is preferable to declare as interface type Listthan concrete type ArrayList. Unless if you want to use specific ArrayList behaviour implementation.
I need to type method signature so it accepts 2 equally typed parameters of different particular concrete subtypes.
Is it possible to code something like this with generics? How would you solve it? (The case is absolutely an example)
public <T extends List<?>> T<String> sum(T<Integer> sublistOfInts, T<Boolean> sublistOfBooleans){
/*fusion both lists*/
return sublistOfStrings;
}
EDIT: In the end, what I am looking for is a way for the compiler to pass:
ArrayList<String> myList = sum(new ArrayList<Integer>(), new ArrayList<Boolean>());
but not:
ArrayList<String> myList = sum(new ArrayList<Double>(), new ArrayList<Boolean>());
nor
ArrayList<String> myList = sum(new LinkedList<Integer>(), new ArrayList<Boolean>());
(...)
EDIT 2: I found a better example. Imagine an interface Tuple, with child classes Duple, Triple>..., it would be perfectly nice to have something like
<T extends Tuple<?>> T<String> reset( T<String> input, T<Boolean> listToNull){
T copy = input.copy();
for (int i=0; i<input.size();i++){
if (listToNull.get(i)){
copy.set(i,null);
}
}
}
What I suggest you do instead
First, get rid of the method argument generics. There's no reason to force a caller to provide ArrayList<Integer> and ArrayList<Boolean> when you want to return an ArrayList<String>. Just accept any List<Integer> and List<Boolean>, and leave it to your method to turn them into the appropriate return List.
Since you know that you want to return some sort of List of String you can write your parameter as <T extends List<String>> and your return type as simply T.
That leaves us with the hard part: getting your method to instantiate an object of unknown type. That's hard. You can't just do new T();. You need to invoke something that will produce a T on your behalf. Luckily, Java 8 provides a Functional Interface for Supplier<T>. You just need to invoke the get() method to get your ArrayList<String> or whatever else you might want. The part that's painful is that your invoker needs to provide their own Supplier. But I think that's as good as it gets in Java 8.
Here's the code:
public <T extends List<String>> T sum(
List<Integer> sublistOfInts,
List<Boolean> sublistOfBooleans,
Supplier<T> listMaker) {
T sublistOfStrings = listMaker.get();
/*fusion of both lists*/
return sublistOfStrings;
}
At least this compiles:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, ArrayList::new);
And this does not:
ArrayList<String> myNewList = thing.<ArrayList<String>>sum(intList, boolList, LinkedListList::new);
You can even leave off the type parameter on the invocation. This compiles:
ArrayList<String> myNewList = thing.sum(intList, boolList, ArrayList::new);
And this does not:
ArrayList<String> myNewList = thing.sum(intList, boolList, LinkedListList::new);
Why you can't just do what you're asking
In brief, it's because type arguments can't themselves be parameterized. And that's because we don't know how many type arguments they themselves would take, nor the restrictions that might be placed on them.
Take the relatively obscure class RoleList. It extends ArrayList<Object>, so it fits List<?>. But it doesn't take a type argument at all. So if someone invoked your sum() method with RoleList, that would require in your example:
RoleList<Integer> intList = // something
RoleList<Boolean> boolList = // something
RoleList<String> myNewList = thing.sum(intList, boolList);
That clearly can't work since it requires an unparameterized type to take type arguments. And if you took off the type arguments like so:
RoleList intList = // something
RoleList boolList = // something
RoleList myNewList = thing.sum(intList, boolList);
Then your method needs to be able to accept two List<Object> arguments and return a value of List<Object>. And that violates your basic premise, that you be able to control such things.
In reality, RoleList should not be allowed here at all, because you can't ever guarantee that one instance will contain only Integers, another only Booleans, and a third only Strings. A compiler that allowed RoleList here would necessarily have weaker type checking than we have now.
So the bottom line is that you just can't do what you're asking because Java just isn't built that way.
Why that's ok
You can still get complete type safety inside your sum() method using my suggested method, above. You make sure that the incoming Lists contain only Integer or Boolean values, respectively. You make sure that the caller can rely on the return of a specific subtype of List containing only String values. All of the guarantees that make a difference are there.
There are two things that strike me about the above. How are you instantiating sublistOfStrings, and what advantages do you expect to get above using plain old inheritance?
There are a couple of ways of instantiating T<String>. You could have a factory check the class of your arguments, and instantiate it based on that. Or you could do something like:
(List<String>)sublistOfInts.getClass().newInstance()
But you can't just go new T<String>(). So you're basing the implementation of your return type off of the type of one of your arguments anyway (unless there's a way I haven't thought of).
By specifying both arguments are of type 'T' doesn't mean they're exactly of the same concrete type 'T' either. For instance
sum((int)1, (long)2L); // valid
sum((int)2, (double)2.0D); // valid ... etc
public <T extends Number> T sum(T a, T b) {
return a;
}
So you aren't enforcing that sublistOfInts and sublistOfBooleans are both of type say ArrayList, and therefore you can return an ArrayList. You still need to write code to check what type of List<?> you'll want to return based on the arguments.
I think you're better off not using generics, and using something like this:
public List<String> sum(List<Integer> sublistOfInts, List<Boolean> sublistOfBooleans) {
// Determine what subclass of list you want to instantiate based on `sublistOfInts` and `sublistOfBools`
// Call factory method or newInstance to instantiate it.
// Sum, and return.
}
You can still call it with subtypes of List<?>. I don't beleive there's any advantage you could get from generics even if Java did let you do it (which is doesn't, because it can't parameterize T like that).
I know what you have is just an example but if you only want to return a single list that contains the String value of all the contents in a group of other lists you could just specify a method that takes a varargs of unbounded lists.
public List<String> sum(List<?>... lists) {
List<String> sublistOfStrings = new ArrayList<String>();
for(List<?> list : lists) {
for(Object obj : list) {
sublistOfStrings.add(obj.toString());
}
}
return sublistOfStrings;
}
Could you help me understand the difference between unbounded wildcard type List and raw type List?
List<?> b; // unbounded wildcard type
List a; // raw type
Along with this can anybody help me understand what is a bounded type parameter list?
List<E extends Number> c;
Here's a summary of the three:
List: A list with no type parameter. It is a list whose elements are of any type -- the elements may be of different types.
List<?>: A list with an unbounded type parameter. Its elements are of a specific, but unknown, type; the elements must all be the same type.
List<T extends E>: A list with a type parameter called T. The supplied type for T must be of a type that extends E, or it is not a valid type for the parameter.
You should really look at Effective Java, Item 23: Don't use raw types in new code.
To use the example from that book, consider the following example... what if you have a collection where you do not care what types of elements are in it. For example, you want to see how many elements are in common between two sets. You might come up with the following:
public static int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o : s1) {
if (s2.contains(o)) {
++result;
}
}
return result;
}
This example, while it works, is not a good idea to use because of the use of raw types. Raw types just aren't type safe at all... you could end up modifying the set in a way that is not type safe and corrupt your program. Instead, err on the side of caution and use the type safe alternative:
public static int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o : s1) {
if (s2.contains(o)) {
++result;
}
}
return result;
}
The difference is that you can only add null to a Set<?>, and you CANNOT assume anything about the element you take out of a Set<?>. If you use a raw Set, you can add anything you want to it. The numElementsInCommon method is a good example where you don't even need to add anything and you don't need to assume anything about what is in the set. That's why it's a good candidate for using the ? wildcard.
Hope this helps. Read that whole Item in Effective Java and it will really become clear.
To answer the second part of your question... remember that I said when you use the ? wildcard, you cannot assume anything about the element you take out of the set? What if you do need to make an assumption about the interface of the object you removed from the set. For example, suppose you want to keep track of a set of Cool things.
public interface Cool {
// Reports why the object is cool
void cool();
}
Then you might have some code like this:
public static void reportCoolness(Set s) {
for (Object item : s) {
Cool coolItem = (Cool) item;
coolItem.cool();
}
}
This is not type safe... you need to make sure you passed in a set with only Cool objects. To fix it, you might say:
public static void reportCoolness(Set<Cool> s) {
for (Cool coolItem : s) {
coolItem.cool();
}
}
This is great! Does exactly what you want and is type safe. But what if later you have this:
public interface ReallyCool extends Cool {
// Reports why the object is beyond cool
void reallyCool();
}
Since all ReallyCool objects are Cool, you ought to be able to do the following:
Set<ReallyCool> s = new HashSet<ReallyCool>();
// populate s
reportCoolness(s);
But you can't do that because generics have the following property: Suppose B is a subclass of A, then Set<B> is NOT a subclass of Set<A>. The technical talk for this is "Generic types are invariant." (As opposed to covariant).
To get the last example to work you would need to create a Set<Cool> by casting (safely) every element in the Set<ReallyCool>. To avoid letting clients of your api go through this nasty, unnecessary code, you can just make the reportCoolness method more flexible like this:
public static void reportCoolness(Set<? extends Cool> s) {
for (Cool coolItem : s) {
coolItem.cool();
}
}
Now your method takes any Set that contains elements that are Cool or any subclass of Cool. All of these types adhere to the Cool api... so we can safely call the cool() method on any element
Make sense? Hope this helps.
On your first question, the difference between List and List<?>:
One significant difference between the two is that when you have an wildcard as the type, the type of the Collection is unknown, so the add method will throw a compile time error.
You can still get values out of the List<?>, but you need an explicit cast.
Both cases let us put into this variable any type of list:
List nothing1 = new ArrayList<String>();
List nothing2 = new ArrayList();
List nothing3 = new ArrayList<>();
List nothing4 = new ArrayList<Integer>();
List<?> wildcard1 = new ArrayList<String>();
List<?> wildcard2 = new ArrayList();
List<?> wildcard3 = new ArrayList<>();
List<?> wildcard4 = new ArrayList<Integer>();
But what elements can we put into this objects?
We can put only String into List<String>:
List<String> strings = new ArrayList<>();
strings.add("A new string");
We can put any object into List:
List nothing = new ArrayList<>();
nothing.add("A new string");
nothing.add(1);
nothing.add(new Object());
And we can't add anything (but for null) into List<?>! Because we use generic. And Java knows that it is typed List but doesn't know what type it is exact. And doesn't let us make a mistake.
Conclusion: List<?>, which is generic List, gives us type safety.
P.S. Never use raw types in your code.
Java allows me to return a String from a function of return type Object but it does not allow me to return ArrayList of Strings from a function of return type ArrayList of Objects
in the second function if java can check at runtime that s is an object .Then why cant it check that ArrayList of Strings is actually an ArrayList of Objects.
That's why Java has Generics, image this case:
public ArrayList<Object> hola(){
return new ArrayList<String>();
}
it even won't pass the compiler because compiler is expecting an ArrayList that accept "Objects" not String, also generics was created to avoid the use of casting and to help the programmer in compile time to check if whats inside the collection is the same type its meant to hold. You might think it violate the point of Polymorphism but it doesn't really, using Generic Class help alot if you think about it in a positive way.
If you want to do something like that you would have to do this way:
public <T extends Object> ArrayList<T> take(ArrayList<T> list){
return new ArrayList<T>();
}
That's the correct way doing it, the <T extends Object> mean accept an Object that is subclass of Object, so it'd accept everything because all the classes are subclass of Object.
As #arshaji said, generics are not covariant; for a quick definition of covariance, see this Wikipedia link.
What it means is that while:
Object < String
It is NOT true that:
List<Object> < List<String>
This is because of type erasure. At runtime, a List<Whatever> becomes a List (for backwards compatibility reasons): the type information of elements of the List in code are lost at runtime.
In essence, and while not technically accurate, this Java 5+ code:
for (final String s: listOfStrings)
doSomethingWith(s);
is equivalent, at runtime, to this Java 4- code:
for (final Object o: listOfStrings)
doSomethingWith((String) o); // note the cast
why cant it check that ArrayList of Strings is actually an ArrayList of Objects.
Its because although String IS-AN Object, ArrayList<String> IS-NOT ArrayList<Object>. You cannot do the following in Java:
ArrayList<Object> objs = new ArrayList<String>();
It means that ArrayList<String> cannot be assigned to an ArrayList<Object>. Even though at runtime the type of Arraylist is erased, you cannot assign one type of ArrayList to another type at compile time.
If you want your method to return an ArrayList of any Object, then you can change your method signature to:
public ArrayList<? extends Object> methodName()
{
//.. Your code here
return new ArrayList<String>();
}
I have found some strange code, where I said "This is never called, because it would throw a class cast Exception". Well the code gets called and its working.
Can anybody explain me: Why is this working?
The method getZipList() is defined to return a List of Strings, but the internal Logic is returning a List with different objects. Also inside the main method a List of Strings is expected as 'list' but the list contains something different.
public class GenericMethodList{
public static void main(String [] args)
{
GenericMethodList o = new GenericMethodList();
List<String> list = o.getZipList(true);
Iterator<?> iter = list.iterator();
while (iter.hasNext()){
ZipCode zipplace = (ZipCode) iter.next();
System.out.println(zipplace.value);
}
}
public List<String> getZipList(boolean someParameter){
//why is this not throwing an exception at runtime?
List list;
if(someParameter){
list = getZipCodes();//List<ZipCode>
} else {
list = getZipStreets();//List<ZipStreet>
}
return list;
}
private List<ZipCode> getZipCodes(){
List<ZipCode> list = new ArrayList<ZipCode>();
list.add(new ZipCode("code1"));
list.add(new ZipCode("code1"));
return list;
}
private List<ZipStreet> getZipStreets(){
List<ZipStreet> list = new ArrayList<ZipStreet>();
list.add(new ZipStreet("street1"));
list.add(new ZipStreet("street2"));
return list;
}
public class ZipCode{
public String value;
public ZipCode(String value){
this.value = value;
}
}
public class ZipStreet {
public String value;
public ZipStreet(String value){
this.value = value;
}
}
}
Thank you very much for your explanations.
You must be getting an "unchecked cast" compiler warning for the return list line because there you are returning a raw type as a parameterized type. At this position an "unchecked cast" is performed from the raw type (for which there is simply no info on the type of elements) into the parameterized type which is declared for the method. The type parameter could be any arbitrary type—the compiler simply has no idea what to allow and what to prevent.
This cast is unchecked in the sense that it cannot be performed at runtime: at runtime the type parameters of the list instance are erased anyway, so the JVM has no idea you are doing a bad thing there.
You should be getting a warning. Because of type erasure (the .class file doesn't actually list the parameterized type for backwards compatibility), the runtime type of a List<E> is just List, and the JVM doesn't seen any difference. The error will come in a distant location when somebody tries to pull a String out of that List and gets a ZipCode instead.
Because you are using a raw type 'List' (i.e. a generic type with no type parameter specified).
Raw types are there for legacy purposes, but should be avoided in new code because it loses type safety, as you have seen :
http://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
You must get below warring at compile time.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Since Generics are based on the implementation of type erasure. So At run time it do not have information about Generic type(generaly called Non-Reifiable Types)
The issue is that you combine in your solution RawType with generic. You perform this in method getZipList(boolean). As List there has none type you brake the type assurance.
Next place where you cheat the compiler is the way you declare the Iterator<?>, as you do not declare the generic parameter i will store object. So next time you avoid type assurance that you are taking advantage in look where you cast to expected type. That is why its work. Normally the casting is performed by compiler but you have implement it by your self in correct way.
IF your code would look like this
Iterator<String> iter = list.iterator();
while (iter.hasNext()){
ZipCode zipplace = (ZipCode) iter.next();// Compilation error
System.out.println(zipplace.value);
}
IF your main method will look like this:
while(String s : list) {
System.out.wirteln(s);
}
An ClassCastException will be thrown. Because the code "will look like"
Iterator<Object> iter = list.iterator();
while (iter.hasNext()){
ZipCode zipplace = (String) iter.next();
System.out.println(zipplace.value);
}
As in Java you can not cast this way. A exception is thrown.
To summarize you have create a code that brakes the type assurance but you have implemented the correct usage of it. That is why its work.