stupid generics

Z

zero

When I first heard about generics I was excited. A great idea finally
available in Java. But the way they are implemented really makes no sense.
Because of type erasure generics do nothing that 1.4 code didn't already do
by using Object as "generic" class for all (sub)classes.

And the problem with arrays of generic classes? Simply ridiculous. There
is no sane reason why the following code (from a self-learning chess
program I've been working on) should not work, and yet...

ArrayList<Piece>[] board;
board = new ArrayList<Piece>[8]; // error: cannot create a generic
// array of ArrayList<Piece>
board[0] = new ArrayList<Piece>();
board[0].add(0, new Rook(false, 0, 8));

where of course Rook is a subclass of Piece.

I'm forced to use board = new ArrayList[8]; which of course gives an
unchecked warning.

They should just throw away the whole generics thing and replace it with a
C++ like approach.
 
D

Daniel Dyer

And the problem with arrays of generic classes? Simply ridiculous.
There
is no sane reason why the following code (from a self-learning chess
program I've been working on) should not work, and yet...

ArrayList<Piece>[] board;
board = new ArrayList<Piece>[8]; // error: cannot create a generic
// array of ArrayList<Piece>
board[0] = new ArrayList<Piece>();
board[0].add(0, new Rook(false, 0, 8));

where of course Rook is a subclass of Piece.

I'm forced to use board = new ArrayList[8]; which of course gives an
unchecked warning.

I share your frustration with the difficulty in creating a generic array
(it is possible with the java.lang.reflect.Array.newInstance method and an
unchecked cast), but why not model the board as a two dimensional array of
pieces?

Piece[][] board = new Piece[8][8];

Dan.
 
Z

zero

I share your frustration with the difficulty in creating a generic
array (it is possible with the java.lang.reflect.Array.newInstance
method and an unchecked cast), but why not model the board as a two
dimensional array of pieces?

Piece[][] board = new Piece[8][8];

Dan.

To be honest I don't remember why I chose the array of arraylists. I wrote
this code over a year ago, but then other things got in the way and the
project was postponed. I just started cleaning up the code to move to 1.5
and continue development.

I guess a board doesn't change size, so there's no real point in using
arraylists. I may indeed change it, thanks for pointing it out.

Doesn't change the fact that generics are stupid though ;-)
 
D

Daniel Dyer

it is possible with the java.lang.reflect.Array.newInstance method and
an unchecked cast

I don't know why I made that so complicated, it's just equivalent to this:

ArrayList<Piece>[] board = (ArrayList<Piece>[]) new ArrayList[8];

You will get a warning (which you can suppress with the @SuppressWarnings
annotation, or at least you will be able to once it is implemented
properly in 1.6), but at leat you will get compile-time type checking on
subsequent operations involving the generic list.

Dan.
 
R

Roedy Green

board = new ArrayList<Piece>[8]; // error: cannot create a generic

For a chessboard, you have precisely 8 row and 8 columns. That will
never change. Therefore you should use an array not an ArrayList of
ArrayLists. It is much simpler code.

Piece[][] board = new Piece[8][8];

for ( int row=0; row<8; row++ )
{
for ( int col=0; col<8; col++ )
{
board[row][col] = new Piece();
}
}

You don't need generics with arrays. Typing in built in properly.
 
Z

zero

board = new ArrayList[8];
this means you want 8 ArrayLists, not an ArrayList of 8 elements. Is
that what you meant?

If you did want 8 ArrayLists, you still have initialise each of the 8
slots with an ArrayList object.

See http://mindprod.com/jgloss/gotchas.html#ARRAY

Hence the next line in my code:
board[0] = new ArrayList<Piece>();

Obviously this is part of a for loop, I just slimmed down the code. That
was not really the point of my post anyway. The point was, it is
*impossible* to create an array of parameterized types without getting a
warning.
 
Z

zero

board = new ArrayList<Piece>[8]; // error: cannot create a generic

For a chessboard, you have precisely 8 row and 8 columns. That will
never change. Therefore you should use an array not an ArrayList of
ArrayLists. It is much simpler code.

Piece[][] board = new Piece[8][8];

for ( int row=0; row<8; row++ )
{
for ( int col=0; col<8; col++ )
{
board[row][col] = new Piece();
}
}

You don't need generics with arrays. Typing in built in properly.

Yes, Dan pointed that out already too. See my reply to his post.
 
T

Thomas Hawtin

Roedy said:
board = new ArrayList<Piece>[8]; // error: cannot create a generic


For a chessboard, you have precisely 8 row and 8 columns. That will
never change. Therefore you should use an array not an ArrayList of
ArrayLists. It is much simpler code.

Might want to use the code for Go boards as well. Perhaps.

I would tend to make the Board a class itself, and slightly optimise/get
rid of the array of arrays confusion. (A List would do in place of the
array.)

private int index(int row, int col) {
if (!(
0 <= row && row < size &&
0 <= col && col < size
)) {
throw new IllegalArgumentException();
}
return row*size + col;
}
public Piece get(int row, int col) {
return board[index(row, col)];
}
You don't need generics with arrays. Typing in built in properly.

Generics is a built in property of arrays. You don't need arrays of
generics.

Tom Hawtin
 
G

Googmeister

Thomas said:
Generics is a built in property of arrays. You don't need arrays of
generics.

I'm not sure I follow. I agree the OPs example was poor motivation,
but many of us would very much like to be able to create arrays of
generics, e.g., array implementation of a stack.
 
T

Thomas G. Marshall

Thomas Hawtin coughed up:
Roedy Green wrote:
....[rip]...
You don't need generics with arrays. Typing in built in properly.

Generics is a built in property of arrays. You don't need arrays of
generics.


That doesn't make any sense to me at all. In this example:

ArrayList<Apple>[] allGroves = new ArrayList<Apple>[100];

I would be asking for an array of ArrayLists's that each only take Apples.
This would prevent someone from doing this by accident later:

allGroves[50] = new ArrayList<Orange>();

Regardless of whether or not the OP has modeled his particular issue
correctly, not having this ability seems an annoyance that /might/ not have
been necessary.
 
T

Thomas Hawtin

Thomas said:
Thomas Hawtin coughed up:
Roedy Green wrote:

...[rip]...

You don't need generics with arrays. Typing in built in properly.

Generics is a built in property of arrays. You don't need arrays of
generics.



That doesn't make any sense to me at all. In this example:

ArrayList<Apple>[] allGroves = new ArrayList<Apple>[100];

I would be asking for an array of ArrayLists's that each only take Apples.
This would prevent someone from doing this by accident later:

allGroves[50] = new ArrayList<Orange>();

Regardless of whether or not the OP has modeled his particular issue
correctly, not having this ability seems an annoyance that /might/ not have
been necessary.

What I was trying to say (extremely badly) is that arrays fit in with
the concept of genericity in that they have the property of being
generic. Unfortunately they do it with different (and less safe rules)
than the rest of the language. Once we have something the wraps arrays,
then the only real need for arrays of references is for optimisation.
Mixing the two is just an annoyance.

Tom Hawtin
 
R

Roedy Green

I'm not sure I follow.

With ArrayList you use <Thing> notation to tell the compiler what sort
of beasts will live in the ArrayList. With plain arrays, you just use
Thing[] without that notation. You sidestep all the quirky generics
issues of type erasure etc. With arrays, the typing is built in
properly rather than hacked with generics.
 
G

Googmeister

Roedy said:
I'm not sure I follow.

With ArrayList you use <Thing> notation to tell the compiler what sort
of beasts will live in the ArrayList. With plain arrays, you just use
Thing[] without that notation. You sidestep all the quirky generics
issues of type erasure etc. With arrays, the typing is built in
properly rather than hacked with generics.

OK, I see. To me "array of generics" refers to what you want to
do when you implement an ArrayList (or any other array-based
data structure).

public class ArrayList<Item> {
private Item[] elements; // error: "can't create an array of
generics"
...

I'd very much like to do this. Generics are particularly useful with
collections and other data structures, but the language gets in your
way if you try to create your own.
 
R

Roedy Green

public class ArrayList<Item> {
private Item[] elements; // error: "can't create an array of
generics"

I wrote a little program to confirm this to myself:

import java.util.ArrayList;
/**
* explore use of Generics and arrays
*/
public class GenAr

{
/**
* test harness
*
* @param args not used
*/
public static void main ( String[] args )
{
ArrayList<String>[] stuff = new ArrayList<String>[ 10 ];
}
}

I feel embarrassed that Java's generics are so Mickey Mouse they don't
even integrate with arrays.
 
Z

zero

Thomas Hawtin coughed up:
Roedy Green wrote:
...[rip]...
You don't need generics with arrays. Typing in built in properly.

Generics is a built in property of arrays. You don't need arrays of
generics.


That doesn't make any sense to me at all. In this example:

ArrayList<Apple>[] allGroves = new ArrayList<Apple>[100];

I would be asking for an array of ArrayLists's that each only take
Apples. This would prevent someone from doing this by accident later:

allGroves[50] = new ArrayList<Orange>();

Regardless of whether or not the OP has modeled his particular issue
correctly, not having this ability seems an annoyance that /might/ not
have been necessary.

The generics tutorial(1) gives a reason for this annoyance, however I think it
could all have been sidestepped by a better implementation for generics.

<disclaimer>
Following code is NOT Java, but is the way I wish it would work. (based on the
way templates are implemented in C++)
</disclaimer>

Take generic class GenericContainer, with type parameter T:

public class GenericContainer<T>
{
T[] data;

public GenericContainer(int size)
{
data = new T[size];
}

public T get(int index)
{
return data[index];
}

// ...
}

When you use this class, for example;

GenericContainer<MyClass> c = new GenericContainer<MyClass>(5);

the compiler should generate a GenericContainerOfMyClass class, which would be:

public class GenericContainerOfMyClass
{
MyClass[] data;

public GenericContainerOfMyClass(int size)
{
data = new MyClass[size];
}

public MyClass get(int index)
{
return data[index];
}

// ...
}

Thus a different class would be created for each type of GenericContainer you
want.

This would greatly improve things. The way generics are implemented now I see
only 2, very minor, advantages over 1.4 code: the compiler checks the type, and
you don't have to typecast when for example getting an object from a collection.



(1) http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
 
R

Ross Bamford

...[rip]...

That doesn't make any sense to me at all. In this example:

ArrayList<Apple>[] allGroves = new ArrayList<Apple>[100];

I would be asking for an array of ArrayLists's that each only take
Apples. This would prevent someone from doing this by accident later:

allGroves[50] = new ArrayList<Orange>();

Regardless of whether or not the OP has modeled his particular issue
correctly, not having this ability seems an annoyance that /might/ not
have been necessary.

The generics tutorial(1) gives a reason for this annoyance, however I
think it
could all have been sidestepped by a better implementation for generics.

[ ... ]
(1) http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

Hmm, that's interesting. In that, they give the reason for avoiding this
kind of thing as

"This restriction is necessary to avoid situations like:

List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException

If arrays of parameterized type were allowed, the example above would
compile without any unchecked warnings, and yet fail at run-time."

Perhaps it's just me, but I don't really see much difference between that
and:

List<String> lsa = new ArrayList<String>(); // ... but this is.
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // still unsound, and *no* runtime store check
String s = lsa.get(0); // run-time error - ClassCastException

Have I missed something, or is it just a pretty poor excuse. If the
compiler couldn't check array access at all then I could understand the
reasoning, but it can - I just can't see any real barriers that prevent
generic array instantiation. What am I missing?
 
R

Ross Bamford

<disclaimer>
Following code is NOT Java, but is the way I wish it would work. (based
on the
way templates are implemented in C++)
</disclaimer>

Take generic class GenericContainer, with type parameter T:

public class GenericContainer<T>
{
T[] data;

public GenericContainer(int size)
{
data = new T[size];
}

public T get(int index)
{
return data[index];
}

// ...
}

When you use this class, for example;

GenericContainer<MyClass> c = new GenericContainer<MyClass>(5);

the compiler should generate a GenericContainerOfMyClass class, which
would be:

public class GenericContainerOfMyClass
{
MyClass[] data;

public GenericContainerOfMyClass(int size)
{
data = new MyClass[size];
}

public MyClass get(int index)
{
return data[index];
}

// ...
}

Thus a different class would be created for each type of
GenericContainer you
want.

I think the current system achieves a reasonable balance between
flexibility on the one hand, and performance and backward compatibility on
the other. I'm not saying it's perfect (*far* from it), but I think that
if they had gone the bytecode-generation route it would have been a lot
less useful - an unrestricted number of new classes being generated on the
fly, entirely 'under the hood' (i.e. outside your app's control). It might
make even basic operations entirely unpredictable in terms of time,
compromise security, and make it much more difficult to implement custom
classloading strategies. I expect it's likely that the generics team
started with something like above, given that bytecode generation is used
elsewhere in the platform, and then worked backward to try to make it
performant and compatible.
This would greatly improve things. The way generics are implemented now
I see
only 2, very minor, advantages over 1.4 code: the compiler checks the
type, and
you don't have to typecast when for example getting an object from a
collection.

I think generics are a great convenience for working with collections, and
useful in other situations too. I think the problem is more that they
promise too much - it's natural to assume you can do things like
T.newInstance() and new List<String>[40]. Erasure is certainly a minefield
of ifs and elses, but I'm not convinced that creating separate classes
under the hood is a viable alternative.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

generics puzzle 57
How to sort a CSV file with merge sort JAVA 7
Generics and for each 12
can this be done with generics? 32
subclassing and generics 19
Generics annoyance 18
JDK5 Generics 6
Generics 12

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top