I'm studying for the SCJP/OCPJP and I came across a sample question that seams strange to me.
The sample code instantiated two generic collections:
List<?> list = new ArrayList<?>();
List<? extends Object> list2 = new ArrayList<? extends Object>();
The "correct" answer to the question was that this code would compile but adding to either collection would produce a runtime error.
When I try to compile code like this I just get errors. The Java tutorial does not even show this type of code, it instead usually uses wildcards as a part of an upcast.
Collection<?> c = new ArrayList<String>();
Are the two generic collections above even legitimate code? The second by my logic would only disallow interfaces. The first one looks completely useless. Why use a generic that makes no attempt at control?
Check out the excellent Java generics tutorial PDF. More specifically the section about wildcards contains the answer to your question, and I quote
Collection<?> c = new ArrayList<String>();
c.add( new Object() );
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.
If you want to declare Type at runtime, you can do something like this:
public class Clazz1<T> {
private final List<T> list = new ArrayList<T>();
private List<T> getList() {
return list;
}
/**
* #param args
*/
public static void main(String[] args) {
Clazz1<Integer> clazzInt = new Clazz1<Integer>();
clazzInt.getList().add(2);
System.out.println(clazzInt.getList());
Clazz1<String> clazzString = new Clazz1<String>();
clazzString.getList().add("test");
System.out.println(clazzString.getList());
}
}
I answered this somewhat before in this answer. ? cannot be used in the instantiation. I'm not sure why it says the code would compile, none of the java compilers I have used would allow that. You could do what is shown above by the following:
List<?> list = new ArrayList();
That would compile and run, but you couldn't do:
list.add("hello world"); //This wouldn't compile
new produces a concrete instance of an object. The concrete instance can have only one type, including any generics. Knowing this, wildcards cannot work with new.
Related
Caveat: Yes, I know that I could make the calling methods use Collection rather that a sub-class or cast the Collection or several other things. And will probably do that while waiting for answers. But I'm curious if this is possible and my Google-fu isn't getting me anywhere. :-)
So I have a method which has either a Set or a List as a parameter and the returns a new one of the same (and, yes, there are other sub-classes of Collection, but these are the two I will see). So I tried using generics:
private <T extends Collection<String>> T doStuff(T input) {
T output = (input instanceof List)
? new ArrayList<String>()
: new HashSet<String>();
// do stuff to fill output from input
return output;
}
This version has red ink saying that I can't convert an ArrayList to T. I tried variations on this with no luck. Any thoughts?
EDIT: Thanks for humoring my curiosity. I will of course be using something simpler like just returning a Collection, but it is good to know what the issue was for future reference.
To avoid the compilation error, you must instantiate an object of type T which is not possible in Java because of the type erasure. I suggest using two overloaded methods instead of a generic one.
Let S extends Collection<String> be the abstract type of the collection you'd like to return from your method, i.e. List or Set.
Let C extends S be the runtime-type of the returned result, i.e. ArrayList or HashSet.
In order to get an instance of C at compile-time, you will have to explicitly provide a Class<C> instance, as an argument.
So, the method becomes:
public <S extends Collection<String>, C extends S> S doStuff(S input, Class<C> clazz) throws Exception {
C output = clazz.newInstance();
// do stuff to fill output from input
return output;
}
You'd be able to call this methods with:
List<String> list = new ArrayList<String>() {{ add("Hello"); }};
doStuff(list, ArrayList.class);
Set<String> set = new HashSet<String>() {{ add("World"); }};
doStuff(set, HashSet.class);
Of course, you should take care of the checked exceptions that might be thrown from the clazz.newInstance() callback.
If you're really able to return a Collection, that means you only need the Collection interface, and you don't care about the speed/size trade-offs of the various Collection subclasses.
If that's truly the case, then it doesn't matter whether you return a List or a Set, regardless of which you're called with. So just do that:
private Collection doStuff(Collection input) {
// do stuff to fill output from input
return new ArrayList(input);
// or, return new HashSet(input);
}
If you, or your callers, do actually care about the methods (or speed/size guarantees) of particular Collection subclasses, then you'll need to overload on the actual subclasses of interest.
Note of course that overload resolution is determined at compile time using static (reference) types, not the dynamic types actually pointed to at runtime.
I am trying to understand the features of a generic class and its concrete instances, and also the features of a generic collection.
suppose I define a generic class: Stack<T>
now I'm trying to use it this way:
Stack<String> ts = new Stack<>(5);
Stack<Object> to = new Stack<>(5);
to = ts // compilation error!
so obviously Stack<String> is not a Stack<Object>.
I got confused when I defined a generic Collection:
Set<E> elemnts = new Hashset<>();
and noticed that I can use elements.toString()! this means that Set<E> elemnts extends Object!
so whats really going on here?
The compilation error doesn't mean that String isn't an Object, which isn't true. E.g., the following code works just fine:
String s = "I am String!";
Object o = s;
What the error means is that Stack<String> isn't a Stack<Object>. For example, you could call push(new Object()) on a Stack<Object>, but obviously not on a Stack<String>.
So what are generics in Java exactly? As mentioned by dave, the generic types are compiler enforcements. To be exact, at runtime, the generic types do not exist anymore. They are replaced by Object or the upper bound of a generic type. This process is called Type Erasure. So, generic types only exist at compile-time.
Then why can we not cast an ArrayList<String> into an ArrayList<Object>? Even though the type parameters stand in a relation (String is a Object), the generic classes do not. Let me give a simple example. If
ArrayList<String> strings = new ArrayList<String>();
ArrayList<Object> objects = strings;
would be possible, if one calls
objects.add(new Object());
what should happen? Since strings can only hold Strings, Java cannot "put" an Object into this list. But on the other hand, if you look at the declaration of objects there is nothing hinting, that this should not work. Therefore, Java does not allow conversion between generic classes, even if the generic types stand in a relation.
But generic types are not present at runtime. So what is the hustle? Should ArrayList<String> not really be an ArrayList<Object> at runtime and therefore, all of this should not matter? Well no. Since you know the type at compile-time, you can use certain contracts. For example, when retreiving an element out of an ArrayList<String> one can be sure that this retrieved object has the public char charAt(int) method (since a String has this method). If, for whatever reason, this ArrayList<String> would return an Object instead of a String, one could not call the charAt(int) method.
String class is implicitly subclass of Object. Since Object class is super class of all the classes in java. Therefore below statement is valid
String x="hello";
Object o= x;
But Stack<String> is not a sub class of Stack<Object>. Therefore code gives compile time error.
Stack<String> ts = new Stack<>(5);
Stack<Object> to = new Stack<>(5);
to = ts // compilation error!
For the following:
Stack<String> ts = new Stack<>(5);
Stack<Object> to = new Stack<>(5);
to = ts // compilation error!
Yes, this compilation fails, because generics are essentially a feature of the compiler enforcing type safety. Their is actually only one Stack class as far as the virtual machine is concerned.
This statement:
Set<E> elemnts = new Hashset<>();
Is equivalent to:
Set<E> elemnts = new Hashset<E>();
The notation was introduced in Java 7 and is sometimes called the 'diamond operator' because of the <>. It is just to remove redundancy.
It is the Set class that is extending Object, just like all classes in Java extend object.
The following holds true for the Class object in your code:
boolean b = ts.getClass() == to.getClass(); // b is true
When instantiating ArrayLists I am used to seeing code like this
ArrayList<Type> arr = new ArrayList<Type>();
or
ArrayList<Type> arr = new ArrayList<>();
however today I have come across an instantiation of ArrayList that looks like this:
ArrayList<Type> arr = new <Type>ArrayList();
what is going on, and why does that give an "unsafe operations" compile warning?
Edit:
Yes, found the reference. See JLS §15.12.2.1 - Identify Potentially Applicable Methods:
If the method invocation includes explicit type arguments, and the
member is a generic method, then the number of type arguments is equal
to the number of type parameters of the method.
This clause implies that a non-generic method may be potentially
applicable to an invocation that supplies explicit type arguments.
Indeed, it may turn out to be applicable. In such a case, the type
arguments will simply be ignored.
Emphasis mine.
Also see JLS §15.9.3 - Choosing the Constructor and its Arguments, for understanding how the constructor invocation is resolved. It also mentions that the above mentioned process is followed for resolution.
Original answer:
Such kind of invocation is often required, when you have a generic constructor, and the compiler is not able to infer the correct type arguments. For example, consider the below code:
class Demo<T> {
public <X> Demo(X[] arg1, X arg2) {
// initialization code
System.out.println(arg1.getClass());
System.out.println(arg2.getClass());
}
}
Suppose you invoke that constructor like this:
Demo<String> demo = new Demo<String>(new String[2], new Integer(5));
You would think that the type inference should fail, as the type arguments should have same types. Here we're passing String and Integer types. But it doesn't. The compiler infers the type X as:
Object & Serializable & Comparable<? extends Object&Serializable&Comparable<?>>
Now, you might want the type parameter to be inferred as just Object, then in that case, you can provide explicit type arguments, as in the below code:
Demo<String> demo = new <Object>Demo<String>(new String[2], new Integer(5));
This is similar to how you give explicit type argument while method invocation.
Now, in your code, you have given the explicit type arguments, but you're using raw type of the class to instantiate it:
ArrayList<Integer> arr = new <String>ArrayList();
The <String> is the explicit type argument for the constructor, and compiler will be fine with it. But the issue is, you're instantiating raw type ArrayList, and that is where compiler is giving your unchecked warning. If you change that code to:
ArrayList<Integer> arr = new <String>ArrayList<>();
The warning will go away. But since ArrayList constructor is not a generic constructor, the type argument seems to be just ignored by the constructor. In fact there is no use of that type argument there.
Strangely enough, this also compiles:
public static void test() { }
public static void main(String... args) {
Main.<Integer>test();
}
...even though test() is a non-generic method.
I've just tried:
ArrayList<Integer> arr = new <String>ArrayList();
And got the same warning (not an error!). Looks like the compiler ignores1 the generics after the new keyword and before the ArrayList. It's just like writing:
ArrayList<Integer> arr = new ArrayList();
1 I'm not sure if it really "ignores" that, I'll be glad if someone confirms/correct me
The code, it does nothing!
int a = new <String>Integer(5);
It also compiles but generates a warning of "unused generics".
So basically it is useless but not bad enough to generate an error by default it seems. Either way your arraylist is NOT properly generified here.
Please note that generics are compiled away anyway so at the bytecode level it probably won't look any different. I have looked in eclipse for a way to turn this into an error instead of warning but no luck there.
UPDATE
This answer boils down to the same as the other one which is currently at +5, so why is mine downvoted? Please leave a comment if you downvote.
My question is exactly similar to the one here. Just that, I want to do it in Java.
Suppose, I want to crate a list as:
List<a_type> myList = new ArrayList<a_type>();
The problem is that what a_type would be, is decided on the base of a condition. The two possible types are totally unrelated, having no superclass but Object in common. My problem will be solved if I can store the Type in a variable requiredType and use it as List<requiredType>. I am not sure if I'm just being greedy or foolish or ignorant in wanting this, but that's the situation.
Although not important, but the scenario that explains why I need it is explained here.
PS: Please do not mention type-erasure. Kindly consider the situation as it is. I could have very well myself mentioned it in the question. If you think there are design issues, kindly read the situation here and suggest alternatives.
That's not possible in Java, because of the type-erasure and due to the fact that generics is only limited to compiletime.
e.g.
List<Integer> integers = new ArrayList<Integer>();
During compiletime, when the the type-erasing is done becomes
List integers = new ArrayList();
The generics is only a way to tell the compiler, that the list should only deal with objects of type Integer, and let's it point it out if you have somewhere tried to add something other than Integer for type safety.
The only effective (if “risky”) way I could see to do so, would be something akin to:
List<?> myList = new ArrayList<?> ();
/* … */
final Object o = myList.get (0);
if (o instanceof OneType) {
OneType o1 = (OneType)o;
/* … */
} else if (o instanceof OtherType) {
OtherType o2 = (OtherType)o;
/* … */
} else {
throw new RuntimeException ("unexpected type found in list…");
}
You will probably want to litter that code with #SuppressWarnings
Possibly a better(?) idea would be to implement at least a “marker interface” (without perhaps any methods) in both types, and use that as the commonality.
public interface CanBeAddedToDualTypedList { };
public class OneType implements CanBeAddedToDualTypeList { … };
public class OtherType implements CanBeAddedToDualTypeList { … };
List<CanBeAddedToDualTypedList> myList =
new ArrayList<CanBeAddedToDualTypedList> ()
Edit I also put an answer on your related question, showing a concrete example of using a common interface for that specific case.
Java generic are not available at runtime so not possible with java
Search for "Type Erasure" on google and you will understand it better.
You can store the a_type.class object while that wont help you with generics, it can be used to add additional runtime type checks class.cast(...).
Generics do not support runtime typing, the generic type itself does not exist at runtime. C# has a runtime class for each Generic type List<String> is different from List<int>. In java this is not the case both List<String> and List<Integer> degrade to List at runtime.
AFAIK (never tried it) extending a Generic class stores the type somewhere accessible to reflection class StringList extends List<String>{}, the StringList remembers the Generic type used to extend List.
How come, in Java, I can write
List<?> list = new LinkedList<Double>();
but not
List<Container<?>> list = new LinkedList<Container<Double>>();
where Container is just something like
public class Container<T> { ... }
This came up because I have a method that accepts a List<Container<?>>, and I would like to use Arrays.asList to pass arguments to it:
process(Arrays.asList(new Container<Double>(), new Container<Double>()));
But the language doesn't allow this, because it infers the type of Arrays.asList to be List<Container<Double>>, and that is not assignable to List<Container<?>>.
If I add a String-parameterized Container to the call,
process(Arrays.asList(new Container<Double>(), new Container<String>()));
it still doesn't work, because it infers the type List<Container<? extends Serializable & Comparable<?>>> for Arrays.asList. Only if I pass in something that is neither Comparable nor Serializable does it work properly.
Of course, I can put in a cast and get the compiler to shut up, but I'm wondering if I'm doing something wrong here.
Each parameterization must be wildcarded:
List<? extends Container<?>> list = new LinkedList<Container<Double>>();
Edit: I was searching through Angelika Langer's FAQ for an explanation, but didn't find one (it's probably there, but hiding somewhere in the 500 pages).
The way to think about this is that each parameterization is independent, and the compiler will not infer information about the parameterization unless you explicitly say to do so.
Edit: I was searching through Angelika Langer's FAQ for an explanation, but didn't find one (it's probably there, but hiding somewhere in the 500 pages).
Thank you very much for the answer. I found an explanation in the FAQ, and after some squinting at it I think I get it.
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#What%20do%20multilevel%20wildcards%20mean?
Consider what would happen if you could.
List<Container<Double>> list1 = new LinkedList<Container<Double>>();
List<Container<?>> list2 = list1; // this shouldn't be allowed, because of the below
Container<String> foo = new Container<String>();
foo.add("hi");
list2.add(foo); // legal, because Container<String> is a subtype of Container<?>
Container<Double> bar = list1.get(0);
Double x = bar.get(0); // type error; it is actually a String object
// this indicates that type safety was violated
However, using List<? extends Container<?>> doesn't have this problem because you cannot put anything (except null) into a list of this type (because there is no type that is guaranteed to be a subtype of "? extends Container<?>").