Why should I have to specify <?> when using a generic in Java? - java

I'm quite new in Java, although I have much experience in C++ and other languages. So templates/generics are not something I don't know.
There's something that bothers me though, it is this <?> that I was told I should use everytime I use a generic instance of something when I don't know in advance of which specific type it will be:
Like:
List< MyGeneric > foo; // bad
List< MyGeneric<?> > bar; // good
IntelliJ doesn't barf on me when using the first expression, and I don't understand why it should. My coworkers have expressed that the 2nd expression was much better, but couldn't tell me exactly why.
I mean, what exactly is the difference between these two, apart from the second being explicit about the fact that it is a generic that we manipulate ?
The compiler certainly knows that it is a generic at compile time, so my guess is that the second expression is only better because it tells the programmer that he is manipulating a generic.
Am I right?
Edit: for clarification, I ovbiously use the most restrictive type, like List<MyGeneric<Double>>, whenever I know in advance what I am going to store in there. My question is for when I store unknown types of generics.

Every time? It's not applicable always, and it doesn't always make sense.
Let's describe what that actually is: <?> is an unbound wildcard, which immediately implies two things:
MyGeneric is a generic class, but
You do not know what type it's holding (and it likely doesn't matter).
It is preferable to the first expression in that the first expression always guarantees that you'll be working with a raw type, and you really don't want to use raw types. However, it is a gross overgeneralization to assume that using an unbound wildcard every time would be ideal.
If you actually know or care the type, or know or care about its bounds, use that instead.

Let's give an example of why it's bad to use the first. Assuming MyGeneric is defined like this:
class MyGeneric<T> {
private final T instance;
MyGeneric(T instance) { this.instance = instance; }
T get() { return instance; }
}
The following code would compile and run, but fail at runtime with a ClassCastException:
List<MyGeneric> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));
for (MyGeneric instance : list) {
Integer value = (Integer) instance.get(); // Compiles, but fails at runtime.
}
This compiles because you're using raw types: the compiler doesn't know that instance.get() can't return an Integer; it would merely warn you that it might be unsafe.
On the other hand, the following code would not even compile:
List<MyGeneric<String>> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));
for (MyGeneric<String> instance : list) {
Integer value = (Integer) instance.get(); // Won't compile, incompatible types.
}

The difference is that a raw type ignores the fact that the class is a generic, while the wildcard <?> specifies that the class is a generic but the type argument is unknown.
Raw means that you lose all compiler type-checking. Wildcard keeps type-checking intact.
Example:
public class MyGeneric<T> {
private T val;
public T get() {
return this.val;
}
public void set(T val) {
this.val = val;
}
}
MyGeneric a = new MyGeneric<Integer>();
a.set("Foo"); // accepted
Setting the value for a to a String when it was declared to be an Integer is accepted by the compiler, because a was defined raw, which means that the compiler is ignoring the fact that the class is a generic. When val is later used as an Integer, the program will crash. It's a bomb waiting to go off.
MyGeneric<?> b = new MyGeneric<Integer>();
b.set("Bar"); // compile error
Trying to set the value for b will not compile:
The method set(capture#1-of ?) in the type MyGeneric<capture#1-of ?> is not applicable for the arguments (String)
Here the compiler knows that the class is a generic and will not allow setting the value to anything (even an Integer), because it doesn't know what type would be allowed (wildcard = unknown, remember?). The compiler safeguards here, as it should.

List<?> means a list typed to an unknown type. This could be a List<A>, a List<B>, a List<String> etc.
Since the you do not know what type the List is typed to, you can only read from the collection, and you can only treat the objects read as being Object instances. Here is an example:
public void processElements(List<?> elements) {
for(Object o : elements){
System.out.println(o);
}
}
The processElements() method can now be called with any generic List as parameter. For instance a List<A>, a List<B>, List<C>, a List<String> etc. Here is a valid example:
List<A> listA = new ArrayList<A>();
processElements(listA);
Following tutorials will further help you to understand it:
https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
http://tutorials.jenkov.com/java-generics/wildcards.html

Related

Java Object return type vs. Generic Methods

I saw several questions about generic return type, but none answers my question.
If there is no bound for any of the arguments, such as the following method in JayWay :
public static <T> T read(String json, String jsonPath, Filter... filters) {
return new JsonReader().parse(json).read(jsonPath, filters);
}
What is the point of using this as generic ?
I told the guys from my team that this method should be used as :
JsonPath.<Boolean>read(currentRule, "$.logged")
instead of:
(boolean) JsonPath.read(currentRule, "$.logged")
But I really can't tell the difference...
Generics work by the compiler inserting invisible casts into your code.
For example, before generics were added to the language you'd have to do this.
List list = new ArrayList();
list.add("Foo");
list.add("Bar");
String str0 = (String) list.get(0);
String str1 = (String) list.get(1);
This was very annoying. Because get() returned Object, you had to cast every single time you wanted to get a String from the List.
Nowadays, List is generic, and get() returns T, so you can just do this.
List<String> list = new ArrayList<>();
list.add("Foo");
list.add("Bar");
String str0 = list.get(0);
String str1 = list.get(1);
What is happening here is that the compiler turns the new version into the old version by adding the casts for you, but they're still there.
However, the entire point of generics is that these compiler generated casts are guaranteed to be safe - i.e. they can't possibly throw a ClassCastException at runtime.
In my opinion, if you use generics to hide casts that are not guaranteed to be safe, just because they're annoying, it is an abuse of the feature.
Whether it's a generic method and you do
Boolean a = JsonPath.<Boolean>read(currentRule, "$.logged");
or it returns Object and you do
Boolean a = (Boolean) JsonPath.read(currentRule, "$.logged");
both versions could throw a ClassCastException at runtime, so I think it's better if you are forced to cast so that at least you are aware that you're doing something that could fail.
I consider it bad practice for the return type of a generic method to involve the type parameter T if the method parameters do not, unless the returned object cannot be used in a way that compromises type safety. For example,
public static <T> List<T> emptyList()
in Collections is ok (the list is empty so it can't contain an element of the wrong type).
In your case, I think the read method should not be generic and should just return Object.
The main reason that I would stay away from
JsonPath.<Boolean>read(currentRule, "$.logged")
is that it is internally performing an unchecked cast, and hiding this fact. For instance, you could invoke this method at the same place:
JsonPath.<String>read(currentRule, "$.logged")
and there is no way that you'd know there might be a problem there until it actually happens at runtime - it still compiles, and you don't even get a warning.
There is no getting away from the unchecked cast - I'd just rather have it right there in front of me in the code, so I know there is a potential danger; this allows me to take reasonable steps to mitigate the issue.
#SuppressWarnings("unchecked") // I know something might go wrong here!
boolean value = (boolean) JsonPath.read(currentRule, "$.logged")
Having a type-parameter that has never been set (when calling JsonPath.read(currentRule, "$.logged")), actually makes the compiler completely ignore all the generic information within the method and replace all the type-parameter with:
Object, if the type-parameter doesn't have an upper-bound. (like in your case)
U, if the type-parameter is bounded like <T extends U>. For example, if you have a <T extends Number> as a type-parameter and ignore it by calling JsonPath.read(...), then the compiler will replace the type-parameter with Number.
In the case with the cast ((boolean) JsonPath.read(...)), the type-parameter is replaced with Object. Then, this type is unsafely transformated to boolean, by first returning a Boolean (probably), and then auto-unboxing this wrapper to boolean. This is not safe, at all. Actually, every cast is not safe - pretty much you tell the compiler: "I know what this type will be at Runtime, so please believe me, and let me cast it to something else that's compatible with it.". Your humble servant, the compiler, allows that, but that's not safe, if you're wrong. :)
There's another thing with your method, also. The type-parameter is never used within the method body or parameters - this makes it pretty redundant. Since by doing a cast to boolean you insist that you know the return type of new JsonReader().parse(json).read(jsonPath, filters);, then you should just make the return type boolean (or Boolean):
public static Boolean read(String json, String jsonPath, Filter... filters) {
return new JsonReader().parse(json).read(jsonPath, filters);
}
There is nothing functionally different between the two. The byte-code will probably be identical.
The core difference is that one uses a cast while the other uses generics.
I would generally try to avoid casting if there is any alternative mechanism and as the generic form is a perfectly effective alternative I would go for that.
// The right way.
JsonPath.<Boolean>read(currentRule, "$.logged");

Why Java Cannot Create Instances of Type Parameters

I know Java does not allow not Create Instances of Type Parameters. Many articles simply said "Type Erase" as the reason. But does type parameters initialization not occur before type erase? Is Type Erase the only reason? Here is a example:
public class GenObj {
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
public static main(){
List<String> list= new ArrayList<String>();
GenOjb.append<String>(list);
}
}
When we call the generic method using GenOjb.append(list), I think the compiler will replace E in the method with String first and then do "Type Erase", is that correct? If so, as long as we have a way to ensure E does indeed have a default constructor, we should be able to create instance of type parameters. Can someone explain in more detail why Java does not allow creating instance of parameter type? Thanks.
It is instructive to ask: How would you do it without Generics?
Every program with Generics can be converted into an equivalent program without Generics by simply removing generic parameters and inserting casts in appropriate places. This is called type erasure. So, if you want to know if you can do it with Generics, you need to first ask if you can do it without Generics.
Without Generics, your program looks like this:
public class GenObj {
public static void append(List list) {
Object elem = // what goes here?
list.add(elem);
}
public static main(){
List list= new ArrayList();
GenOjb.append(list);
}
}
Due to runtime type erasure, the type is not available to do anything with.
You can however pass a type token:
public static <E> void append(List<E> list, Class<E> c) {
E elem = c.newInstance();
list.add(elem);
}
This presupposes that the class has a no-args constructor.
Remember that Generics are for compile time checking and that when you compile a class within .java file, Java produces a single .class file for that class.
When we call the generic method using GenOjb.append(list); I think
compiler will replace E in the method with String first and then do
"Type Erase", is that correct?
No, nothing is replaced in the method. Once the .class file is generated, that's what you get. When compiling, the compiler simply verifies that the String type is an acceptable type argument for the append() method. Since you've specified no bounds on E, the compiler judges that String is an acceptable type argument.
Can someone explain in more details why java not allow creating
instance of parameter type?
That's just not how the java language works. Class instantiation happens at run time. At run time, there is no longer any notion of type variables because of type erasure and therefore we cannot know what E is.
There are a few alternatives for getting an instance of whatever type T ends up being. See here:
Instantiating a generic class in Java
Instantiating generics type in java
Create instance of generic type in Java?
Because under the covers, a List<E> is just a List. The actual type that has been substituted for E is not passed on the stack. Therefore, when the JVM is running this code, there is no way to tell what type E is, so it has no way of knowing what class to instantiate.

Java send parameterized generic type as valueType

I have a method that returns a type that it is given:
T foo(Class<T> valueType) {...};
String s = foo(Java.lang.String.class);
I'm trying to send a generic type as my class, but getting compiler errors. For example, let's say I want to return an ArrayList of Strings:
ArrayList<String> list = foot(ArrayList<String>.class)
the parameter "String" is giving errors. Is there anyway I can specify the generic type to return?
The runtime class field value does not depend on the generic type you apply, and this syntax is illegal. More on this here.
Would this work for you(edited)?
ArrayList list = foot(ArrayList.class)
Just cast the input or output of the method:
ArrayList<String> list = foot((Class<ArrayList<String>>)(Class<?>)ArrayList.class);
or
ArrayList<String> list = (ArrayList<String>)foot(ArrayList.class);
Basically, the Class class does not work well with generics, because it represents a runtime thing, whereas generics works at compile time. At runtime, there is just one class object for ArrayList in the program. Even if you call it Class<ArrayList<String>> or Class<ArrayList<Integer>> or Class<ArrayList>, it's still the same single object. So what type should it be? There's no simple answer that works for everything.
The Java language decided that the class literal ArrayList.class should just have the type Class<ArrayList>. But that causes problems when you want an expression of type Class<ArrayList<String>>, so you have to bend the types.
Type Erasure rears its ugly head. Essentially, Java does compile-time checking on the generic <…> clauses, then erases them from the run-time objects, losing the information forever. Nasty, eh? Since .class metaobjects are referenced at run-time, they suffer from the erasure as well.
ArrayList<String>.class is Erased at compile-time, to be ArrayList.class.
If you want type-safety with the generics, you'll have to pass in two parameters, e.g.
ArrayList<String> foo = foot(ArrayList.class, String.class)
Assuming you are here only concerned about static types, this trick might be useful:
#SuppressWarnings("unchecked")
public static <T>
Class<T> appropriateClass(T... args) {
return (Class<T>) args.getClass().getComponentType();
}
Class<ArrayList<String>> c = appropriateClass();
ArrayList<String> list = foo(c);
Actually I am not very proud of the trick. But you might want to give it a try.
If you test it:
System.out.println(c); // prints "class java.util.ArrayList"
So it is equivalent to an ugly cast like (Class<ArrayList<String>>) (Class<?>) ArrayList.class.
If you actually need to have different tokens for ArrayList<String> and ArrayList at runtime, you should use the TypeToken technique as mentioned by Sean Patrick Floyd.

Can we create an Array of Generic class? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Java how to: Generic Array creation
I want to create a Stack which contains 10 elements, I'm trying to do this with Generics.
I started by creating a Stack class of Type T, but I'm getting compilation error when I try to instantiate the array of generic type.
public class Stack<T>{
private T[] stk = new T[10];
}
What I'm doing wrong here?
You can't do this. Not without using some hacky round about code, and even then you have to do an unsafe cast in the end that completely ruins the entire reason for trying to be type safe in the first place.
This is because Java has something called type erasure, the compiler throws away all the generic information and the runtime doesn't know what T is.
Here is the preferred way of doing this:
#SuppressWarnings("unchecked")
static <T> T[] newArray(Class<T> type, int length)
{
return (T[]) java.lang.reflect.Array.newInstance(type, length);
}
This doesn't create the throw about list instance in the naive solution of calling toArray() on a List<T> implementation hack.
For more discussion on this see
Here is an answer to a similar question about creating a type safe array at runtime.
There reason you can't create a Array of a generic type is this:
When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.
For instance, Box is translated to type Box, which is called the raw type — a raw type is a generic class or interface name without any type arguments. This means that you can't find out what type of Object a generic class is using at runtime. The following operations are not possible:
public class MyClass<E> {
public static void myMethod(Object item) {
if (item instanceof E) { //Compiler error
...
}
E item2 = new E(); //Compiler error
E[] iArray = new E[10]; //Compiler error
E obj = (E)new Object(); //Unchecked cast warning
}
}
http://download.oracle.com/javase/tutorial/java/generics/erasure.html
The reason to use generics is that you don't want to typecast.
That said, here are two solutions.
You can use a array of Objects and garantee you'll only push and pop T's by the methods you supply to access the array:
public class MyStack2<T> {
Object[] myStack2;
public MyStack2() {
myStack2 = new Object[10];
}
public void push(T t) {
// do whatever you wanna do to push t, like myStack2[x] = t;
}
public T pop() {
// do whatever you wanna do to pop t like return (T)myStack2[0];
// Here we typecasted, but we can be sure that myStack2 contains only T's
// because we garanteed it at the push method.
}
}
OR
You can use another thing other than array to store your stack.
public class MyStack<T> {
Stack<T> myStack;
public MyStack() {
myStack = new Stack<T>();
}
public void push(T t) {
myStack.push(t);
}
public T pop() {
return myStack.pop();
}
}
As you can see, java already provides a Stack class so you don't have to write one, but if you really want to do it, maybe to understand how it works, you can replace the Stack class in this example by a List. With a List you'll be able to play almost the same you'd do if you were using an array.
The reason that that doesn't work is that when you write a type like String[] in Java, that means that the object knows itself at runtime that its component type is String (or a subclass thereof). Generic type parameters are not available at runtime, hence you cannot do it. We wish there was something like Object[]<T>, i.e. "an array that does not know its component type at runtime, but which has generic type checks at compile time", but such a thing does not exist in the language.
However, it appears that from your code that you intend to use this array internally, and you don't really care if the array knows T at runtime; you just need an array, period. If you don't intend to pass the array out of the class, then there are two options:
Use a variable of type Object[], and create new Object[10]. This requires you to cast to T if you want to return an element from a method, which is an unchecked cast.
Use a variable of type T[], and assign to it using = (T[])new Object[10]. Now people will point out that the subtyping relationship is not technically true, but so long as it's inside the class (inside the scope of T), it doesn't matter, because T is erased. So with this method you have to be extra careful never to pass or return the array reference, because you won't be warned that it's unsafe.
Both methods are the same after type erasure, and both methods will cause unchecked cast warnings, so it's really your personal preference. The second one is more convenient (you can pretend it's a T[] and get things out of it without cast), while the first one is more formally correct and safer (the second requires you to be diligent not to pass it out).

Why is List<Number> not a sub-type of List<Object>?

public void wahey(List<Object> list) {}
wahey(new LinkedList<Number>());
The call to the method will not type-check. I can't even cast the parameter as follows:
wahey((List<Object>) new LinkedList<Number>());
From my research, I have gathered that the reason for not allowing this is type-safety. If we were allowed to do the above, then we could have the following:
List<Double> ld;
wahey(ld);
Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object> reference). Now, after the method call, ld refers to a list with a type List<Double>, but the actual list contains some String objects!
This seems different to the normal way Java works without generics. For instance:
Object o;
Double d;
String s;
o = s;
d = (Double) o;
What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile.
This leads me to believe this is purely a design decision with regards to the type restrictions on generics. I was hoping to get some comments on this decision?
What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. The equivalent with generics would be:
Object o;
List<Double> d;
String s;
o = s;
d.add((Double) o);
Which behaves the same way (compiles, but fails at runtime). The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. For example:
public void Foo(List<Object> list, Object obj) {
list.add(obj);
}
This looks perfectly fine and type-safe until you call it like this:
List<Double> list_d;
String s;
Foo(list_d, s);
Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters.
So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. That's bad, because it's hidden and therefore hard to avoid and harder to debug.
Consider if it was...
List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. Thus, it is not allowed.
They aren't subtypes of each other due how generics work. What you want is to declare your function like this:
public void wahey(List<?> list) {}
Then it will accept a List of anything that extends Object. You can also do:
public void wahey(List<? extends Number> list) {}
This will let you take in Lists of something that's a subclass of Number.
I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler.
There are essentially two dimensions of abstraction here, the list abstraction and the abstraction of its contents. It's perfectly fine to vary along the list abstraction - to say, for instance, that it's a LinkedList or an ArrayList - but it's not fine to further restrict the contents, to say: This (list which holds objects) is a (linked list which holds only numbers). Because any reference that knows it as a (list which holds objects) understands, by the contract of its type, that it can hold any object.
This is quite different from what you have done in the non-generics example code, where you've said: treat this String as if it were a Double. You are instead trying to say: treat this (list which holds only numbers) as a (list which holds anything). And it doesn't, and the compiler can detect it, so it doesn't let you get away with it.
"What we are doing here is essentially
the same thing, except this will pass
compile-time checks and only fail at
run-time. The version with Lists won't
compile."
What you're observing makes perfect sense when you consider that the main purpose of Java generics is to get type incompatibilities to fail at compile time instead of run time.
From java.sun.com
Generics provides a way for you to
communicate the type of a collection
to the compiler, so that it can be
checked. Once the compiler knows the
element type of the collection, the
compiler can check that you have used
the collection consistently and can
insert the correct casts on values
being taken out of the collection.
In Java, List<S> is not a subtype of List<T> when S is a subtype of T. This rule provides type safety.
Let's say we allow a List<String> to be a subtype of List<Object>. Consider the following example:
public void foo(List<Object> objects) {
objects.add(new Integer(42));
}
List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);
So, forcing this provides type safety but it also has a drawback, it's less flexible. Consider the following example:
class MyCollection<T> {
public void addAll(Collection<T> otherCollection) {
...
}
}
Here you have a collection of T's, you want to add all items from another collection. You can't call this method with a Collection<S> for an S subtype of T. Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection.
To fix this, Java provides what they call "wildcards". Wildcards are a way of providing covariance/contravariance. Now consider the following using wildcards:
class MyCollection<T> {
// Now we allow all types S that are a subtype of T
public void addAll(Collection<? extends T> otherCollection) {
...
otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
}
}
Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). This way we get flexibility and type safety.

Categories