Your 2 cents: Java Generics, love it, hate it, or waiting?

C

Chris Smith

I don't see how this is lost. Type erasure refers to generics info
being lost after compile finishes, right? Type erasure does not effect
the number of generated compiler warnings.

Generics without type erasure would allow the Java virtual machine at
runtime to check the correctness of casts to generic types. That would
eliminate the need for the compiler to generate an unchecked cast
warning at compile time.
Can you be more specific, please.

Sure. There are 65 examples in the core Java API. Search
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/class-use/Class.html
for the string "Class<T>". None of these parameters should be
necessary. (To be fair, sometimes the actual parameter serves to
disambiguate the method, and prevents the need to specify explicit type
parameters; but specifying an explicit type parameter would be clearer
and more consistent.)
I do not see what you are referring to. Maybe we mean very different
things by "type erasure".

No, we don't. I'm just talking about the consequences of type erasure,
whereas you seem to be limiting your consideration to the definition
itself. For example:
Regardless of "type erasure" or .... "type retention" the paramters,
signatures, documentation of methods would not change.

Of course it would be POSSIBLE to define methods with all the same
parameters they have now. It just wouldn't be sensible. There would be
better ways to do it.
Can you list a
handful of methods that take "Class<T>" as a paramter?

See above.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

I was of something else: If you have sources that were written before
5.0 you can write new classes which use generics and have data flow
between instances of previously written classes and generics using
classes.

It would, of course, be possible to do this without type erasure as
well, in exactly that set of circumstances where it's safe, by giving an
appropriate definition of raw types. When it's not safe, you could
provide trivial wrappers (much like the current Collections.checkedList,
except without its stupid Class<E> parameter; see earlier post) that
would fit the necessary API and cast at run-time. The result would be
better than what we have now.
If you have classes B,C,D which use class A internally, where class A
is a container. You then modify A to be a generic, paramterizable on
Classes. The change to A does not break B,C,D. B,C,D can stay excalty
as they were even though the container they use internally has changed
to be a generic.

Assuming the result only has to run on Java 1.5, this doesn't require
type erasure. And since Java 1.5 produces a new class file version
number, even the current implementation doesn't save you from that,
anyway.

So you just define the raw type, as used in the old source and binary
code, as being equivalent to what we now call the type erasure of the
type, and voila! Everything works.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
R

Roedy Green

Specifically, the compatibility gained is the ability to use code
compiled with Java 1.5 in a pre-1.5 release. This is not an important
kind of compatibility, and it appears Sun has decided not to mai

There is another kind of compatiblility. The same class name can be
used with and without generics.
 
C

Chris Smith

Roedy Green said:
There is another kind of compatiblility. The same class name can be
used with and without generics.

Sorry, I don't understand you here.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
R

Roedy Green

No, we don't. I'm just talking about the consequences of type erasure,

one of the biggies is you can't say new T( ...) in your generified
<T> class. With type erasure T is just Object.

Designing a type system where that was possible would be difficult
since a subclass of T does not necessarily have the same constructors
available as T.
 
R

Roedy Green

Sorry, I don't understand you here.

The way things are, JDK 1.5 ArrayList has generics. You can call it
with JDK 1.4 code, without trouble, ignoring its genericity and
putting up with warnings. Without type erasure, I think you would
need to use two separate classes: ArrayList (without Generics) and
GArrayList (with generics)

You would be forced to use generics whenever you used GArrayList and
could not use generics with old ArrayList. Somebody told me that is
what happened in C# when they did their generics without type erasure.
 
C

Chris Smith

Roedy Green said:
one of the biggies is you can't say new T( ...) in your generified
<T> class. With type erasure T is just Object.

Designing a type system where that was possible would be difficult
since a subclass of T does not necessarily have the same constructors
available as T.

Yep, definitely difficult, and certainly not type-safe. What would be
possible, though, is:

T obj = T.class.newInstance();

or the equivalent with Constructor. Currently, you need to pass an
actual parameter of type Class<T> somewhere. Of course, since there is
only one possible value of that parameter, it's really no parameter at
all, but just syntax boilerplate... and worse: it exposes the use of
reflection on T as part of the public API of the element.

This was one of the two problems I mentioned earlier.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

Roedy Green said:
The way things are, JDK 1.5 ArrayList has generics. You can call it
with JDK 1.4 code, without trouble, ignoring its genericity and
putting up with warnings. Without type erasure, I think you would
need to use two separate classes: ArrayList (without Generics) and
GArrayList (with generics)

You would be forced to use generics whenever you used GArrayList and
could not use generics with old ArrayList. Somebody told me that is
what happened in C# when they did their generics without type erasure.

Ah! That is one way of doing it. Earlier, I proposed a different
solution. That was to define the raw type as being the same as what is
now called the type erasure of the type, but expanded as an explicit
parameterized type instantiation. For example, "Map" would become
"Map<Object,Object>".

This means, of course, that many of the existing uses of raw types would
be incompatible with generic types. For example, a method defined to
take a parameter of Map would be incompatible with Map<String,String>
because there is no implicit assignment from Map<String,String> to
Map<Object,Object> (which is the implicit expansion of Map). That's no
insurmountable problem, though... just an inconvenient API. It can be
solved in several ways:

1. As always, deprecate the old API, introduce a new overload that takes
the right type (note that since we don't have type erasure, overloads
can be distinguished by type parameters). Eventually, in Java 1.97
(also known as Java 97.0) remove the deprecated method.

2. In the short term, provide mappings that let someone define checked
adapters between types. For example, define Collections.typedMap, which
is a method of two type parameters K and V, which takes a parameter of
type Map<Object,Object>, and returns a result of type Map<K,V>, and
performs type checking on every mutation operation to ensure that the
type safety is maintained. Similar, define a Collections.untypedMap
which does the inverse (and also needs to type-check, since you can add
things to a Map<Object,Object> that can't be added to a Map<K,V>).

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
E

Ed

Chris wrote:

"They are optional if you
work on your own code in your own basement ..."

That's indeed what I meant.

And when I said, "But it just doesn't matter," I meant, "But it just
doesn't matter to me," as in: this is my personal vote.

Apologies.

..ed
 
T

Thomas Hawtin

Roedy said:
one of the biggies is you can't say new T( ...) in your generified
<T> class. With type erasure T is just Object.

That is nothing to do with type erasure.
Designing a type system where that was possible would be difficult
since a subclass of T does not necessarily have the same constructors
available as T.

C++ templates manages it by being, if you like, a "syntax level" feature
rather than Java's "type level" feature.

The obvious way to solve the problem with Java is to introduce an
abstract factory:

interface MyFactory<PRODUCT> {
PRODUCT newInstance(...);
}

More work for the trivial case, but more flexible and it clearly
expresses the type requirements within the type system.

Tom Hawtin
 
T

Thomas Hawtin

Chris said:
actual parameter of type Class<T> somewhere. Of course, since there is
only one possible value of that parameter, it's really no parameter at
all, but just syntax boilerplate...

Not entirely correct. Class<Integer> may be Integer.class, int.class or,
of course, null. Normally the latter two should be checked for (not
always done correctly) and throw an appropriate exception. Okay, that
isn't much of an improvement...
and worse: it exposes the use of
reflection on T as part of the public API of the element.

That's probably going to be exposed by implication anyway.

Tom Hawtin
 
D

Dimitri Maziuk

Thomas Hawtin sez:
That is nothing to do with type erasure.


C++ templates manages it by being, if you like, a "syntax level" feature
rather than Java's "type level" feature.

The obvious way to solve the problem with Java is to introduce an
abstract factory:

interface MyFactory<PRODUCT> {
PRODUCT newInstance(...);
}

How's that gonna work if PRODUCT is a T? -- it still has to call
"new PRODUCT( ...)" at some point and that's still not possible
without passing it a specific type (or fixing the whole constructor
mess).

Dima
 
T

Thomas Hawtin

Dimitri said:
Thomas Hawtin sez:

How's that gonna work if PRODUCT is a T? -- it still has to call
"new PRODUCT( ...)" at some point and that's still not possible
without passing it a specific type (or fixing the whole constructor
mess).

Implementations of the interface are specific to a particular type. Or
they delegate in some way - constructors are not the only way of getting
hold of an instance.

Tom Hawtin
 
O

opalpa

Sure. There are 65 examples in the core Java API. Search
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/class-use/Class.html
for the string "Class<T>". None of these parameters should be
necessary. (To be fair, sometimes the actual parameter serves to
disambiguate the method, and prevents the need to specify explicit type
parameters; but specifying an explicit type parameter would be clearer
and more consistent.)

Wow. Thanks for the info.

Opalinski
(e-mail address removed)
http://www.geocities.com/opalpaweb/
 
J

John C. Bollinger

Thomas said:
Not entirely correct. Class<Integer> may be Integer.class, int.class or,
of course, null. Normally the latter two should be checked for (not
always done correctly) and throw an appropriate exception. Okay, that
isn't much of an improvement...

Null, yes, but are you sure about int.class? Wait a moment....

Well, I'll be! According to Sun's compiler, int.class is assignment
compatible with all of these: Class<Integer>, Class<? extends Integer>,
and Class<? super Integer>, yet a runtime check shows that the two
classes are unequal (as should be). And... wait... Eclipse agrees! Is
this actually the specified behavior? I didn't know that.

What is the twisted justification for this miscarriage of justice, logic
and good taste?
 
C

Chris Uppal

John said:
Well, I'll be! According to Sun's compiler, int.class is assignment
compatible with all of these: Class<Integer>, Class<? extends Integer>,
and Class<? super Integer>, [...]

It makes my skin creep too, but it seems to be the defined behaviour.

JLS3:
15.8.2 Class Literals
A class literal is an expression consisting of the name of a class,
interface, array, or primitive type, or the pseudo-type void, followed
by a '.' and the token class. The type of a class literal, C.Class,
where C is the name of a class, interface or array type, is Class<C>.
If p is the name of a primitive type, let B be the type of an expression
of type p after boxing conversion (§5.1.7). Then the type of p.class is
Class<B>. The type of void.class is Class<Void>.

One rather nice side-effect is that:

List list = new ArrayList(); // or some pre-1.5 code
Collections
.checkedCollection(list, int.class)
.add(22);

will compile, but will fail at runtime ;-)

Pretty disgusting. (But thanks for pointing it out anyway, Thomas)

-- chris
 
T

Thomas Hawtin

Chris said:
List list = new ArrayList(); // or some pre-1.5 code
Collections
.checkedCollection(list, int.class)
.add(22);

will compile, but will fail at runtime ;-)

More worryingly, it should compile lint-free using generics throughout.
Pretty disgusting. (But thanks for pointing it out anyway, Thomas)

I was going to send in a patch to stop such checked collection
construction at runtime when I noticed it the other week (must have got
distracted). Together with removing the lazily created zero length
arrays from the serialisation format.

Tom Hawtin
 
O

Oliver Wong

In my opinion no benefit is lost by "type erasure". I've yet to come
across a single benefit lost.

It would have been nice if you could use "instanceof" with generics.

<example>
public void someMethod(ArrayList<?> foo) {
if (foo instanceof ArrayList<String>) {
//... do something
}
}
</example>

Similarly, it'd be nice if the runtime checked generic parameters in
casts and threw the ClassCastException occasionally:

<example>
ArrayList<Comparable> bar = getArrayListOfComparables();
try {
ArrayList<String> foo = (ArrayList<String>)bar;
} catch (ClassCastException) (
System.err.println("bar is an ArrayList, but not an ArrayList<String>");
}
</example>

This would only be possible if the generic type was not erased at
runtime.

- Oliver
 
M

Mike Schilling

Danno said:
Right now I think they suck especially when working with libraries that
are older than tiger. That is my opinion.
What is your vote on generics?

Warning: I can't use them in production code, because that has to work
inside containers running 1.4, so this opi ion is based on reading about
generics and playing with them, not extensive use.

To the extent that generics can be used without thinking about them, e.g

List<String> list;
String string = list.get(0);

they're good. They make collection types self-documenting, make code more
readable, and remove the need to insert casts into many common idioms.

Once you have to think about generics, though, they're ugly. For instance,
suppose I'm implementing a generic List, rather than using one, and I want
to allocate an array of members:

import java.util.List;

public class MikesList<E> implements List<E>
{
private E[] members;

public MikesList(int size)
{
// Note that this causes an unchecked warning
members = (E[]) new Object[size]; // *

}
...
}

Why in the world can't the starred line be written as

members = new E[size];

Yes, I'm aware that the type E isn't really known at runtime, and that the
result would be to create an array of Object anyway, but why must the bones
of the generics implementation show through this way?
 

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

Members online

No members online now.

Forum statistics

Threads
473,812
Messages
2,569,694
Members
45,478
Latest member
dontilydondon

Latest Threads

Top