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.
Related
I am going through the Java Arrays and additionally, I am looking into generics. Below are the two methods of initializing an array
int[] data = {1,2,3,4,5,6,7,8,9};
// or
int[] data;
data = new int[] {1,2,3,4,5,6,7,8,9};
But when I am using generics, I have mixture of data types for example,
String[] outcome = {"0 wins", "Tie" , "X wins"};
Above array has a single data type of String. What if in an array I have something like below
outcome = {7, "Tie" , 9.0};
now I have a mixture of data types in an array. How I can write it or if it is possible to do with generics? Can I do it with ArrayList?
Thank you
I'd like to correct this:
But when I am using generics, I have mixture of data types
Generics require homogeneous data types. For instance, a List<Integer> is a list that can only ever hold an Integer, and a List<? extends Number> can only ever hold Numbers, which covers other number types like Long, Short, Double, etc...but are referred to by the single type Number.
Anyway, what you're looking for is a Bag - a collection which can hold any arbitrary object. You can implement this with an Object[] or a List<Object>, and you're going to have to check the type of each element you pull out when you want to use it, since there's no way in Java to have a heterogeneous data type, which is what you're looking for.
tl;dr: In my opinion, arrays are not a good fit for the problem, you should use objects instead.
This is not a direct answer to your question, but an answer in the form of a redesign.
First of, let us tackle your statement about generics and arrays. Arrays are covariant and retained, while generics are invariant and erased.
Covariant means that when B extends A, you can Write A[] aArray = new B[someSize];. Invariant means that this is not possible: ArrayList<A> aList = new ArrayList<B>(); will lead to a compile time error.
Retained means that the information about the type is retained at runtime: an array always "knows* what type its elements has. Erased means that the type information is gone after compilation. This is also called Type Erasure.
The mixture of covaraint and retained vs. invariant and erased has good potential to get you into trouble. That is the reason why ArrayList uses an Object[] instead of a T[] as its backing datastructure.
Now to the actual question. As was already said by others, we could go down the road ande create an Object[]. I would strongly advice against this since we loose all type information. The only way to get back that information is a instanceof check, which makes your code rigid. Imagine you change the type of an entry. In this case, the instanceof will return false, possibly leading to unwanted behavior and (best case) some test turning red or (worst case) we may not notice it.
Now how to get around this? We create a class representing (what I infer are) match results:
public class MatchResult {
private final int firstTeamScore;
private final int secondTeamScore;
public MatchResult(final int firstTeamScore, final int secondTeamScore) {
this.firstTeamScore = firstTeamScore;
this.secondTeamScore = secondTeamScore;
}
public int getFirstTeamScore() {
return firstTeamScore;
}
public int getSecondTeamScore() {
return secondTeamScore;
}
public String getResultForFirstTeam() {
if (firstTeamScore > secondTeamScore) {
return "Win"; // In an actual implementation, I would replace this with an enum
} else if(firstTeamScore = secondTeamScore) {
return "Tie";
} else {
return "Lose";
}
}
// You can add a method public String getResultForSecondTeam(), I omitted it for brevity
}
What have we won? We have types. The scores are always ints, the results always Strings. If we were, for example, change the type of the getReultforFirstTeam() from String to, e.g., an Enum, we would get compiler errors for all locations where the type does not match anymore. Thus, we hone the fail-fast design and are forced to modify the code where necessary. And thus, we do not even have the chance to get the sneaky, unwanted behaviour we had before.
1 way to handle this is that you create an array of Object, that can accommodate all of data types
Object[] outcome = {7, "Tie" , 9.0};
And later you can access objects like:
if(outcome[0] instanceof Integer){
Integer i = (Integer) outcome[0];
}
and vice versa..
outcome = {7, "Tie" , 9.0};
is simply not legal.
You can only use this syntax - an array initializer, where the element type is omitted after the equals - in a variable declaration, e.g.
Object[] outcome = {7, "Tie" , 9.0};
As mentioned earlier, you could use an Object array. Alternatively, you can use a generic class. Here's an example:
public class Queue<E> {
private ArrayList<E> queue;
/**Unparametrized constructor**/
public Queue() {
queue = new ArrayList<E>();
}
/**Enqueues an element into the queue.**/
public void enqueue(E val) {
queue.add(val);
}
/**Dequeues an element from the queue.**/
public E dequeue() {
E output = queue.get(0);
queue.remove(0);
return output;
}
/**Gets the current size of the queue.**/
public int size() {
return queue.size();
}
}
Read up on generics and how to use them.
You're going to have to create an array of objects, since all objects in java extends Object:
Object[] arr = new Object[3];
//to add objects to it:
arr[0]=new String("element at index 0");
arr[1]=new Integer(1);
arr[2]=new Character('2');
And to find if the object at index x is (for example) an Integer then your going to have to use a cast:
int x = (Integer)arr[x]; //x could be 0 or 1 or 2
Also you can do it with an ArrayList:
List<Object> listObjects = new ArrayList<Objects>();
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;
}
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>();
}
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! :(
Hi guys i got a problem with an unsafe cast operation.
Here is the problem. I gotta Constructor which accepts different types of Orders (PlayList, UserOperations, etc.) or should. So i decided to give it a Parameter as Object and check the type of the received once called. The Problem is one of those is a parameterized ArrayList (ArrayList < PlayList >) and since it is impossible to check a parameterized ArrayList I have to "dig" in. At first step i check if it is an ArrayList, at second I go into it an check the type of its content.
The problem afterwards is I get an unsafe cast warning which i dunno yet how to handle.
Greetings
public Order(int aTyp, Object aOrderContent) {
this.orderTyp = aTyp;
if (aOrderContent instanceof ArrayList< ? >) {
ArrayList<?> objList = (ArrayList< ? >)aOrderContent;
if (objList.get(0) != null && (objList.get(0)) instanceof PlayList) {
playList.addAll((ArrayList<PlayList>)aOrderContent) ;
}
} else if (aOrderContent instanceof UserOP) {
}
}
Rethink your design. Don't make the constructor take an Object. This is too opaque and allows consumers of your code to pass any type. It sounds like you should be using a generic class instead (though it's hard to tell based on your partial example). Possible solution:
// Might need an upper bound on T if you want to limit the allowed parameterizations
class Order<T> {
private final int orderTyp;
private final List<T> someList = new ArrayList<>();
public Order(int aTyp, List<t> aOrderContent) {
this.orderTyp = aTyp;
someList.addAll(aOrderContent);
}
}
Keep in mind it may be better to use an enum instead of int for the orderTyp, if you know (at compile time) all the possible type values.
Side note: if a List is empty, List#get(0) with throw an exception, not return null.
Instead of making the constructor take an Object overload you constructor. Have one take an List<?> and one take a UserOP ect. Also, it would be ideal if all the options for the List extended from the same interface so you could have List<MyInterface> instead of List<?>