Java Generic object initialization - java

Please look at this snippet first :
public MultiThreadManager( Class<T> c) {
T[] allJobs = (T[]) Array.newInstance( c , MAX_THREAD_SIZE ) ;
for ( int i = 0 ; i < MAX_THREAD_SIZE ; i ++ ) {
allJobs[i] = (T) new Object();
service.submit( allJobs[i] );
getWaitingThreads().add( allJobs[i] );
}
}
Here is the exception :
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to slave.JobTemplate
What I am trying to do :
The Constructor of MultiThreadManager should take a generic type ( say Job.java ) which implements Callable. Create array of all those generic data type ( Job,java ) . Initialize it so the constructor of generic data type ( Job.java ) will run and execute them in a executor service.
Please help me identify my error or please suggest a better way.
Thank You in advance
Thanks you all , but things are little more complex :
Herez the other information :
public class Job extends JobTemplate<String> {...details ...}
public abstract class JobTemplate< T > implements Callable<T> {...details..}
and finally
MultiThreadManager< Job > threadManager = new MultiThreadManager< Job >( Job.class );
Again thanks :)

You'll need more reflection, just as you need to create the array:
allJobs[i] = c.newInstance();
and surround with try-catch for all those pesky checked exceptions.
However, I would suggest using new Callable[] because there's no need to go into the specifics of the actual job type. You should also consider a design where reflection is unnecessary: the caller instantiates the jobs instead of passing in the class object. The current solution suffers from the restriction on the Job type to be instantiated only through the default constructor.

When you say new Object(), that creates a new object of class Object. Its dynamic, run-time type is Object. So the cast to T wouldn't be logically valid, unless T is in fact Object.
What you would need to do to create a T is use reflection to invoke the appropriate constructor on T.

Robin & Marko showed the source of the issue, and I have one more thing to stress out, from "Effective Java" By Joshua Bloch:
Item 25: Prefer lists to arrays
...
arrays and generics have very different type rules. Arrays
are covariant and reified; generics are invariant and erased. As a
consequence, arrays provide runtime type safety but not compile-time
type safety and vice versa for generics. Generally speaking, arrays
and generics don’t mix well. If you find yourself mixing them and
getting compile-time errors or warnings, your first impulse should be
to replace the arrays with lists.
Explanation:
Covariant - means, for example, that Array of Objects is supertype of Array of Integer. Generics are invarient, means, you can't cast a List<Integeer> to a List<Object>
Reified - all the information that exists for arrays during compile-time is also available during run-time. Generics are implemented by erasure which means that their type constraints is enforced only during compile-time and then erased (it doesn't exist during run-time).
To sum up:
Mixing arrays with generics will most likely cause you problems - try to avoid mixing the two by using Lists instead of arrays:
public <T> void MultiThreadManager(Class<T> c)
throws IllegalAccessException, InstantiationException {
List<T> allJobs = new ArrayList<T>(MAX_THREAD_SIZE) ;
for (int i = 0; i < MAX_THREAD_SIZE; i++) {
allJobs.add(c.newInstance());
service.submit( allJobs.get(i) );
getWaitingThreads().add( allJobs.get(i));
}
}

Related

Experimenting with java generics

I was playing little bit with java generics, I came across this piece of code, which I am confused why it is happening so.
I am passing my second parameter K as Integer and inside generic method I was casting float to my K type, and in main() I am receiving it as Integer,
In my Code inspector I was seeing the Float number completely sitting in my list (not chopped after casting to Integer) which is of Integer type, but when I try to pick element to save it in Integer variable it gives ClassCastException.
Can someone explain what is going wrong with generics so it is not saving us from casting exception.
Note : I reach to this scenario when I removed my second parameter K from signature so there will be nothing defining type of K , in that case I think Java make it Object and then possibly we get cast exception but why in this case when I am passing K type as well.
import java.util.ArrayList;
import java.util.List;
public class IntegerPrinter {
Integer item;
public void print() {
System.out.println(item);
}
public <T,K> List<K> anyPrint(List<T> num,K lo) {
List<K> mylist = new ArrayList<>();
mylist.add( (K) new Float(2.99f));
return mylist;
}
public IntegerPrinter(Integer item) {
this.item = item;
}
}
import java.util.ArrayList;
import java.util.List;
public class GenericsInAction {
public static void main(String[] args) {
IntegerPrinter oldPrinter = new IntegerPrinter(188);
oldPrinter.print();
List<Integer> dates = oldPrinter.anyPrint(new ArrayList<Integer>(),7);
Integer x = dates.get(0);
}
}
I condensed the code down to the essential parts and modified it slightly to highlight the behaviour that is important:
class Ideone {
public static void main(String[] args) {
List<Integer> dates = new IntegerPrinter().anyPrint(7);
System.out.println(dates.get(0)); // succeeds
Integer x = dates.get(0); // Line 8, throws
}
}
class IntegerPrinter {
public <K> List<K> anyPrint(K lo) {
List<K> mylist = new ArrayList<>();
mylist.add((K) Float.valueOf(2.99f));
return mylist;
}
}
When executed, this program will result in the following output:
2.99
Exception in thread "main" java.lang.ClassCastException: class java.lang.Float cannot be cast to class java.lang.Integer (java.lang.Float and java.lang.Integer are in module java.base of loader 'bootstrap')
at Ideone.main(Main.java:8)
Ideone.com demo
Now, let us step through the code and try to understand what is going on.
This line:
mylist.add((K) new Float(2.99f));
basically tells the compiler "do not care for the type, we (as programmers) guarantee that it is a K, tread it as a K".
Then, if we dig deeper, we see that ArrayList uses an Object[] as backing data structure. So there is on problem here, the backing Object[] elementData can store everything.
Things get weird when we start retrieving elements. The JLS is somewhat vague about the type assertions in those cases (I think they are covered under §5.1.5 and §5.1.6.3, but I am not entirely sure). It basically says "the compiler has to assert the types, but only when necessary".
So if we retrieve an element from our List<Integer>, that clearly is not an Integer, but is passed along to a method that can deal with Object, no type-assertion is necessary. This is exactly the case here:
System.out.println(dates.get(0));
The closest signature matching in System.out is the println(Object) method. This is the situation in JLS, §5.1.5: a widening conversion, it will never throw.
On the other hand, if we now try to retrieve an Integer and try to store it in an Integer:
Integer x = dates.get(0);
Now, a type check is in place. And indeed, if we check the output of the program, we see that the System.out.println(...) took place, but the assignment to an int-variable was the statement that triggered the ClassCastException. This is the situation described in JLS, §5.1.6.3: a narrowing conversion at run time (that comes from ArrayList's elementData(int) method).
Footnote
Generics are most certainly one of the most, if not the most, complex and confusing parts in the JLS. I made a best-effort attempt to cite the JLS on its relevant parts, this might be miss-cited. I also know that this question was asked before, but I am unable to find the duplicate. If:
a citation of the JLS is wrong, and another part should be cited instead, please ping me via comments or edit the post
you find the (a) duplicate, please ping me, and I will close the question as duplicate (and delete my answer, if possible)
Since ArrayList is a generic type whose type erasure is java.lang.Object, that is what is stored in the list. You can think of the type erasure as being the run-time type, the "real" type, as opposed to the compile-time type that the compiler knows about. Any type can be stored in the ArrayList when the program runs.
It just so happens the the type erasure of K in anyPrint is also java.lang.Object, because you have no bounds on the type K. The method is compiled once for all usages, and it must be able to accept any type for K. So when the code for anyPrint is compiled, the cast to K in the line mylist.add( (K) new Float(2.99f)); is ignored, since the type erasure of K is java.lang.Object. Casting to java.lang.Object is useless and pointless. It compiles as mylist.add(new Float(2.99f)); and the code inserts an object of type java.lang.Float into a list of type java.lang.Object.
Also, a cast in Java on an object type simply ensures the object has the correct type, it does not change the values of the object, like a cast on a primitive type. So there is no reason for you to believe the value 2.99f could change.
GenericsInAction is compiled separately.
The parametrized type of K is java.lang.Integer in the main method of GenericsInAction, since you pass in a 7 which is converted to java.lang.Integer via auto-boxing, to be compatible with the type erasure of java.lang.Object in anyPrint. So, when that main method is compiled, the compiler inserts a run-time check, a checkcast, right after the call to dates.get, a check that ensures that the call to dates.get(0); dates returns an object of type java.lang.Integer, since the type of K must be java.lang.Integer inside main.
Since you inserted a java.lang.Float into the list, that run-time check fails and throws ClassCastException.

Why does the compiler not deduce captures relationships in generics? [duplicate]

This question already has answers here:
Java Generics: Wildcard capture misunderstanding
(7 answers)
Closed 7 years ago.
Imagine an interface like this
public interface MessageParameter<T> {
public List<T> unmarshal(byte[] array);
public int getLength(List<T> values);
}
and a consumer of that interface
public class GenericUser {
List<MessageParameter<?>> payload = new ArrayList<>();
public void run() {
byte[] byteArray = new byte[] { 1, 2 };
for (MessageParameter<?> element : payload) {
element.getLength(element.unmarshal(byteArray)); //compiler error
}
}
}
The compiler gives an error
The method getLength(List<capture#1-of ?>) in the type MessageParameter<capture#1-of ?> is not applicable for the arguments (List<capture#2-of ?>)
Clearly since I am using element in both method calls, the type of both is the same and it should be allowed. Another way to ask the same question, why is the compiler creating capture#2?? why can't it deduce that they are logically both the same capture?
Am I missing something? is there a counter-example where this code would throw a runtime exception??
My main question is not how to fix the code (although that would be interesting as well, my current solution is to use Object instead of ?), but what is the logical reason for this error? It looks to me like a shortcoming on the implementation of the compiler more than a logical limitation
The answer is that the compiler is not that smart to accept that the runtime type corresponding to ? is the same because it does not care that your one-line expression involves the same element:
element.getLength(element.unmarshal(byteArray));
is semantically similar to:
List<?> unmarshalledList = element.unmarshal(byteArray);
element.getLength(unmarshalledList);
In this case, it is not so obvious that the list unmarshalledList would surely have to have the same "any-type" as the one expected by getLength(). The above are two separate statements (even though they're contiguous). Imagine that they're not contiguous. You may have something like:
MessageParameter<?> otherElement = getOtherElement();
for (MessageParameter<?> element : payload) {
List<?> unmarshalledList = element.unmarshal(byteArray);
// unmarshalledList can be re-assigned from another parameterized type
unmarshalledList = otherElement.unmarshal(byteArray);
element.getLength(unmarshalledList); // error
}
In other words, the compiler cannot assume that the variable unmarshalledList will retain the same ? type from element when the program reaches the statement invoking getLength on the same element. It can be re-assigned to a different parameterized type in between.
I believe you're misinterpreting the meaning of ? in a generic. That symbol is known as the wildcard; and it refers to a truly unknown type. This is different from your current effort, in which it would literally be better to use Object, as your types are not completely unknown—you know that they both implement Object and can reference them as such. (? extends Object might be better in some places).
As to the reason why ? is not synonymous with Object, remember that primitives do not inherit from Object, but may be referenced with the wildcard. Therefore, after type erasure, your program cannot be certain that the two wildcards are referring to compatible entities; unless you explicitly tell it as much.

Why can't I create an array of a type parameter in Java?

Well, I have read a lot of answers to this question, but I have a more specific one. Take the following snippet of code as an example.
public class GenericArray<E>{
E[] s= new E[5];
}
After type erasure, it becomes
public class GenericArray{
Object[] s= new Object[5];
}
This snippet of code seems to work well. Why does it cause a compile-time error?
In addition, I have known from other answers that the following codes work well for the same purpose.
public class GenericArray<E>{
E[] s= (E[])new Object[5];
}
I've read some comments saying that the piece of code above is unsafe, but why is it unsafe? Could anyone provide me with a specific example where the above piece of code causes an error?
In addition, the following code is wrong as well. But why? It seems to work well after erasure, too.
public class GenericArray<E>{
E s= new E();
}
Array declarations are required to have a reifiable type, and generics are not reifiable.
From the documentation: the only type you can place on an array is one that is reifiable, that is:
It refers to a non-generic class or interface type declaration.
It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
It is a raw type (§4.8).
It is a primitive type (§4.2).
It is an array type (§10.1) whose element type is reifiable.
It is a nested type where, for each type T separated by a ".", T itself is reifiable.
This means that the only legal declaration for a "generic" array would be something like List<?>[] elements = new ArrayList[10];. But that's definitely not a generic array, it's an array of List of unknown type.
The main reason that Java is complaining about the you performing the cast to E[] is because it's an unchecked cast. That is, you're going from a checked type explicitly to an unchecked one; in this case, a checked generic type E to an unchecked type Object. However, this is the only way to create an array that is generic, and is generally considered safe if you have to use arrays.
In general, the advice to avoid a scenario like that is to use generic collections where and when you can.
This snippet of code seems to work well. Why does it cause a compile-time error?
First, because it would violate type safety (i.e. it is unsafe - see below), and in general code that can be statically determined to do this is not allowed to compile.
Remember that, due to type erasure, the type E is not known at run-time. The expression new E[10] could at best create an array of the erased type, in this case Object, rendering your original statement:
E[] s= new E[5];
Equivalent to:
E[] s= new Object[5];
Which is certainly not legal. For instance:
String[] s = new Object[10];
... is not compilable, for basically the same reason.
You argued that after erasure, the statement would be legal, implying that you think this means that the original statement should also be considered legal. However this is not right, as can be shown with another simple example:
ArrayList<String> l = new ArrayList<Object>();
The erasure of the above would be ArrayList l = new ArrayList();, which is legal, while the original is clearly not.
Coming at it from a more philosophical angle, type erasure is not supposed to change the semantics of the code, but it would do so in this case - the array created would be an array of Object rather than an array of E (whatever E might be). Storing a non-E object reference in it would then be possible, whereas if the array were really an E[], it should instead generate an ArrayStoreException.
why is it unsafe?
(Bearing in mind we are now talking about the case where E[] s= new E[5]; has been replaced with E[] s = (E[]) new Object[5];)
It is unsafe (which in this instance is short for type unsafe) because it creates at run-time a situation in which a variable (s) holds a reference to an object instance which is not a sub-type of the variable's declared type (Object[] is not a subtype of E[], unless E==Object).
Could anyone provide me with a specific example where the above piece of code causes an error?
The essential problem is that it is possible to put non-E objects into an array that you create by performing a cast (as in (E[]) new Object[5]). For example, say there is a method foo which takes an Object[] parameter, defined as:
void foo(Object [] oa) {
oa[0] = new Object();
}
Then take the following code:
String [] sa = new String[5];
foo(sa);
String s = sa[0]; // If this line was reached, s would
// definitely refer to a String (though
// with the given definition of foo, this
// line won't be reached...)
The array definitely contains String objects even after the call to foo. On the other hand:
E[] ea = (E[]) new Object[5];
foo(ea);
E e = ea[0]; // e may now refer to a non-E object!
The foo method might have inserted a non-E object into the array. So even though the third line looks safe, the first (unsafe) line has violated the constraints that guarantee that safety.
A full example:
class Foo<E>
{
void foo(Object [] oa) {
oa[0] = new Object();
}
public E get() {
E[] ea = (E[]) new Object[5];
foo(ea);
return ea[0]; // returns the wrong type
}
}
class Other
{
public void callMe() {
Foo<String> f = new Foo<>();
String s = f.get(); // ClassCastException on *this* line
}
}
The code generates a ClassCastException when run, and it is not safe. Code without unsafe operations such as casts, on the other hand, cannot produce this type of error.
In addition, the following code is wrong as well. But why? It seems to work well after erasure, too.
The code in question:
public class GenericArray<E>{
E s= new E();
}
After erasure, this would be:
Object s = new Object();
While this line itself would be fine, to treat the lines as being the same would introduce the semantic change and safety issue that I have described above, which is why the compiler won't accept it. As an example of why it could cause a problem:
public <E> E getAnE() {
return new E();
}
... because after type erasure, 'new E()' would become 'new Object()' and returning a non-E object from the method clearly violates its type constraints (it is supposed to return an E) and is therefore unsafe. If the above method were to compile, and you called it with:
String s = <String>getAnE();
... then you would get a type error at runtime, since you would be attempting to assign an Object to a String variable.
Further notes / clarification:
Unsafe (which is short for "type unsafe") means that it could potentially cause a run-time type error in code that would otherwise be sound. (It actually means more than this, but this definition is enough for purposes of this answer).
it's possible to cause a ClassCastException or ArrayStoreException or other exceptions with "safe" code, but these exceptions only occur at well defined points. That is, you can normally only get a ClassCastException when you perform a cast, an operation that inherently carries this risk. Similarly, you can only get an ArrayStoreException when you store a value into an array.
the compiler doesn't verify that such an error will actually occur before it complains that an operation is unsafe. It just knows that that certain operations are potentially able to cause problems, and warns about these cases.
that you can't create a new instance of (or an array of) a type parameter is both a language feature designed to preserve safety and probably also to reflect the implementation restrictions posed by the use of type erasure. That is, new E() might be expected to produce an instance of the actual type parameter, when in fact it could only produce an instance of the erased type. To allow it to compile would be unsafe and potentially confusing. In general you can use E in place of an actual type with no ill effect, but that is not the case for instantiation.
A compiler can use a variable of type Object to do anything a variable of type Cat can do. The compiler may have to add a typecast, but such typecast will either throw an exception or yield a reference to an instance of Cat. Because of this, the generated code for a SomeCollection<T> doesn't have to actually use any variables of type T; the compiler can replace T with Object and cast things like function return values to T where necessary.
A compiler cannot use an Object[], however, to do everything a Cat[] can do. If a SomeCollection[] had an array of type T[], it would not be able to create an instance of that array type without knowing the type of T. It could create an instance of Object[] and store references to instances of T in it without knowing the type of T, but any attempt to cast such an array to T[] would be guaranteed to fail unless T happened to be Object.
Let's say generic arrays are allowed in Java. Now, take a look at following code,
Object[] myStrs = new Object[2];
myStrs[0] = 100; // This is fine
myStrs[1] = "hi"; // Ambiguity! Hence Error.
If user is allowed to create generic Array, then user can do as I've shown in above code and it will confuse compiler. It defeats the purpose of arrays (Arrays can handle only same/similar/homogeneous type of elements, remember?). You can always use array of class/struct if you want heterogeneous array.
More info here.

Java Best Practices - Returning an Object vs. a Generic

I'm using Generics for the first time for a school project and I have come across a philosophical dilemma regarding whether to return objects or my declared generic element in my methods.
My OCD is telling me that I need to always return the known type but I'm finding that doing so creates some downstream annoyances when I feed primitive datatypes into my class (and, of course, for this project I'm only ever feeding primitives into this class).
Here's an example of what I mean:
public class DansPriorityQueue<E extends Comparable>
{
private ArrayList<E> tree;
//Here's a method that returns an object
public Object peek() {
return tree.get(0);
}
//Here's a method that returns the generic type
public E peek() {
return tree.get(0);
}
(As an FYI.. I'm required to implement this JDK class myself but I am fortunately not required to implement the same interfaces that the real PriorityQueue does so I do have a choice as to whether I want to use the Object or the generic)
My Issue
It makes me feel a little dirty but I'm tempted just to return an Object rather than my E generic element on these methods because when I return E, JUnit forces me to cast my integer values:
DansPriorityQueue<Integer> dpq = new DansPriorityQueue<Integer>();
dpq.add(1);
assertEquals("Expected different value", (Integer) 1, dpq.peek());
When I return an object on the other hand, the auto-boxing doesn't force me cast my primitive value.
Here's a more eloquent description of the issue I've been facing:
http://www.aschroder.com/2009/10/php-1-java-0-the-method-assertequalsobject-object-is-ambiguous-for-the-type/
------------EDIT----------------
Here's the actual error I receive when I return the generic type and fill my list with the autoboxed Integer object without the cast above: The method assertEquals(String, Object, Object) is ambiguous for the type DansPriorityQueueTest
--------- END EDIT--------------
Questions
Can anyone tell me why I should or should not return an object as opposed to the generic element I'm working with? Both seem to have upsides and downsides... what's the best practice?
I know vaguely that returning an Object can cause some casting issues later on but I've not yet run into them... does anyone have a specific example of how this can be dangerous?
In the JDK, I've noticed that many of the Collections methods return Object by default. Is this because Generics was introduced in a later version of Java or was this a conscious decision by Sun Systems?
Can anyone tell me why I should or should not return an object as opposed to the generic element I'm working with? Both seem to have upsides and downsides... what's the best practice?
It depends. In a case like this you'd want to generic type - otherwise what's the point of defining generic type for the class?
I know vaguely that returning an Object can cause some casting issues later on but I've not yet run into them... does anyone have a specific example of how this can be dangerous?
Sure!
DansPriorityQueue<String> queue = new DansPriorityQueue<String>();
//add items
Float f = (Float)queue.getObject(); //uh-oh! this compiles but will fail
Float f = queue.getObject(); //generic type, fails during compile
In the JDK, I've noticed that many of the Collections methods return Object by default. Is this because Generics was introduced in a later version of Java or was this a conscious decision by Sun Systems?
It's due to backward compatibility mostly, or for cases where you truly will use the collection to contain disparate values (a mishmash of say, JLabels, Strings and Icons for instance for rendering a JTable for instance).
assertEquals("Expected different size", (Integer) 2, dpq.size());
I don't think this should be a problem. dpq.size() should just return an int regardless off what is stored in the priority queue. It would not be a generic value.
You can create something like
DansPriorityQueue<Double> queue = new DansPriorityQueue<Double>();
for(double d = 0; d < 10; d+=1.0)
queue.add(d);
and that should cause no issues, right?

Java use generics to set the primitive type of an array later

I am trying to write some simple numerical code in Java where one can choose between a float and double later. A simplified version of my class looks like the example below:
public class UniformGrid<T> {
public T[] data;
public UniformGrid(int arrayDim) {
data = new T[arrayDim];
}
}
This didn't work I got a generic array creation error when trying to compile. Googling and reading some SO answers I learned about java.lang.reflect.Array and tried to use
data = (T[]) Array.newInstance(T.class, arrayDim);
Which also didn't work, since T is (probably) a primitive type. My Java knowledge is quite rusty (especially when it comes to generics) and I would like to know why the new operator cannot be used with a generic array type. Also of course I am interested in how one would solve this problem in Java.
You cannot create a generic array in Java because of type erasure. The easiest way to get around this would be to use a a List<T>. But if you must use an array, you can use an Object[] for your array and ensure that only T objects are put into it. (This is the strategy ArrayList takes.)
Ex:
private Object[] data = new Object[10];
private int size = 0;
public void add(T obj) {
data[size++] = obj;
}
public T get(int i){
return (T) data[i];
}
Of course you'll get an unchecked warning from your compiler, but you can suppress that.
Generics can't be used when creating an array because you don't know at runtime what type T is. This is called type erasure.
The solution is simple: use List<T> data.
Sorry, you'll have to take another approach:
Type parameters must be reference types, they can't be primitive types.
Only reference types support polymorphism, and only for instance methods. Primitive types do not. float and double don't have a common supertype; you can not write an expression like a + b and choose at runtime whether to perform float addition or double addition. And since Java (unlike C++ or C#, which emit new code for each type parameter) uses the same bytecode for all instances of a generic type, you'd need polymorphism to use a different operator implementation.
If you really need this, I'd look into code generation, perhaps as part of an automated build. (A simple search & replace on the source ought to be able to turn a library operating on double into a library operating on float.)
This is possible, as long as you use Float and Double instead of float and double, as primitive types are not allowed in Java Generics. Of course, this will probably be quite slow. And, you won't be able to (safely) allow direct public access to the array. So this answer is not very useful, but it might be theoretically interesting. Anyway, how to construct the array ...
data = (T[]) new Object[arrayDim];
This will give you a warning, but it's not directly anything to worry about. It works in this particular form - it's inside a generic constructor and data is the only reference to this newly constructed object. See this page about this.
You will not be able to access this array object publicly in the way you might like. You'll need to set up methods in UniformGrid<T> to get and set objects. This way, the compiler will ensure type-safety and the runtime won't give you any problems.
private T[] data;
public void set(int pos, T t) {
data[pos] = t;
}
public T get(int pos) {
return data[pos];
}
In this case, the interface to set will (at compile-time) enforce the correct type is passed. The underlying array is of type Object[] but that's OK as it can take any reference type - and all generic types are effectively List<Object> or something like that at runtime anyway.
The interesting bit is the getter. The compiler 'knows' that the type of data is T[] and hence the getter will compile cleanly and promises to return a T. So as long as you keep the data private and only access it through get and set then everything will be fine.
Some example code is on ideone.
public static void main(String[] args) {
UniformGrid<A> uf = new UniformGrid<A>(1);
//uf.insert(0, new Object()); // compile error
uf.insert(0, new A());
uf.insert(0, new B());
Object o1= uf.get(0);
A o2= uf.get(0);
// B o2= uf.get(0); // compiler error
System.out.println(o1);
System.out.println(o2);
System.out.println("OK so far");
// A via_array1 = uf.data[0]; // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LA;
}
As you would desire, there are compilation errors with uf.insert(0, new Object()) and B o2= uf.get(0);
But you shouldn't make the data member public. If you did, you could write and compile A via_array1 = uf.data[0];. That line looks like it should be OK, but you get a runtime exception: Ljava.lang.Object; cannot be cast to [LA;.
In short, the get and set interface provide a safe interface. But if you go to this much trouble to use an array, you should just use an ArrayList<T> instead. Moral of the story: in any language (Java or C++), with generics or without generics, just say no to arrays. :-)
Item 25 in Effective Java, 2nd Edition talks about this problem:
Arrays are covariant and reified; generics are invariant and erased.
As a consequence, arrays provide run-time type safety but not compile-time type safety and vice versa for generics. Generally speaking arrays and generics don't mix well.

Categories

Resources