Java Error: New Generic TreeNode Array - java

I have generic class of TreeNode:
public class TreeNode<E> {
public E key;
public int num_of_children;
public TreeNode<E> [] children;
public TreeNode(int num_of_children)
{
this.num_of_children = num_of_children;
children = new TreeNode[num_of_children];// Why not: new TreeNode<E>[num_of_children]?
}
public TreeNode<E> clone()
{
TreeNode<E> node = new TreeNode<E>(num_of_children);
return node;
}
}
When I try to do:children = new TreeNode<E> [num_of_children];
I get error. But "new TreeNode[num_of_children]" works.
I read about type erasure, and I don't understand why TreeNode<E>[] doesn't work.
Why is that? Please enlighten me!

Things like new TreeNode<String>[] and new TreeNode<E>[] are disallowed by Java. The only things you can do are new TreeNode[] and new TreeNode<?>[] (unbounded wildcard parameter).
The reason for this is a little complicated, but instructive. Arrays in Java know their component type at runtime, and every time you put something in, it checks to see if it's an instance of the component type, and if not, throws an exception (this is related to how array types are covariant and therefore inherently unsafe at compile time).
Object[] foo = new Integer[5];
foo[2] = "bar"; // compiles fine, but throws ArrayStoreException at runtime
Now add generics. The problem with a generic component type is that, there is no way for you to check if an object is an instance of say, TreeNode<Integer> at runtime (as opposed to TreeNode<String>), since generics are erased from runtime types. It can only check TreeNode, but not the component type. But programmers might have expected this checking and exception throwing behavior from arrays, since it normally works. So to avoid this surprise failure, Java disallows it. (In most code, you won't run into this problem anyway because you won't be mixing objects of the same type but different type parameters. But it is theoretically possible to come up.)
Of course, you can simply work around the problem by creating an array of raw or wildcard parameter type, and then casting to the proper type, e.g. (TreeNode<Integer>)new TreeNode[5]. What's the difference? Well, that's an unchecked cast, which generates a warning, and you, the programmer, takes responsibility for all the unsafe things that might happen later. If it does something unexpected, the compiler can say, "we told ya so!".

Because the Java Language Specification writes:
An array creation expression creates an object that is a new array whose elements are of the type specified by the PrimitiveType or ClassOrInterfaceType.
It is a compile-time error if the ClassOrInterfaceType does not denote a reifiable type (§4.7). Otherwise, the ClassOrInterfaceType may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type (§9).
The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.
It is not clear to me why they require this. Certainly, the component type of the array must be available at runtime, and it would be misleading for the programmer if it were different from the type specified in the source code. Consider:
E[] a = new E[10];
Here, it would be bad if the compiler used the erasure of E as the array component type, as the programmer might well depend upon the array to check that nothing but instances of E is stored in it.
It's less clear what harm would come from allowing:
List<E>[] lists = new List<E>[10];
The only thing that comes to mind is that assigning an array element would amount to an unchecked cast, because the array would check the element is a List, but not that it is a List<E>, and thus fail to throw an ArrayStoreException.
In practice, you can safely suppress this warning as long as you remain aware that the array will not check the type parameters of its component type.

Related

Why should I have to specify <?> when using a generic in 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

Java reflection get runtime type when using generics

I am wondering how can I get the runtime type which is written by the programmer when using generics. For example if I have class Main<T extends List<String>> and the programmer write something like
Main<ArrayList<String>> main = new Main<>();
how can I understand using reflection which class extending List<String> is used?
I'm just curious how can I achieve that. With
main.getClass().getTypeParameters()[0].getBounds[]
I only can understand the bounding class (not the runtime class).
As the comments above point out, due to type erasure you can't do this. But in the comments, the follow up question was:
I know that the generics are removed after compilation, but I am wondering how then ClassCastException is thrown runtime ? Sorry, if this is a stupid question, but how it knows to throws this exception if there isn't any information about classes.
The answer is that, although the type parameter is erased from the type, it still remains in the bytecode.
Essentially, the compiler transforms this:
List<String> list = new ArrayList<>();
list.add("foo");
String value = list.get(0);
into this:
List list = new ArrayList();
list.add("foo");
String value = (String) list.get(0); // note the cast!
This means that the type String is no longer associated with the type ArrayList in the bytecode, but it still appears (in the form of a class cast instruction). If at runtime the type is different you'll get a ClassCastException.
This also explains why you can get away with things like this:
// The next line should raise a warning about raw types
// if compiled with Java 1.5 or newer
List rawList = new ArrayList();
// Since this is a raw List, it can hold any object.
// Let's stick a number in there.
rawList.add(new Integer(42));
// This is an unchecked conversion. Not always wrong, but always risky.
List<String> stringList = rawList;
// You'd think this would be an error. But it isn't!
Object value = stringList.get(0);
And indeed if you try it, you'll find that you can safely pull the 42 value back out as an Object and not have any errors at all. The reason for this is that the compiler doesn't insert the cast to String here -- it just inserts a cast to Object (since the left-hand side type is just Object) and the cast from Integer to Object succeeds, as it should.
Anyhow, this is just a bit of a long-winded way of explaining that type erasure doesn't erase all references to the given type, only the type parameter itself.
And in fact, as a now-deleted answer here mentioned, you can exploit this "vestigial" type information, through a technique called Gafter's Gadget, which you can access using the getActualTypeArguments() method on ParameterizedType.
The way the gadget works is by creating an empty subclass of a parameterized type, e.g. new TypeToken<String>() {}. Since the anonymous class here is a subclass of a concrete type (there is no type parameter T here, it's been replaced by a real type, String) methods on the type have to be able to return the real type (in this case String). And using reflection you can discover that type: in this case, getActualTypeParameters()[0] would return String.class.
Gafter's Gadget can be extended to arbitrarily complex parameterized types, and is actually often used by frameworks that do a lot of work with reflection and generics. For example, the Google Guice dependency injection framework has a type called TypeLiteral that serves exactly this purpose.

Java Class Cast Exception when creating Generic Array

I am trying to write a PriorityQueue which must be genericized to a Comparable. Here is the constructor:
public class DavidiArrayPriorityQueue <E extends Comparable<E>> implements PriorityQueue<E> {
private E data[];
private int numElements;
//creates an empty priority queue with 10 spaces by default
public DavidiArrayPriorityQueue(){
data= (E[]) new Object[20];
numElements=0;
}
When I initialize it with
DavidiArrayPriorityQueue<Integer> test=new DavidiArrayPriorityQueue<Integer>();
It throws [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
The element-type of an array is actually part of the array, known at runtime. So when you write new Object[], you are creating an array with element-type Object, and even if your intent is that the elements of the array will all always have type (say) Comparable, you still can't cast it to Comparable[].
In your case, you're casting it to E[]. Due to erasure, the cast can't be fully enforced at runtime, so it's downgraded to a cast to Comparable[]; so, technically speaking, you could trick the compiler into allowing this, by writing (E[]) new Comparable[]. But that's a bad idea, because then you have an array expression of type E[] whose element-type is not actually E. You've circumvented the type system, and this can cause confusing errors later on.
It's better to just have data be of type Object[] (or perhaps Comparable<?>[]), and perform the necessary casts to E. This will result in compiler warnings, because the compiler won't be able to check those casts, either, but at least you can verify that your code is correct and correctly preserves the type system (and then suppress the warnings, with a comment).

Java Generics: Instantiating arrays with parameterized types: illegal?

I must be confused here.
I read everywhere that in generics arrays of parametrized types are illegal.
Example from AngelikaLanger:
static void test() {
Pair<Integer,Integer>[] intPairArr = new Pair<Integer,Integer>[10] ; // error
addElements(intPairArr);
Pair<Integer,Integer> pair = intPairArr[1];
Integer i = pair.getFirst();
pair.setSecond(i);
}
Quote from Langer (but everywhere else I read it says the same thing):
The compiler prohibits creation of arrays whose component type is a
concrete parameterized type, like Pair in our
example. We discussed in the preceding entry why is it reasonable
that the compiler qualifies a Pair[] as illegal.
So far ok.
But in my code here:
private MyEntry<E> [] elements = (MyEntry<E>[])new Object[capacity];
I do exactly that, it compiles fine (I use eclipse) but get a class cast exception error (Object can not be cast to MyEntry):
My question is, why does this line compiles in the first place?
I thought that this instantiation is disallowed by the compiler.
What I am doing wrong/differerent here?
UPDATE:
On the same page, why am I able to succesfully do:
List<E> elements[] = (List<E>[])new LinkedList[capacity];
and have no runtime exceptions?
UPDATE:
Everywhere I have read (mentioned Langer since she's quoted often) it says that this declaration (arrays of parametrized types) is disallowed by compiler.
I can understand what happens after that.
I can't understand why the compiler doesn't report an error.
I am not judging, I am saying everywhere I read, it says this does not compile.
Am I missreading something?
UPDATE:
I saw some comments related to the missing parameter in the new part.
This also has no issue:
List<Entry<KeyType, ValueType>> table[] = (List<Entry<KeyType, ValueType>>[])new LinkedList[capacity];
In your first example, there's no problem with the instantiation - here's exactly what you're creating:
new Object[capacity]
Perfectly legal. You do however get a runtime exception when you attempt to cast, because an array of Object is not an array of MyEntry<E>. You might have a point that the cast or declaration could be rejected by the compiler, if these generically-parameterised arrays can't exist, though this depends what order erasure kicks in. In any case, the instantiation itself is fine.
In the second example, you're creating a non-generic array of LinkedList. You then assign it to a genericised reference, which at runtime will have been erased to just a List[]. This works fine (because rightly or wrongly, arrays are covariant).
I'm not sure why you were expecting a runtime exception; it's not much different to calling, say
List<E> = new LinkedList();
You would get some unchecked warnings, but nothing that would stop the code compiling or running.
You have completely misunderstood whatever you have read. There is absolutely nothing wrong with having the type that is an array of a parameterized type: MyEntry<E>[] or HashMap<String,Integer>[][] or whatever. You can have variables of such types all you want, and use them anywhere a type can be used.
However, with array creation, you cannot do something like new MyEntry<E>[...]. It is not allowed by the language (for type safety reasons we will not go into here), so it is a compile error.
The best solution is either new MyEntry[] (array of raw type) or new MyEntry<?>[] (array of wildcard type); either one is allowed by the language. Both of them will require you to do an explicit cast back to MyEntry<E>[].
Since you ask about your code examples, your first example is syntactically correct (there is nothing wrong with new Object[...], and it is syntactically okay to cast to MyEntry<E>[]), so there is no compile error. However, the runtime check of the cast fails at runtime, because the object's actual type Object[] is not a subtype of MyEntry[].
The second code example is also syntactically correct, and plus the runtime check of the cast succeeds (LinkedList[] is a subtype of List[]).
Because LinkedList is an instance of List.
But Object is NOT an instance of MyEntry.
Also compiler don't check can one object be cast to another or not. Because it is runtime operation.
You should use:
private MyEntry<E> [] elements = new MyEntry [capacity];
Or:
class SomeOtherEntry extends MyEntry {}
private MyEntry<E> [] elements = new SomeOtherEntry [capacity];
But not:
class SomeOtherEntry extends MyEntry {}
private SomeOtherEntry <E> [] elements = new MyEntry [capacity];
UPDATE:
List<Entry<KeyType, ValueType>> [] table = (List<Entry<KeyType,ValueType>> []) new Linked[capacity];
Built-in Java List classes actually use a work-around whenever you use the
<T> T[] toArray(T[] a) method. If we take a closer look at the code, if you supply an array that's smaller than required, the method actually creates a new array of the type parameter.
Let's see the code: https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/LinkedList.java#L1085
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
...
}

no exception when adding false type in list

i found a (for my state of knowledge) strange behavior during the adding of a Class type to a list.
I have a list which holds all implementing classes of an Abstract class List<Class<MyAbstractClass>> myImplementations. I added a type of a non-derived class and there was no error. Can anyone explain why i can do something like myImplementations.add(SomeOtherClass.class); without any exception? It seems that the second generic type (MyAbstractClass) has no effect at all.
--- edit ---
public abstract class MyAbstractClass{
public static String getMyIdentification(){ throw new RuntimeException("implement method");}
}
public class MyImplementation extends MyAbstractClass{
public static String getMyIdentification(){ return "SomeUUID";}
}
public class OtherClass{}
// in another class:
List<Class<MyAbstractClass>> myImplementations = new ArrayList<Class<MyAbstractClass>>();
myImplementations.add(MyImplementation.class); // does not cause any error
myImplementations.add(OtherClass.class); // does not cause any error, but should in my opinion??
---- edit end ---
Thank you,
el
The type is erased during compilation, so you won't see any exception at runtime. The compiler should complain in your case or give a warning.
List<Class<String>> list = new ArrayList<Class<String>>();
list.add(Integer.class); // should create a compiletime error
list.add(Class.forName("java.lang.Integer")); // should create a warning
// and run due to type erasure
The type parameter Class<String> is erased during compilation - it is only used by the compiler to check if the java source code is valid. The compiled bytecode doesn't contain this information anymore, on byte code level the list will hold and accept Object or any subclass of Object. And because Integer.class is a subclass of Object, the code will run - until the runtime throws ClassCastExceptions at the programmer, just because it expected Class<String> instances.
This behaves as expected, using eclipse compiler:
List<Class<? extends CharSequence>> myImplementations =
new ArrayList<Class<? extends CharSequence>>();
myImplementations.add(String.class);
myImplementations.add(Vector.class);
i.e. the compiler complains only for the second add. If it passes compilation, however, the list is transformed into a raw list, and you won't get an exception until you get elements out of the list - using the foreach loop, for example.
without the ? extends the compilation fails even for String. And that's how it should be. I'm surprised that you don't have any errors, since java generics are invariant - i.e. you cannot add a Subclass instance to a List<Superclass>.
On type erasure
Java's generics are non-reified. A List<String> and a List<Integer> are different types at compile time, but both types are erased to simply List at run-time. This means that by circumventing the compile-time checks, you can insert an Integer into List<String> at run-time, which by itself may not generate ClassCastException. Here's an example:
List<String> names = new ArrayList<String>();
List raw = names; // generates compiler warning about raw type!
raw.add((Integer) 42); // does not throw ClassCastException! (yet!)
// but here comes trouble!
for (String s : names) {
// Exception in thread "main" java.lang.ClassCastException:
// java.lang.Integer cannot be cast to java.lang.String
}
Note that you'd have to deliberately circumvent the compile-time check to violate the generic type invariant: the compiler will do its best to ensure that List<String> will indeed contain only String, and will generate as many warnings and errors as necessary to enforce this.
On checked collections
Sometimes we want to enforce the type safety at run-time. For most scenarios, the checked collection wrappers from java.util.Collections can facilitate this behavior. From the documentation:
<E> Collection<E> checkedCollection(Collection<E> c, Class<E> type)
Returns a dynamically typesafe view of the specified collection. Any attempt to insert an element of the wrong type will result in an immediate ClassCastException. Assuming a collection contains no incorrectly typed elements prior to the time a dynamically typesafe view is generated, and that all subsequent access to the collection takes place through the view, it is guaranteed that the collection cannot contain an incorrectly typed element.
The generics mechanism in the language provides compile-time (static) type checking, but it is possible to defeat this mechanism with unchecked casts. Usually this is not a problem, as the compiler issues warnings on all such unchecked operations. There are, however, times when static type checking alone is not sufficient. For example, suppose a collection is passed to a third-party library and it is imperative that the library code not corrupt the collection by inserting an element of the wrong type.
Another use of dynamically typesafe views is debugging. Suppose a program fails with a ClassCastException, indicating that an incorrectly typed element was put into a parameterized collection. Unfortunately, the exception can occur at any time after the erroneous element is inserted, so it typically provides little or no information as to the real source of the problem. If the problem is reproducible, one can quickly determine its source by temporarily modifying the program to wrap the collection with a dynamically typesafe view. For example, this declaration:
Collection<String> c = new HashSet<String>();
may be replaced temporarily by this one:
Collection<String> c = Collections.checkedCollection(
new HashSet<String>(), String.class);
Running the program again will cause it to fail at the point where an incorrectly typed element is inserted into the collection, clearly identifying the source of the problem.
Here's a modification of the previous snippet:
List<String> names = Collections.checkedList(
new ArrayList<String>(), String.class
);
List raw = names; // generates compiler warning about raw type!
raw.add((Integer) 42); // throws ClassCastException!
// Attempt to insert class java.lang.Integer element into collection
// with element type class java.lang.String
Note that as a "bonus", a Collections.checkedList will throw NullPointerException at run-time on an attempt to add(null).
On Class.isAssignableFrom
Unfortunately the behavior we want in this scenario is not supported by a Collections.checkedList: you can use it to ensure that only java.lang.Class instances can be added at run-time, but it will not ensure that the given Class object is a subclass of another Class.
Fortunately the Class.isAssignableFrom(Class) method allows you to make this check, but you'd have to write your own checked List wrapper to enforce this. The idea is illustrated here in a static helper method instead of a full List implementation:
static void add(List<Class<?>> list, Class<?> base, Class<?> child) {
if (base.isAssignableFrom(child)) {
list.add(child);
} else {
throw new IllegalArgumentException(
String.format("%s is not assignable from %s",
base.getName(),
child.getName()
)
);
}
}
Now we have:
List<Class<?>> list = new ArrayList<Class<?>>();
add(list, CharSequence.class, String.class); // OK!
add(list, CharSequence.class, StringBuffer.class); // OK!
add(list, CharSequence.class, StringBuilder.class); // OK!
add(list, CharSequence.class, Integer.class); // NOT OK!
// throws IllegalArgumentException:
// java.lang.CharSequence is not assignable from java.lang.Integer

Categories