why do we need the pure <?> in Java?
Does anything passed can only be used as Object ? So it's the same useful as any class casted to Object (only 9 methods could be available)
Thanks
I mean, if you have List< ? > list, you can only use items as Object. This means list.get(0) gives you Object and nothing more. Yes, also I can store to there anything, but is it useful for you? It's like you have a good an expensive thing and put it to a garbage bin. The only you can get from a trash bin is trash. As well you can put any good object of any class to List but get only Object from there with only 9 methods.
There are two scenarios where an unbounded wildcard is a useful approach:
If you are writing a method that can be implemented using functionality provided in the Object class.
When the code is using methods in the generic class that don't depend on the type parameter.For example, List.size, or List.clear. In fact, Class<?> is so often used because most of the methods in Class<T> don't depend on T.
For example, see Collections.swap method:
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
Knowing the type does not aid in swapping two elements within a List, so an unbounded wildcard is used. You pass in a List - any List - and the method swaps the indexed elements. There is no type parameter to worry about.
For more information, see: Unbounded Wildcards.
I think the best answer is because sometimes you literally do not know what type a class is parameterized with. As Michael pointed out List<?> is not the same as List<Object>. So ? provides different semantics, which are useful in ensuring program correctness.
Consider two lists. One list is List<String> and the other is List<Integer>. Both of those are Objects, but we still don't want to get them mixed up. We don't want to put a String in the Integer list or vice-versa. So if you have a type declared as List<?>, you cannot safely put any type in that list. The ? could be String or Integer, but you don't know what it is, and you don't want to mix them up, so you can't put either in.
OTOH, List<Object> is just a list of objects, and since you declared it to hold any objects, you can safely put both String and Integer in the list. You said you didn't care what went into the list by declaring it of type Object, so any type is fine. In any meaningful way I can think of, List<Object> is equivalent to a raw type. It's a list that you manage yourself, without help from the compiler.
Related
For instance I have two Arraylists with different data types.
ArrayList<Integer> intValues = new ArrayList<Integer>();
intValues.add(1);
intValues.add(2);
intValues.add(3);
ArrayList<String> strValues = new ArrayList<String>();
strValues.add("4");
strValues.add("5");
strValues.add("6");
If both of these lists contained the same data type objects, I would easily call addAll function;
intValues.addAll(intValues2);
But of course if I try to call addAll function with these different type lists, compiler warns me with incompatible types: ArrayList cannot be converted to Collection<? extends Integer> warning.
So I have to create a bad solution like;
for(String s: strValues)
{
intValues.add(Integer.parseInt(s));
}
Is there a better way to do this, I mean, creating a class which implements List, overriding addAll function etc. so I will be able to call;
intValues.addAll(strValues);
And intValues list will contain 1,2,3,4,5,6.
Edit: I really don't want to store String values in an Integer array, I have to deal with some creepy old code at the moment and I need a Collection to hold some differend kinds of classes, trying to create a Constructor for those objects, this integer-string scenario is just a simple way to introduce my problem.
Let me tell you about my current situation with another integer-string like scenario:
Creepy class A is car, it holds car's weight, price, color, engine type.
Creepy class B is watch, it holds watch's still type, movement type, price, lug size etc.
I am trying to create a holder class, so it will hold those classes and adding a few functions (for example, overriding compare method makes the holder class to compare prices of different classes).
Now I think I have to create a HolderHolder class which implements List so I can call holderHolder.addAll(carsList) and holderHolder.addAll(watchesList), and it will hold these as Holder objects and yes, this does not look pretty.
You act as if what you want is self-evident and logical. It really isn't. "4" and 4 are entirely unrelated, and expecting that your list of integers now has a value 4 when you call addAll with "4" is, as a consequence, as bizarre as expecting your list of movies to gain 'Forrest Gump' when you call .addAll(colorsOfTheRainbow) on that, because in your mind, 'green' is so incredibly similar to 'Forrest Gump', that you might as well assume that. (Here, 'green' is "4" and 'Forrest Gump' is 4).
So let's do some work and make this more sensible:
That 'assumption' (that "4" is so similar to 4, that you want .add("4") to just mean that 4 shows up in your list) needs to encoded, explicitly, in your code. Now it makes sense, and now you can write a function that maps Green to Forrest Gump and use it for that example just the same - we've generalized the principle.
What you're really talking about is a mapping function that maps an element of your List<String> (so.. a String) to a type that your target list is of (Integer), and you then want the operation: Take this list. Map every value in it with my mapping function. Then, add all the mapped values to this other list.
That makes perfect sense.
So, write that.
List<Integer> intValues = ...;
strValues.map(Integer::valueOf).forEachOrdered(intValues::add);
Looks like bad smell.
One bad Solution can be an own implementation of an List with Type Object. But than you have to cast and work with the Classes of the primitive types.
I think i every case you have to parse or cast. That cost to much of performance just for easy call of addAll.
I would think about the incoming data and why they have to be the same but in different types?
Edit:
If i get to know it correct. It is a little bit hard to understand without more detailed infos.
But maybe you can write an mapper class to map thoose old creepy classes in one new class an then you can put these new class in an collection and can compare all by overriding equals.
public class CreepyClassMapper
{
public CreepyClassMapper(Car aCar, Watch aWatch)
{
}
#override
private boolean equals(Object obj)
{
// maybe add an instance check
CreepyClassMapper other = (CreepyClassMapper) object;
// do your compare stuff
return true;
}
}
if i were you, i will create a function like this in util class
public void append(ArrayList<Integer> intValues, ArrayList<String> strValues){
}
List is an interface.
List<String> list=new ArrayList<String>();
Here, lets say we are creating a reference variable of list interface and assigning it to the ArrayList object which implemets List interface.
Lets say if we want to get the size of the list. We will use list.size() which will internally invoke the size() method of ArrayList object using Runtime Polymorphism.
Simply here what I mean to say is the methods of List is implemented in ArrayList class thats why we are able to use it.
Question is,
How I am able to use size() and how the size method is being implemented in what Class ?
List<WebElement> noOfRows=driver.findElements(By.xpath(".//*[#id='leftcontainer']//tbody/tr"));
List<WebElement> noOfCol=driver.findElements(By.xpath(".//*/tr/th"));
int rowSize=noOfRows.size();
int colSize=noOfCol.size();
I hope you guys are getting my point.
When I say List<String> list= new ArrayList<String>();
That means I am going to use ArrayList methods as runtime polymorphism.
But what in this case...
driver.get("https://money.rediff.com/gainers/bse/daily/groupa?src=gain_lose");
List<WebElement> noOfRows=driver.findElements(By.xpath(".//*[#id='leftcontainer']//tbody/tr"));
List<WebElement> noOfCol=driver.findElements(By.xpath(".//*/tr/th"));
int rowSize=noOfRows.size();//How I am able to use method of a List interface
int colSize=noOfCol.size();
I am able to run the program and do everything but just want to clear the concept here . Seems like I am confused on a minor issue but want this confusion to be gone. Thankyou in advance
A variable in Java can have a type List, but it is not possible to instantiate the List interface, that is, to create an object of type List in memory. The objects in memory are of some other type that implements the List interface.
The object returned from findElements implements the interface List, but the actual class of that object is unknown to the compiler. You can query it at runtime, by using the getClass method, but that is usually not necessary because you can treat it as a List without having to know exactly what kind of List it is.
When you call size on the List returned by findElement, the JVM selects the correct size implementation to use based on the actual type of the object. So if it just happens to be an ArrayList then you'll get ArrayList.size, etc. Often the type of List returned from a method like this isn't one of the usual types from java.util but some custom implementation that is tailored to the task at hand.
I faced a problem yesterday, when I was writing my homework. I finished the homework, but I still don't really understand why my code works. I had to write a sort function that takes an varargs of any comparable generic object as an argument and return the argument. The problem was that I had to return an array of sorted objects. So I had to learn more about varargs lists and arrays.
The function was defined like this.
public <T extends Comparable<T>> T[] stableSort(T ... items)
and inside the function I made a list, which I would sort and do all the work on.
List<T> list = new ArrayList<T>(Arrays.asList(items));
and at the end of the function I was returning list toArray so that it matched the output type T[].
list.toArray(items.clone());
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function. That seemed like doing two same things to me. I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray(). I know that this was the correct way to write it, because I finished the homework yesterday and found out this way from forums of the class, but I still don't understand why.
EDIT
The task required me to create a new array with sorted files and return it instead. Due to Type Erasure, it is not possible to instantiate an array of a generic type without a reference to a class that fits the generic. However, the varargs array has type T, so I should have cloned an array of a type which fits the generic constraints. Which I didn't know how to do in time. So I decided to use list to make my time easier till the deadline.
My question is since I already made the list from the varargs, why do I have to do items.clone()
You are right. Unfortunately, the compiler will be unable to determine the type of the array if you simply use the toArray() method. You should get a compilation error saying Cannot convert from Object[] to T[]. The call to item.clone() is required to assist the compiler in type-inference. An alternate approach would be to say return (T[])list.toArray
That said, I would not recommend either of the approaches. It doesn't really make sense to convert an array to a list and convert it back to an array in the first place. I don't see any significant take-aways that you would even understand from this code.
It seems to me there are a few questions here, that may have come together to create some confusion as to why what needs to be done.
I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray().
This is probably just the way it is typed, but it should be made clear that you don't clone the objects in the array, but only make a new List with the references to the objects in the array. The objects themselves will be the same ones in the array as in the List. I believe that is probably what you meant, but terminology can be tricky here.
I thought arrays.asList() would clone the values of array to list...
Not really. Using Arrays.asList(T[] items) will provide a view onto the array items that implements the java.util.List interface. This is a fixed-size list. You can't add to it. Changes to it, such as replacing an element or sorting in-place, will pass through to the underlying array. So if you do this
List<T> l = Arrays.asList(T[] items);
l.set(0, null);
... you've just set the element at index 0 of the actual array items to null.
The part of your code where you do this
List<T> list = new ArrayList<T>(Arrays.asList(items));
could be written as this:
List<T> temp = Arrays.asList(items);
List<T> list = new ArrayList<T>(temp);
The first line is the "view", the second line will effectively create a new java.util.ArrayList and fill it with the values of the view in the order they are returned in by their iterator (which is just the order in the array). So any changes to list that you make now don't change array items, but keep in mind that it's still just a list of references. items and list are referencing the same objects, just with their own order.
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function.
There could be two reasons here. The first is as CKing said in his/her answer. Because of type erasure and the way arrays are implemented in Java (there are separate array types depending on whether it's an array of primitives or references) the JVM would not know what type of array to create if you just called toArray() on the list, which is why that method has a return type of Object[]. So in order to get an array of a specific type, you must provide an array to the method that can be used at run-time to determine the type from. This is a piece of the Java API where the fact that generics work via type-erasure, aren't retained at run-time and the particular way in which arrays work all come together to surprise the developer. A bit of abstraction is leaking there.
But there might be a second reason. If you go check the toArray(T[] a) method in the Java API, you'll notice this part:
If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.
Suppose some code by another dev is using your stableSort method like this:
T[] items;
// items is created and filled...
T[] sortedItems = stableSort(items);
If you didn't do the clone, what would happen in your code would be this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
// List is now a new ArrayList with the same elements as items
// Do some things with list, such as sorting
T[] result = list.toArray(items);
// Seeing how the list would fit in items, since it has the same number of elements,
// result IS in fact items
So now the caller of your code gets sortedItems back, but that array is the same array as the one he passed in, namely items. You see, varargs are nothing more than syntactic sugar for a method with an array argument, and are implemented as such. Perhaps the caller didn't expect the array he passed in as an argument to be changed, and might still need the array with the original order. Doing a clone first will avoid that and makes the effect of the method less surprising. Good documentation on your methods is crucial in situations like this.
It's possible that code testing your assignment's implementation wants a different array back, and it's an actual acquirement that your method adheres to that contract.
EDIT:
Actually, your code could be much simpler. You'll achieve the same with:
T[] copy = items.clone();
Arrays.sort(copy);
return copy;
But your assignment might have been to actually implement a sorting algorithm yourself, so this point may be moot.
You need to use this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
when you want to do an inline declaration.
For example:
List<String> list = new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc"));
By the way, you didn't have to use return list.toArray(items.clone()); You could have used, for example, return list.toArray(Arrays.copyOf(items, 0));, where you are passing to list.toArray() an empty array that contains none of the arguments from items.
The whole point of passing an argument to the version of list.toArray() that takes an argument, is to provide an array object whose actual runtime class is the actual runtime class of the array object it wants to return. This could have been achieved with items.clone(), or with items itself (though that would cause list.toArray() to write the resulting elements into the original array pointed to by items which you may not want to happen), or with, as I showed above, an empty array that has the same runtime class.
By the way, the need to pass the argument to list.toArray() is not a generics type issue at all. Even if you had written this with pre-generics Java, you would have had to do the same thing. This is because the version of List::toArray() that took no arguments always returns an array object whose actual runtime class is Object[], as the List doesn't know at runtime what its component type is. To have it return an array object whose actual runtime class is something different, you had to give it an example array object of the right runtime class to follow. That's why pre-generics Java also had the version of List::toArray() that took one argument; even though in pre-generics, both methods were declared to return Object[], they are different as the actual runtime class returned is different.
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.
Can someone please explain what the difference between ArrayList<?>, ArrayList and ArrayList<Object> is, and when to use each? Are they all same or does each have some different meaning at the implementation level?
ArrayList<Object> is specifically a list of Objects whereas ArrayList<?> is a list whose concrete type we are unsure of (meaning we can't add anything to the list except null). You would use the latter when the list's type is irrelevant, e.g. when the operation you want to perform does not depend on the type of the list. For instance:
public static boolean isBigEnough(ArrayList<?> list) {
return list.size() > 42;
}
This is all covered in the generics tutorial (see the wildcards section).
Finally, ArrayList with no type parameter is the raw type: the only reason it's even allowed is for backwards compatibility with Java versions under 5, and you should refrain from using it whenever possible.
ArrayList<?> means "an ArrayList instance containing a type which is to be determined"
ArrayList is the class of an ArrayList
An ArrayList<Object> means an instance of ArrayList containing Object types.
This looks like it could be a good write-up on this (and more): http://docs.oracle.com/javase/tutorial/java/generics/types.html