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).
Related
I can't seem to create an array of class instances that have a field of a generic.
Like so:
class Main {
public static void main(String args[]) {
Foo<String> foo = new Foo<>();
}
}
class Foo<K> {
public Bar[] arr;
Foo() {
arr = (Bar[]) new Object[10];
}
void push(int index, K value) {
arr[index].value = value;
}
class Bar {
K value;
}
}
REPL
Which gives me
Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [LFoo$Bar; ([Ljava.lang.Object
But I do know for a fact that an array of generics, like so
T[] arr = (T[]) new Object[10];
can be created.
But I do know for a fact that an array of generics, like so
T[] arr = (T[]) new Object[10];
can be created.
Nope. This is incorrect, and that is the root of your confusion.
You can't create 'generic arrays'. Generics are a figment of the compiler's imagination. The JVM itself doesn't have a clue as to what they are. Most generics information poofs out of existence during the compilation step; the few generics that survive (in signatures), are treated as comments by the JVM: The JVM does not know what it means and doesn't care.
T[] arr = (T[]) new Object[10];
is a fancy way of telling the compiler not to complain, and to inject a few casts here and there. That is all that line does.
You've created a new object array. It's not a T[] array. The cast operator does not convert anything. It merely asserts types (is this thing indeed of that type? If yes, great, do nothing. If no, throw an exception). Given that this is by definition a runtime thing (if the compiler knows some expression is of some type, the cast obviously isn't needed), and generics fundamentally do not exist at runtime - then this cast operation truly does nothing. There's nothing to check. It cannot fail and has no bytecode. It is there just to tell the compiler that you take responsibility, as the compiler can no longer ascertain type safety for you.
What you've done is made an array of objects, and assigned it to a variable of type T[]. Local variables don't even exist in class files, they are 'compiled out', so to speak. Hence, you most definitely did not just 'make' a new 'array of T', in any sense you care to take that sentence.
So, having covered that:
Collections of any stripe (be they java.util.List or an array) are invariant. invariant is a concept in typing systems. You are presumably more used to covariant type systems: Java's basic type system is covariant. Covariant means: A subtype of a thing is just as good. In other words, this:
Object o = "hello";
is fine, because String is a subtype of Object, and is covariant - subtypes are fine. This isn't simply 'because the java spec says so' - it makes a more fundamental sense.
But in (writable) collections it just breaks down. Imagine that the 'component type' of a collection was covariant, then I could do this:
Integer[] integers = new Integer[10];
Number[] numbers = integers;
numbers[0] = 5.5;
System.out.println(integers[0]); // hmmmmm!!!
Go through those lines step by step - they explain why covariance is wrong. Hence, for the sake of collection types, java's typing system is invariant. You cannot assign an Integer[] to a Number[]. Unfortunately, 30 years ago when the initial java spec that introduced arrays was written, this wasn't thought through all that far and arrays have turned to be really really weird constructs as a consequence: Their toString, equals and hashCode implementations are well defined, but the definition is: These methods are effectively completely useless, and they can't grow or shrink.
collections do a much better job at it - you can't assign a List<Integer> list to a variable of type List<Number> either, but unlike with arrays, you can ask java for covariance and even contravariance: List<? extends Number> gives you covariance and the compiler acts accordingly - for example, you can't add anything to a List<? extends Number> because there's no way to know what you could possibly add - perhaps that variable is pointing at a List<Integer>, perhaps it is pointing at a List<Double>, and no value is both a Double and Integer simultaneously (except trivially and not useful, but for completeness: null, literally - which actually works; you can call list.add(null) if list's type is List<? extends Number> - it is the only thing you can add).
One of those weird things about arrays is that any array can be assigned / is type-compatible with Object[], even though this is wrong. It's a throwback in order to allow working with objects (what you really need is a ?[] - just like you can have a List<?>, but that syntax did not exist at the time).
Arrays, unlike generics, are not a figment of the compiler's imagination: The runtime actually knows about them, tracks them, etcetera. You can e.g. do this:
Object[] o = new String[10]; // weird, but legal java.
o[0] = 5; // compiles, but throws an ArrayStoreException at runtime.
o.getClass().getComponentType(); // returns 'String.class'
and note how generics doesn't work like this at all:
List<Object> o = new ArrayList<String>(); // does not compile.
// .. but for funsies let's force the issue:
List<String> strings = new ArrayList<String>();
List /* raw */ hack = strings;
List<Object> objects = hack; // compiles with warnings.
hack.add(5.0); // perfectly fine, compiles and runs without error.
String y = strings.get(0); // compiles perfectly fine.... but throws ClassCastException at runtime.
objects.get????? // there is no way to get 'String.class' from this thing. At all.
In other words, what you fundamentally want to do (treat an array of Dogs as an array of Animals) doesn't work - not because of java, but because of the universe: You can add parrots to an array of animal, hence why you can't treat an array of Dogs as an array of Animals. A java specific hacky thing with arrays is that you CAN treat an array of anything as an array of Object specifically (and only Object[], that is hardcoded in the spec), which is wrong and leads to all sorts of broken code, but it's in the language solely to give you an option to work with arrays as a generalized concept, because java 1.0 through 1.4 didn't have generics.
TL;DR: Do not use arrays. They are weird and mostly useless. Make List<T>s instead.
The exception caused by this line :
arr = (Bar[]) new Object[10];
To simplify the explanation i just want to replace your code with this example :
public class Main {
public static void main(String args[]) {
Bar b = (Bar) new Object();
}
}
class Bar{
}
This line Bar b = (Bar) new Object(); compile because Bar extends Object , an this inheritence relation let your code compile, but at RunTime Java find that you are trying to cast an instance of Object class ( created using new Object()) ,and that's impossible because Object is a superType of Bar .
You did the same thing just you added the [ ].
With generics , any T class must extends (directly or indirectly) Object class , T can be Object himSelf , so the code compile and can throw a ClassCastException .
When I write generic class in Java, I use below code
public ResizableArray() {
this.count=0;
this.elements = (T[]) new Object[0];
}
However I get a warning:
Type Safety: Unchecked Cast from Object[] to T[]
Why do I get this warning?
You're creating an Object[] (that is, an array of instances of class "Object", so basically an array of anything). You're casting it into an T[] (that is an array that can ONLY contain instances of class T). You don't test if the objects in the array (I know there are none, but the compiler doesn't) actually IS a T - if it isn't, the cast will throw an ClassCastException at runtime. That's why you get the warning.
If T is anything else but Object, you are basically lying with that downcast. This is because the type of Java arrays, unlike generic types, is reified. In other words,
boolean isStringArray(Object[] os) { return os instanceof String[]; }
called with
System.out.println(isStringArray(new Object[]));
actually prints false: the runtime knows the component type of the array.
What you would really like to do there is return new T[], but you have probably found out this isn't allowed, and it isn't allowed due to exactly the mismatch between generics and array types described above.
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.
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);
...
}
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).