Skip to main content

Runtime Type Safety and Inheritance for Generics - what they should've been

50 replies [Last post]
patrikbeno
Offline
Joined: 2004-10-11
Points: 0

First of all, sorry if this is a duplicate idea, I did not have enough time to go through all forum.

Currently, generics are just syntactic sugar (more or less). We all know that.

Parameterized object instances and declarations should retain their type parameters at runtime. Instead of explaining why, I give you an example:

When you say new ArrayList() you mean new object contains Strings and nothing else. Parameterized class should include checkcast wherever type parameter is used to enforce ClassCastException.

Rationale behind this suggestion is clear:
If we can do this:

<br />
   Object obj = "hello";<br />
   Number num = (Number) obj; // throws CCE<br />

we fooled compiler but not runtime (we get ClassCastException). Then wen should be able to do this:
<br />
   Object obj = new ArrayList();<br />
   List nums = ( List ) obj; // should throw CCE<br />

and we should get CCE again at runtime (although we fooled compiler again).
Another:
<br />
   List l = new ArrayList();<br />
   l.add(new Integer(1)); // should throw CCE<br />

Yet another: inheritance.
You cannot do this:

<br />
List numbers = new ArrayList();<br />

Why oh why? if you can do
Number n = new Integer(0)
why? Who understands this?

Just because compiler cannot guarantee type safety and data integrity (dangerous add() on downcasted variable?) Oh my! so let runtime handle it and you don't need such stupid restrictions! Did you left hand knew what right hand was doing???

Definitelly this is a must: runtime type safety for Java genericsm, full logical inheritance in well-established and well-understood context

I don't care what it costs or what Sun dreamt about while designing generics. All I know is that current solution is poorly designed, ambiguous, and no newbie will ever understand that - and it definitelly stands in the way of wider adoption of generics.
If you need to change JVM, do it. If you need to change bytecode, just do it. Don't hesistate.

But finish things properly.

Bugs can be fixed. Design flaws usually cannot. They become must-live-with legacy features Is this what you want?

--
Patrik Beno
J2EE Software Architect
_____________________________________________
Cleverlance - The Clever Enterprise Solutions
www.cleverlance.com

Pod Pekárnami 7/161
190 00 Praha 9
Czech Republic

phone: +420 266 177 166
mobile: +420 608 040 445
fax: +420 266 177 155

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
afreije
Offline
Joined: 2004-10-14
Points: 0

Did you all read [url http://www.cs.rice.edu/~eallen/papers/nextgen-final.pdf]the NextGen proposal[/url]?
The solution they present is (very abbreviated):
- parameterized classes are abstract;
- instances of paramterized types use implicit subtypes, almost like
[code]class ListPerson extends List() {}[/code].
These subtypes are 'normal' types and therefor fully runtime type-safe.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Just reviewed it quickly. But it much better than erasure.
However:

1) AFAIK it does not support static context:

static T whatever(T t) {...}

2) for every parameterized type new class is introduced. That's unnecessary overhead. can be done better. In Java6 I hope ;-)

vhi
Offline
Joined: 2004-10-11
Points: 0

See "Re: Runtime Type Safety and Inheritance for Generics - what they should've" above. I wrongly linked my reply to this message.

Message was edited by: vhi

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

to what? paste a link

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

I must have missed this post :-(

[i]> I've been in situations in the past where known bugs could not be fixed because people came to rely on the "buggy" behavior. To fix the bug would be to invalidate years of work carried out by many people. The bug was documented, everyone knows about it but it still exists to this day. Have we released a "bug" in the language? A language is a fundamental interface to your computer. We come to rely on a consistant behavior from certian syntax no matter the incongruancies in the syntax it's self. Now that an inconsistent implementation of generics has been introduced, the worst thing that can happen is that you change it and break that contract.[/i]

I understand. But we are lucky :-)
Since generics (current impl.) cannot rely on runtime support, they were designed to be far too restrictive on the compiler/source level.
This means, that people using Java 5 generics will NOT be able to do a lot of things that might(ought!) be allowed in Java 6. In turn, this also means that there's nothing to break (on source level).
I believe that every [b]reasonable[/b] line of code written with Java 5 generics can be safely compilable and executable with Java 6 genereics (as I see it).
However, since Java 5 generics are flawed, compiler can be fooled and runtime is helpless, we sure are able to write unsound example that just won't run on Java 6.

I mean this:
[code]
List list = new ArrayList ();
doWhateverYouWantWithThis( list ); // I don't care
return;

...

/* elsewhere and everywhere else :-) */
list.add(new Double(1.0)); // must fail on Java 6

[/code]

If created [i]ArrayList[/i] is never ever used as List but is instead treated as List everywhere else, first call to [i]add(new Double(1.0))[/i] must fail under Java6 but executes smoothly under Java5.

However, despite the fact that this code gets broken in Java 6, it cannot be considered a Java5 bug workaround or whatever. It is just horrible nonsense and misuse. And as such, It does not deserve support of backward compatibility. Such misuses are deprecated even now.

[i]> What I have trouble understanding is the need to enforce type safety at any cost. IME using composition not only offers type safety, it is a better design choice as you are now using types that are symantically significant to your domain than ArrayList is.
> ...
> One final point, the generic syntax is akin to the dreaded Hungarian notation. Change the type and now you have to trolling through your code changing all of declarations. This is a clear violation of DRY supported at a very funidemential basis.[/i]

i think i already wrote about this.
Type safety that generics offer (or should) is just worth the effort and price.
Composition is fine for some scenarios but unsuitable for plenty others.
If you change type parameter, you not only need to change all declarations but all places where you use that variable/declaration. This problem is the same with or without generics. not an issue. (And note that you can declare new type simply: [i]class Customers extends ArrayList {}[/i]

[i]>I have to humbly disagree that features should only be useful for experts[/i]

This is not what I tried to say. Point is that current implementation of the generics is so complicated to understand (especially with respect to other language features) that newbie might just get lost and gives up...
It is not syntax, that's ok, It is the whole behaviour.
once they (generics) will be cleaned up, there will be no problem with them, not even for newbies. They will be no more complicated than arrays. [b]!!![/b]

[i]> Look at VisualBasic. It too is a fairly simple language but look at how productive it has allowed people to be. Compare that to C++. C++ is a very powerful and very useful language. But it is very complex and you have to have a good grasp of the syntax (which is not an easy thing to do) in order to be productive.[/i]

yes. But consider the fact that VB with its simplicity allows you to be productive only to the specific level of problem complexity. c++ seems to be much more suitable for
very complex problems. I have heard a few times "let's do it it VB, that would be great for this" and I also heard people saying "No, don't even think about VB for this, we need something more flexible... we need c++"

[i]> From my perspective the message is clear; we should be moving away from complexity, not towards it if we want to improve developer productivity[/i]

First of all, generics simplify things. However, complex problems have their requirements. Java is already used to solve many complex problems, it's not a toy for kids. Generics are evolution and even if you never felt a need for them, believe that they were introduced because of a sound and loud public demand, not just because javac guys had no job to do. (Well as I see it, I would gladly fire a few :-))

wangzaixiang
Offline
Joined: 2004-11-24
Points: 0

> You must be kidding! Generics is the best improvement
> since Java was invented (although not perfectly
> implemented)!

Yes, Generics is very important than static import or enhancement loop, but a good idea is not a good implementation. the implementation in Tiger is very abnormaly. Take a look at the .NET's Generic.

subanark
Offline
Joined: 2004-11-26
Points: 0

As I remember .NET's generics creates a new Type for every combination of different generic parameters.

This is a overhead, that happends wether you want it or not.

The sun developers insist to minimize the penality occured to people that don't use a spefic language features.

tsinger
Offline
Joined: 2003-06-10
Points: 0

Hi kcpeppe,

You must be kidding! Generics is the best improvement since Java was invented (although not perfectly implemented)!

But I'm sure, you mean auto(un)boxing. Yes, this is the worst "feature" in Java 1.5 and should never have been introduced.

Tom

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> Hi kcpeppe,
>
> You must be kidding!
I'm dead serious
> Generics is the best improvement
> since Java was invented (although not perfectly
> implemented)!

ok, how has it improved my life?

>
> But I'm sure, you mean auto(un)boxing. Yes, this is
> the worst "feature" in Java 1.5 and should never have
> been introduced.
Some people believe that auto-boxing is a great feature. I predicted over a year a go that auto-boxing would have devistating effects on performance and... it has. It's been introduced to act as a bandaid or cover over the fact that int, float, char... etc are not first class objects. In fact, they are not objects at all which now leads to the problem of where to put sin, cos and all of those wonderful math functions that we like see but never use ;)

But this thread is not about auto-boxing so I'll not comment on it.. darn I already have!!!

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

What generics should have been is left out of the language! This thread is a clear indication that how generics work and how they should work have neither been clearly thought out or reconciled. Too bad that generics have been dropped on us by the "Java Gods" instead of having an open honest discussion about what the real requirement is and if generics are really a bandaid or a solution. IMHO, they are a bandaid and a pretty grotty one at that!

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Generics cannot be left out of the language. they cannot be left out of the JVM neither. Reasons are mentioned in this discussion.

Well you probably should clarify:
- do you agree with this proposal? any constructive comments?
- or are you just all against the whole generics thing, whatever it means?

Generics are grotty because they are not what they should have been.

However, I consider them still useful for a Java expert who is aware of the generics' dark side. It is a good start and accidentally, it can be improved without breaking backward compatibility, I still believe.

Message was edited by: patrikbeno

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> Generics cannot be left out of the language. they
> cannot be left out of the JVM neither. Reasons are
> mentioned in this discussion.
> any constructive comments?

I think your overall analysis of generics is good. In fact so good that it points out the folly of modifying a language without taking careful consideration of the impact of the change. Clearly the type of analysis that you did should have been before they were released so that we might identify problems at a time when we could have made a difference. These many inconsistencies should have been a signal that something is not quite right and we need to pull back and continue to think about the problem. Certainly not having a feature in the language (after all I've managed quite nicely without generics for 7 years) is better than having a mangled version of it.

I've been in situations in the past where known bugs could not be fixed because people came to rely on the "buggy" behavior. To fix the bug would be to invalidate years of work carried out by many people. The bug was documented, everyone knows about it but it still exists to this day. Have we released a "bug" in the language? A language is a fundamental interface to your computer. We come to rely on a consistant behavior from certian syntax no matter the incongruancies in the syntax it's self. Now that an inconsistent implementation of generics has been introduced, the worst thing that can happen is that you change it and break that contract. Granted not that many people will have that much code at this point in time but by the time the fixes would be put into play that picture will have changed. Alternativly, if people know that generics will change, the may not move from the 1.4 to the 1.5. IMO, if one wants to play and introduce experiential features it should be done in an experimental language, not a production ready one. Java is a production ready language. People have now bet the farm on it's consistancy To be changing the rules at this stage of the game is to risk that stability. But now that the train has left the station, we shouldn't be talking about further aggravating problem but introducing yet more stuff that has not been so well thought out and tried in an experimential language.

IMHO, this is constructive criticism.

> - or are you just all against the whole generics
> thing, whatever it means?

Lets say that I'm very wary of generics as I think that there are several fundamental problems with them and I think that there are perfectly good alternatives that people seem to be ignoring. And as I said previously, the new features in the language have not really been tried and tested. Up til now, the most significant features in the language have some history (as I've pointed out in my editorial Janurary 2004 JDJ).

I can understand the desire to include old familiar features in this new language that you are now using. When I first started coding in Smalltalk after coming from C I thought, wouldn't string manipulation be more efficient and a lot easier if we had pointers? Once I discovered how to best exploit the elegance of the language, I quickly lost my desire to use pointers. The leason that I learned and applied when I moved to Java was to take to take the time to learn how to work with a language. This is not to say that a language shouldn't change. Its to say we need to understand what makes the langauge work so that when we change it we don't interfere with those properties.

What I have trouble understanding is the need to enforce type safety at any cost. IME using composition not only offers type safety, it is a better design choice as you are now using types that are symantically significant to your domain than ArrayList is.

IMO, the generics syntax is a wash. Though it does make the code some what cleaner when working with an iterator, the improvement is not free. You pay for this improvement by having having to type more typing information other places. So, the problem has not been eliminated, just shifted. This shift is magnified if you follow what is considered to be good OO style and write short methods. As and aside, HotSpot, being tuned against good OO style, will perform more optimizations against short methods than it can against long one.

One final point, the generic syntax is akin to the dreaded Hungarian notation. Change the type and now you have to trolling through your code changing all of declarations. This is a clear violation of DRY supported at a very funidemential basis.

>
> Generics are grotty because they are not what they
> should have been.
>
> However, I consider them still useful for a Java
> expert who is aware of the generics' dark side.

I have to humbly disagree that features should only be useful for experts. One of the beautiful things about Smalltalk was the simplicity of the language. In fact, you can teach a programmer Smalltalk syntax in about 30 minutes. On that standard, Java is immensely more complex. Just look at the Java Specialist newsletter produced by Dr. Heinz Kabutz. There are questions regarding syntax that Heinz raises in which he is finding that maybe 3 out of 10 of his readers understands. It is my belief that the number would be closer to 1 out of 10 if the entire Java developer community was considered Still syntacticly, Java is quite simple if you consider the alternatives. It is this simplicity that has allowed us as programmers to be productive. Look at VisualBasic. It too is a fairly simple language but look at how productive it has allowed people to be. Compare that to C++. C++ is a very powerful and very useful language. But it is very complex and you have to have a good grasp of the syntax (which is not an easy thing to do) in order to be productive.

From my perspective the message is clear; we should be moving away from complexity, not towards it if we want to improve developer productivity. IMHO, generics are not moving the language in the right direction and introducing them as they have is a dangerous experiment because once we've seen the effect we won't be able to put the gene back into the bottle.

vhi
Offline
Joined: 2004-10-11
Points: 0

There was already an experimental language before Generics was implemented. It was Generic Java.

Java is not a completely OO, message-based language like Smalltalk. It is actually a cross between efficient C++ and some OO concepts of Smalltalk. Therefore, there are somethings (like compile-time type safety) that Java promotes. Generics fit into these easily. Some people like compile-time type safety, and therefore like Generics. It is not about whether there are real world scenarios when there would be class cast exceptions, it is about what developer FEELS when he writes the code. I *feel* safer when I use generics. I KNOW that people cannot put incompatible objects in my container. A language should be closer to the developer's mentality. Developers that like C++, Java are a bit reluctant to leave checkings to the run-time. They prefer compiler errors than run-time errors. One should take up a language that matches one's thought or one should adapt their thoughts to the language they choose. I, along with scores of others, feel that Generics are compatible with the philosophy that Java promotes.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

I am just wondering what is real value of List declaration?
You can use it as any object unless you attempt to use its type parameters (which are not known)

What we have is a variable with partial information. Useless variable.

List or List

declarations are more useful than List (what are they called, wildcards?). i say declarations are here because we don't have runtime support for generics. If we had it, we would not need it Message was edited by: patrikbeno
patrikbeno
Offline
Joined: 2004-10-11
Points: 0

First of all, comparing obejcts by pointer is a bad practice. so we may be tempted to feel just in not supporting it. ;-)

Anyway, suppose it should be [i]ta.equals(tb)[/i].
Hmm...

No. I think when you call getClass() on parameterized object (ArrayList ), you still receive the same plain old ArrayList.class. Now we're compatible. I.e. List and List share the same class.

This is logical because their type parameters are not stored in the Class object. they are associated with the instance.

So if we want to determine type parameters of a particular instance, we need to do something like this:

[code]
List list = new ArrayList();
Class.getTypeParameters(list);
[/code]

Now we're backward compatible and still have new features.

Found any design flaws?

Message was edited by: patrikbeno

mthornton
Offline
Joined: 2003-06-10
Points: 0

While you must take care when comparing pointers the documentation for Class objects is quite specific --- there is exactly one instance for each unique class. Thus as the language is currently defined using == is legitimate for Class objects.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Cannot agree. The fact you mention is handled in Class.equals() but you should not rely on this in your code.

Class.equals() is inherited from Object and Object.equals() says: return (this == obj);

But when you compare Class objects, you should still use equals(). It's what equals() was designed for.

Nevertheless, I think my proposal can live with ==, too, don't you think? :-)

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

As for compatibility:

List == List

List == List Map == Map Map == Map Any reference to non generic type is expanded automatically to its minimum parameterized type. At runtime. Automatically. Without source rewrite, without recompile... What possibly could break something? Give me an example :-)
mthornton
Offline
Joined: 2003-06-10
Points: 0

What about the Class objects?
Class ta = new ArrayList().getClass();
Class tb = new ArrayList().getClass();

Is ta == tb

If not, then some existing code would be 'surprised'.
On the other hand if they are equal then the type parameters have to be recorded somewhere else. Not impossible, but it needs some work. The harder bit is dealing with parameterized methods in a backward compatible way.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Good point. Please give me another one (something with those parameterized methods.

meanwhile I will think about this one :-)

mthornton
Offline
Joined: 2003-06-10
Points: 0

With regard to arrays vs generics, it is arrays which have the anomalous behaviour (it has always been odd, though undoubtedly convenient). As you note the array behaviour requires a runtime check to ensure safety.

Now while it would be nice to have the actual parameter types available at run time, doing so makes backward compatibility diffult or impossible. Creating extra Class objects to represent instantiated generic types wouldn't break too much old code, but for consistency you would also have to do it for parameterized methods and this would require an extra hidden argument. So what happens if you make an existing interface generic, you get methods with different signatures and thus old code no longer works without at least a recompile (so all your existing JDBC drivers are broken until the supplier catches up). If you look at C#, you will see that they didn't change existing classes, but instead created a whole new set which entails other problems in mixing generic code with old code.

So, at least for now, we can't automatically get the actual type information. Perhaps in a future version, when there is little non generic code remaining, it will be easier to add support for run time type information. The generic information in the byte code might allow existing compiled code to be converted and thus avoid the wait for the supplier.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

You must be kidding. Arrays just behave as they should. Necessity for runtime type checking is a price we pay for proper behaviour. It is not fair to say arrays behave oddly :-(

Saying that something is difficult does not allow you to say it is wrong.

I think all we need is that runtime would interpret type of raw non-generic List instances as List

. I.e. runtime would automatically convert non-generic type references to generic references with default types. This may introduce runtime incompatibilities (but taht's the point of runtime type checking, isn't it?) but bytecode and source code compatibility would be preserved. Old method: Object get(Object key) is generified as new: V get(K) but at runtime (bytecode), single method is available: Object get(Object) Difference is, that runtime needs to check type parameters before passing them into the call, and after return. The only difference, no changes to byte code or source code. Effectivelly, this happens: [code] Map map = new HashMap(); Object key = "hello"; Object value = new Integer(0); map.put( String.class.cast(key), /* or simply (String) key; */ Integer.class.cast(value)); /* or (Integer) value; */ Integer result = Integer.class.cast(map.get(key)); /* or (Integer) map.get(key); or nothing since checkcast is enforced by assignment to Integer variable; */ [/code] Class.cast() calls are inserted automatically by runtime as determined from the actual type parameters of the called parameterized object instance. This is not code inserted by compiler. JVM does this. This is perfectly backwards compatible. [b]Please, I know this is not easy task. But give me an example of real problem[/b] and we try to find a solution (or consider it unsolvable now). Message was edited by: patrikbeno
regexguy
Offline
Joined: 2003-06-20
Points: 0

This comment comes up frequently. The user euxx made a proposal on how we might fix it. I have taken the trouble to try and write it up in a little more detail to see (1) if there is anything wrong with it and (2) maybe to get it considered for java 7.0 (or whatever).

Reasons why generics need erasure:
==================================

Summary from Neal Gafter's blog: http://gafter.blogspot.com/

1) Don't want to introduce overhead. Each object/method should not have
to have a pointer to an array of type information -- an extra pointer
per object is too much overhead.

2) Must preserve meaning of .class, .getClass(), instanceof must
continue to work, etc.

The code:
List li = new ArrayList();
List ld = new ArrayList();
if(li.getClass() == ld.getClass()) {
System.out.println("a");
}
if(ArrayList.class == li.getClass()) {
System.out.println("b");
}
if(ld instanceof List) {
System.out.println("c");
}

Should print out "a", "b", and "c". However we try to
implement runtime generics we should preserve this.

3) Array store checks: If you allocated something with new T[]
inside a method, how would you do array store checking?

Array store example:
Number[] n = new Double[3];
n[0] = new Float(3.14); // runtime exception

The reason people want runtime data:
====================================

People want to write things like:

X[] makeArray(List list) {
X[] xa = new X[list.size()];
for(int i=0;i xa[i] = list.get(i);
}
return xa;
}

Also they may want to use reflection to
dig out type information for logging, etc.

How can we get there?
=====================
User euxx has proposed, I think, a brilliant solution. Still
haven't seen a post where someone found something wrong with it.

Each object already has a pointer to its class, this object
is retrieved with a getClass() method.

We could modify Class[] so that it contains an array of types:
specifically a field named "types" of type Class[]. ArrayList.class
would return an instance of Class where this field is null.

The code:
List x = new ArrayList();

would result in dynamic creation of a new Class object. There
would be a copy of ArrayList.class with the type
array set to Class[]{Integer.class}, and it would have its super
class reference filled in with the class it was copied from.

The class pointer inside object x would point to this new object,
but it's getClass() method would return the subclass -- the
untyped class -- for backwards compatibility.

A new static method, Class.getTypes(Object o), could be used
to retrieve the typed class and its types array.

This implementation seems to preserve backwards compatibility
without introducing any significant overhead. The only overhead
is:
(1) getClass() method is slightly slower and
(2) one addition object, a copy of ArrayList.class with a
types array filled in is created in memory.

wangzaixiang
Offline
Joined: 2004-11-24
Points: 0

I cant understand the Tiger's Generic design at all, just like the discuss in this thread:

Number ns[] = new Integer[10]; // compiles OK

List ns = new ArrayList(); // compiles error

and there is a lot of troubles on the semantic on Generic, I am just a engineer, not an expect or professor, but i would not design any conflict code. Why the professional/expect team and a serious Java version can produce such a thing? I cant understand.

For me, a normal java programmer, i cant see any reason why not implements the Generic in Runtime-Type-Safety mode? if the compiler can provide more, it is good, but the runtime must works correctly, how can you accept that add a Float to a Integer-List and it report no error?

Also, I dont think there is any difficult on implements a Runtime-Type-Safe Generic, I can tell the expert how to do it.(I dont know how .NET do it, but i think the expect can learn). the JVM require no modify at all(but of course, a JVM implementation can be optimized for unnessary casting on most case, that will improve some speed than the current). We need only support it via the compiler.

Given a class HaspMap, we can add 2 field in the Hashmap, eg, KType, VType type as Class, and when we new instance HashMap, we assign values for these field.

then put(K, V) method(the realy signature is still put(Object key, Object value) first check key instanceof KType and value instanceof VType.

Oh, will this be slow than the Tiger's Generic? yes if your dont optimize the JVM implementation, but i think the cost is not as serious as the profit. also, it can be optimized. and more, with a optimized JVM, the call map.get(key) which signatured as return a Object, require cast to VType in Tiger, can also be optimized in most case. It means that a optimized JVM can run type safe generic fast than unsafe generic.

All the cost is now, a generic instance is some fat than original, but only 4 or 8 bytes, is that the problem?

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Suppose you have class with two methods:

#1
void message(Object obj) {...}
#2
void message(Collection animals) {...}

and you do this:

message(new Object()); // calls #1
message(new ArrayList()); // calls #2

What happens? Which method is called in line #2? Both signatures do match. JLS solves this. See [i]JLS 15.12.2.2 Choose the Most Specific Method[/i]
It just works.
Now let's change the call #2 to use generics:

message(new ArrayList()); // calls #2

Fine, without change :-) Let's generify method #2 a little bit. Due to erasure, runtime signature of the method remains the same.

#2
void message(Collection animals) {...}

And gee, what happens:

message(new ArrayList()); // calls #1
message(new ArrayList()); // calls #2

[b]I understand why it is so, no need to explain.[/b] I only say this is weird, it is bad design. I would prefer consider this a bug since it breaks JLS. Must be fixed. And I would not worry about backward compatibility (it's a bug)

Check it out yorself, play a bit and wonder :-|
[code]
class Animal {}
class Dog extends Animal {}

public class Weird {
public static void main(String[] args) {
new Weird().message(new Object());
new Weird().message(new ArrayList());
new Weird().message(new ArrayList());
}

private void message(Collection animals) {
System.out.println("You gave me a collection of animals.");
}

private void message(Object object) {
System.out.println("You gave me an object.");
}
}

[/code]

tim12s
Offline
Joined: 2004-02-02
Points: 0

I think this is a great idea. Furthermore, I think the fact that it should throw a CCE only shows that it is a programmer error and would significantly aid in software testing. (catch more bugs sooner).

I would propose that if this were implemented and that it had a significant performance impact, then this impact should be addressable by having a parameter similar to "-enabledassertions", "-enableruntimegenerics".

I understand that where indicates a read-only collection (any functions may return Number but parameters cannot be passed in as arguments).

If this were extended, such that it is legal to add inherited/extended classes of Number to such an exception, then -enableruntimegenerics should be a reasonable runtime option.

I think any code that depends on a CCE being thrown has serious issues but, following good development practices, catching issues earlier can only help.

Realisticly, because they did not support the passing of parameters into ? based generics, the simple List should have been sufficient IFF they had runtime checking.

-Tim

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

As you say, if we had runtime support for generics, construct does not make sense (is not needed because it is fully compensated by .

I am not afraid of performance at all. Maybe I try to elaborate on this a little bit but I have no time right now. But even if there would be any considerable performance loss, it is worth safety, it just pays off.

One more note to the association Whatever == [b]readonly[/b] Whatever: it depends on design:

[b]1)[/b] If the Map was defined as follows, Map would be readonly:

class Map {
V get(Object key); // note: Object, not K
void put(K key, V value);
}

map.get() is permitted, map.put() is not

[b]2)[/b] it does not make sense to ask Map for get("string") because it may not contain Strings. Idea is that if you ask get("string") from Map, you are doing something wrong and you've better be warned (CCE) instead of returned null; Example where this can be dangerous:

Map map = (Map ) rawmap // declared somewhere as Map
Something st = map.get("key"); // this always returns null because this map does not contain String keys (they're Integers) but you are never warned that you are asking the wrong object.

So, let's define Map as follows:

class Map {
V get(K key); // note: not an Object, but K
void put(K key, V value);
}

with this declaration, Map variable would be completely useless; neither get() nor put() is permitted.

[b]3)[/b] And finally, even if this example makes no sense, consider this:

class Map {
V get(K key);
void put(Object key, Object value);
}

this is writeonly object with Map variable: get() not allowed, put() allowed

[b]Conclusion:[/b] java.util.Map was designed to be useful at least in readonly mode with Map. But if it was not (get(K) instead of get(Object)), it would be useless.

[b]Another generics paradox:[/b]

java.util.Map declares [i]V get(Object)[/i] and [i]void put(K,V)[/i]. And the language paradox is, that:

Map m;
m.put("key", "value") // forbidden V is unknown (

but
Whatever w = (Whatever) m.get("string"); // allowed despite the fact that V returned by get() is unknown.

If type parameter of the method argument is unknown, compiler does not allow the call.
If type parameter of the returned value is unknown, compiler does not care, it allows the call.

Do you see the difference?
Do you see inconsistency in the language?
Got the point?

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

>
> Got the point?

Sadly no. :|

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

since you do not ask for clarification of anything, I tend to believe you just don't want to :-(
prove me wrong :-)

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> since you do not ask for clarification of anything, I
> tend to believe you just don't want to :-(
> prove me wrong :-)

*sighs*... if all we have is rhetoric then we certainly don't have much to stand on do we.... I'm afraid my last comment was pure un-adulterated rhetoric and since I do believe that I do have something to stand-on... I'm going to have to take you up on this ;)

Ok, the question that I've never been able to get anyone to answer is why do they feel the need for generics. I'll ask it here again as a starting point for a more meaningful discussion.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

So this is how it is. If you can't see any benefit in generics how can one expect you would understand the necessity of this proposal...

Well, ok, back to your question. Need for generics. I'll try to be very very brief:

1) better data modelling capabilities. when you see List, Map, Set in some API, you know nothing about what data these structures should contain. Javadoc may or may not specify this, or may be out of date. While coding, it just a matter of time when one makes a mistake and puts unexpected object into unexpected collection because compiler is helpess here. If you don't see collection's subtype, tools do not see it, too. This leads to additional 'deployment' descriptors etc.

Example may be JDO (Java Data Objects, solution for transparent persistence): if you have variable [i]List persons[/i] which is supposed to contain [i]Person[/i] instances, you have to say this somehow to JDO implementation - you mention this in additional XML descriptor file. With generics, you say [i]List persons[/i] directly in the code and you're done.

2) Better readability and more simple maintenance: if you see variable [i]Map map[/i], you have to figure out from code what data types does it contain (ok it may have been commented - or may not). On the other hand, when you see [i]Map>[/i], you know all you need from the delaration. It may seem a little verbose and it is. But there's nothing redundant, it contains only information you need.

3) Increased type safety: compiler does not allows you to do put(new Integer(0), "hello") into Map.

4) Less verbosity while using generified variables - no casting needed: with [i]Map>[/i] you can do e.g. [i]map.get("myfiles").get(0).getAbsolutePath()[/i]

And more... But I believe these are primary benefits.
I hoped for runtime type safety of generics from the first time I have heard about them. I am very dissappointed now, generics are just broken and not in sync with all the language.

I believe we can change this.
And I believe you now understand why generics are so great (well, they will be :-))

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> So this is how it is. If you can't see any benefit in
> generics how can one expect you would understand the
> necessity of this proposal...

Humm, What I'm trying to do is return to first principles. Right now we are focused on a solution. The question is, what was the problem? And if there is a real problem, what are the solutions available and what are the cost/benefits of each. Should we proto-type each of the solutions to validate our analysis and so on. The first set is to make sure that we understand the problem.

>
> Well, ok, back to your question. Need for generics.
> I'll try to be very very brief:
>
> 1) better data modelling capabilities. when you see
> List, Map, Set in some API, you know nothing about
> what data these structures should contain.

In other words, these Classes define how they will operate work without defining what they will operate on.

> may or may not specify this, or may be out of date.
> While coding, it just a matter of time when one makes
> a mistake and puts unexpected object into unexpected
> collection because compiler is helpess here.

So we are anticipating some future problems because we have not specified what a collection will operate on.

If you
> don't see collection's subtype, tools do not see it,
> too. This leads to additional 'deployment'
> descriptors etc.
>
> Example may be JDO (Java Data Objects, solution for
> transparent persistence): if you have variable
> [i]List persons[/i] which is supposed to contain
> [i]Person[/i] instances, you have to say this somehow
> to JDO implementation - you mention this in
> additional XML descriptor file. With generics, you
> say [i]List persons[/i] directly in the code
> and you're done.

Is this a fault with Java or with the JDO?

>
> 2) Better readability and more simple maintenance: if
> you see variable [i]Map map[/i], you have to figure
> out from code what data types does it contain (ok it
> may have been commented - or may not). On the other
> hand, when you see [i]Map>[/i],
> you know all you need from the delaration. It may
> seem a little verbose and it is. But there's nothing
> redundant, it contains only information you need.

On spot maintenance it may be simpler but you lose on overall maintainability and reusability. So we have a trade-off in which the winner is not clear to myself.
>
> 3) Increased type safety: compiler does not allows
> you to do put(new Integer(0), "hello") into
> Map.

Assumes that you need this type safety. If you use raw exposed collections, then you most likely would like this feature. IMHO, using raw exposed collections is not a good practice.

>
> 4) Less verbosity while using generified variables -
> no casting needed: with [i]Map > List>[/i] you can do e.g.
> [i]map.get("myfiles").get(0).getAbsolutePath()[/i]

At the cost of more vebosity at the time of declaration. The cost of this verbosity cannot be recouped if you follow (what is considered to be good) OO practice of witting short methods.
>
> And more... But I believe these are primary benefits.
>
> I hoped for runtime type safety of generics from the
> first time I have heard about them. I am very
> dissappointed now, generics are just broken and not
> in sync with all the language.

Again, this is a good reason to think these things out, proto-type them and really test them before exposing them into a production ready language.

So far all of the problems you've listed can be solved with much simpler solutions. JDO could use annotations though this is not without it's dangers. It seems to me that a lot of explicit type casting is unnecessary as the compiler typically has enough information to perform the cast without the need to me to provide it with extra information. Even with the new for loop, you are forced to use generics even though it is crystal clear what the typing is suppose to be.

All of the other problems are covered if you do not use exposed collection. I can't say that I've seen a collection exposed directly in an API. Maybe you can offer an example. That said, IMHO, it is not good practice to use an exposed collection nor is a wise to use heterogeneous collections. Better practice is to use composition to encapsulate the collection. The reason for this is collections offer not symantic meaning in any of the problem domains that I work in. The objects that display collection like behavior (like Contacts, or Customers) typically also require some domain specific behavior also. That said, Customers has meaning and as you've quite correctly pointed out, ArrayList (or HashMap) does not. So composition increased readability by providing a symanticly correct entity into the domain. And interestingly enough, we get type safety as a side effect of using this design pattern. No need to worry about extra syntax ;)

Lets find all of our customers that have an outstanding balance.

=== 1) Pre-generics situation ===

class Customers {

private ArrayList customers;

public Customers() {
this.customers = new ArrayList();
}

// methods are missing

public Customers findCustomersWithOutstandingBalance() {
Customers customersWithOutstandingBalance = new Customers();
Iterator iter = this.customers.iterator();
while ( iter.hasNext()) {
Customer customer = (Customer)iter.next();
if ( customer.getBalance() > 0)
customersWithOustandingBalance.add( customer);
}
return customersWithOustandingBalance;
}
}

==== 2) Generics ====

public class Customers {

private ArrayList customers;

public Customers() {
this.customers = new ArrayList();
}

// methods are missing

// Here we have a choice. I'll choose the way recommed with generics.
public ArrayList findCustomersWithOutstandingBalance() {
ArrayList customersWithOustandingBalance = new ArrayList();
Iterator iter = this.customers.iterator();
while ( iter.hasNext()) {
Customer customer = iter.next();
if ( customer.getBalance() > 0)
customersWithOustandingBalance.add( customer);
}
return customersWithOustandingBalance;
}
}

==== 3) Wouldn't this be nice?

class Customers {

private ArrayList customers;

public Customers() {
this.customers = new ArrayList();
}

// methods are missing

public Customers findCustomersWithOutstandingBalance() {
Customers customersWithOustandingBalance = new Customers();
Iterator iter = this.customers.iterator();
while ( iter.hasNext()) {
Customer customer = iter.next();
if ( customer.getBalance() > 0)
customersWithOustandingBalance.add( customer);
}
return customersWithOustandingBalance;
}
}

I have type saftey in each case but it took me a lot less work to achieve it in case 1 and 3 and much less to use it in 3. Sorry but just as I do see the problems that you are trying to address, I just don't see that the extra complications and problems that come with generics are worth the benefit.. which IMHO is marginal at best!

>
> I believe we can change this.

I think the gene is out of the bottle :(

I don't think that I'm being unreasonable when I say that you've still not provided a problem that says.. yes, generics are the answer! Can I suggest that we start again from first principles? What are the problems in Java that we need solve? I can think of a couple but type safety in collections is not one of them.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

[i]> In other words, these Classes define how they will operate work without defining what they will operate on.[/i]

Yes. And we need two things:
1) parameterize instances of these classes - tell them what data they are supposed to operate on
2) compiler support to catch as many errors as possible at compile time

While point 1 can be solved without generics, point 2 cannot.

[i]> Is this a fault with Java or with the JDO?[/i]

Not a fault. Limitation (of java).

[i]> ... but you lose on overall maintainability and reusability... [/i]

I don't know what you are talking about. Are you? Write a few more sentences :-)

[i]> Assumes that you need this type safety. If you use raw exposed collections, then you most likely would like this feature. IMHO, using raw exposed collections is not a good practice.[/i]

If you want typesafe collections, you must code them yourself. Why should I code myself or even generate whole bunch of different collection classes if they always do the same thing, just with different data?
I need simple thing: parameterize colletction instance.

[i]> At the cost of more vebosity at the time of declaration. [/i]

If you don't specify what data you operate on in declaration (List of Persons), you should write extra comment to clarify this. Otherwise you're producing less maintainable and less readable code. Not mentioning the fact that compiler does not verify your comment when it gets out of sync with code.
So just be honest, you cannot be less verbose without generics and maintenance cost is higher than with generics (maintenance of comments or extra docs).

[i]> Again, this is a good reason to think these things out, proto-type them and really test them before exposing them into a production ready language.[/i]

Agreed but this is not about whether generics are useful or not.

[i]> JDO could use annotations though this is not without it's dangers.[/i]

Annotations would be more verbose, less type safe, etc etc. Good workaround if you don't have generics, but generics are much better.

[i]> It seems to me that a lot of explicit type casting is unnecessary as the compiler typically has enough information to perform the cast without the need to me to provide it with extra information.[/i]

Yes but that's the whole point in Java: Compiler [i]could possibly[/i] do the casting for you but than you'd get ClassCastException at runtime that otherwise could be caught at compile time. Compiler requires explicit up-casting to warn you and you insert that type cast to tell compiler: Yes, I know what I am doing.
You might object that compiler should just generate warning instead of errors when cast is needed but with this you will end up with thousands compile-time warnings that nobody cares about (how would you recognize warning that matters and those that do not?).

[i]> I can't say that I've seen a collection exposed directly in an API[/i]

In libraries it is no so often. In [i]many[/i] end-user applications, they are used really frequently. (still talking about APIs).

[i]> Better practice is to use composition to encapsulate the collection[/i]

Yes there are some situations where this is really reasonable. And there are others where this is unneccesary overhead. Furthermore, List can be used wherever Collection is accepted (sorting, searching, lookups, comparisions, whatever). With your Customers encapsulating class you lose this flexibility and you have to provide workaround. Feasible, of course but unneccessary.

suppose I have a List of Persons that has no additional functionality compared to ordinary List. To make it type safe, I need to redefine add() method and insert explicit type cast. I cannot redefine get() to return Person instance so caller will still need to do typecast. I may even want to redefine contains() or remove() to verify that only Person instances are passed in etc etc etc

Too much hand-work for what should be achieved declaratively: List

And if you don't like using something like Map>, you can still create new class:

class MyMapOfListFiles extends HashMap> {}

...and you're done. No hand-crafted code-bloated proprietary sources (encapsulation pattern). Compare this to all your examples (not considering method findCustomersWithOutstandingBalance). Btw in your examples are readonly classes, you would need whole bunch of other delegate methods (add, remove, contains, etc)

Of course there are other (mostly just partial) solutions (annotations, bytecode generation or enhancement etc) but introducing generics into the language is the most reasonable one.

[i]> I have type safety in each case but it took me a lot less work to achieve it[/i]

No you just don't and you now see it, I hope :-)

Message was edited by: patrikbeno

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

Think the element that is missing from this discussion is the fact that people are saying, there is a problem with Java and here is a fix. The thing is, the onnius is on them to put for a compelling case that there really is a problem with Java. The burden of proof is on you to demontrate that the problem exists. IMHO, this has never been done. It's like here is a cool feature, wouldn't it be nice to have it in Java. It shouldn't work that way. Java is (or was) a stable production ready language. Granted it's not perfect and there are changes that could be made to improve the situtation. But, the burden of proof that a problem exists and the proposal will fix the situation lie with you! Further more, the barrier for acceptance needs to be set pretty high because, we are dealing with a production ready language.

IMHO, no one has done any of these things. Further more, the changes (according to your own analysis) have made the situation worse! Now, you (the pegorative you ;)) go on to make your points, the same points that I've heard over and over again for quite some time and you've (see last quote) ignored the code fragments that I've provided that say, hey... this particular issue is not really a problem! Certianly not one that rises to the level needing to experiment in a supposidly production ready release! This is extremely dangerous thinking.

> [i]> In other words, these Classes define how they
> will operate work without defining what they will
> operate on.[/i]
>
> Yes. And we need two things:
> 1) parameterize instances of these classes - tell
> them what data they are supposed to operate on
> 2) compiler support to catch as many errors as
> possible at compile time
>
> While point 1 can be solved without generics, point 2
> cannot.

I have achieved type safety is all 3 code fragments and at a lower cost in syntax in fragment 3.

>
> [i]> Is this a fault with Java or with the JDO?[/i]
>
> Not a fault. Limitation (of java).
>
> [i]> ... but you lose on overall maintainability and
> reusability... [/i]
>
> I don't know what you are talking about. Are you?
> Write a few more sentences :-)

Imagine if the JDK only had collections that were typed to contain String. How useful would that be? nuff said ;)

>
> If you want typesafe collections, you must code them
> yourself. Why should I code myself or even generate
> whole bunch of different collection classes if they
> always do the same thing, just with different data?
> I need simple thing: parameterize colletction
> instance.

Please reread my previous posting...

>
> [i]> At the cost of more vebosity at the time of
> declaration. [/i]
>
> If you don't specify what data you operate on in
> declaration (List of Persons), you should write extra
> comment to clarify this. Otherwise you're producing
> less maintainable and less readable code. Not
> mentioning the fact that compiler does not verify
> your comment when it gets out of sync with code.

please re-read my prevous posting.

>
> [i]> Again, this is a good reason to think these
> things out, proto-type them and really test them
> before exposing them into a production ready
> language.[/i]
>
> Agreed but this is not about whether generics are
> useful or not.

true but its a warning that we need to be cautious about the changes we make in a language.

>
> Yes but that's the whole point in Java: Compiler
> [i]could possibly[/i] do the casting for you but than
> you'd get ClassCastException at runtime that
> otherwise could be caught at compile time. Compiler
> requires explicit up-casting to warn you and you
> insert that type cast to tell compiler: Yes, I know
> what I am doing.

again, read about composition...

>
> [i]> I can't say that I've seen a collection exposed
> directly in an API[/i]
>
> In libraries it is no so often.
how about never ;)

In [i]many[/i]
> end-user applications, they are used really
> frequently. (still talking about APIs).

and as I said before, this is a design flaw with the application. People should really look at how things work in the JDK. It's not always the best but there is a lot pretty decient examples of how to work in there.

>
> [i]> Better practice is to use composition to
> encapsulate the collection[/i]
>
> Yes there are some situations where this is really
> reasonable. And there are others where this is
> unneccesary overhead.

*blinks*... ok, a major tenent of OO is that you work toward building a (symaticly correct) model of your problem domain. I can't recall the last time I saw a domain where ArrayList was symanticly correct. As for the comment on overhead, are you sure that you're not going after premature optimizations?

The point of doing OO is that

Furthermore, List can
> be used wherever Collection is accepted

not if it's typed (see comment above).

(sorting,
> searching, lookups, comparisions, whatever). With
> your Customers encapsulating class you lose this
> flexibility

replace lose with don't have and the same applied with a collection that has been typed with generics.

> suppose I have a List of Persons that has no
> additional functionality

how do you build it? where is it contained? I think these are behaviors that are specific to your domain and they do need to be encapsulated somewhere!

compared to ordinary List.
> To make it type safe, I need to redefine add() method
> and insert explicit type cast. I cannot redefine
> get() to return Person instance so caller will still
> need to do typecast.

generics does not remove the need for the cast, it just hides it.

> Too much hand-work for what should be achieved
> declaratively: List

I think my code fragments demonstrate that this is not true.

> No you just don't and you now see it, I hope :-)

I'm sorry to say that, what I feel from reading your repsonse that from this is that you want cool features without a real serious consideration of the problems and possible solutions. I think that you'll feel that I'm anti generics not matter what. So, I think I'm going to drop out of this discussion and just agree to disagree on this particular topic ;)

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

I agree that advocating generics does not belong into this discussion. This thread is for people who accepted the need for generics but want to improve them (finish them properly).

So let's not dilute this thread with our talks about the obvious (from intended readers' point of view).

If you don't mind, I would gladly continute our discussion on a private line and I believe that I am able to convince you not only about the fact that generics are (can be) cool feature but also they are very very useful addition to the language.

Feel free to drop me an email: patrik.beno@zmail.sk

fatihc
Offline
Joined: 2004-04-09
Points: 0

patrikbeno I do not agree with you. Yes, runtime generic type information would be good. But generic inheritance should never be the way YOU suggest it.

If it would, you though could not use it at runtime. You can not use the arrays that way also. It will throw an exception always (with array it is ArrayStoreException). It has sound reasons why it throws exceptions.

For the same reasons generic classes would be forced to check and throw exceptions. So what at all is the benefit of your suggestions??? The only thing, that happens will be, that poor programers wont understand how generic classes really work and use it the wrong way (getting the exceptions all the time).

Your suggestions will bring errors to runtime instead of compile time. And that is a bad thing!

I am pretty sure, that for example List will never be a supertype of List in Java. This can never work properly with imperative programing paradigms. If the language does not know mutable objects (such as functional languages), than it would be very safe to have List be a supertype of List. But Java is not a declarative/funtional programing language. It is object oriented, hence know "mutable" objects. Your suggestions are very dangerous here.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

I am pretty sure you just don't understand what I suggest. That just happens sometimes so don't worry :-)

Let me explain things once more, let's discuss :-).

[b]As for arrays:[/b]
You can safely do this:
Number[] numbers = new Integer[] { 1 };
numbers[0] = new Integer(0);

No ArrayStoreException.
The same thing I want from generics, resp. List & List, to be specific.

a) My suggestion does not bring errors to runtime instead of compile time. instead, it enforces runtime errors that were not caught by the compiler. And it can hapen because you can fool compiler by [i]List integers = (List ) (Object) doubles;[/i] but you should not be able to fool runtime.

b) Any List could safely be used as List as long as you use it in readonly mode (only get() operations). You don't really care if it contains Doubles or Integers, you just use Numbers. When you use such list for write operations (add()), it is your fault and compiler cannot check it. but runtime should.

[b]current state is that[/b]

a) compiler can detect your errors ([i]integers.add(double)[/i]) but cannot enforce nor guarantee data integrity of generic types. it is just too easy to fool it: [i](List )(Object))integers).add(double);[/i]

b) you cannot use List as List no matter how logical and intuitive it is. The reason is that compiler cannot rely on compiler to ensure data integrity so compiler tries to discourage you from doing these kind of things. nevertheless, you can fool compiler and runtimewill not detect this, so what is the real benefit?

[b]My proposal[b]
- extends current design from static-unsafe-only mode [b]to[/b] static-unsafe [b]plus dynamic runtime[/b] mode
- synchonizes behaviour with common user expectations
- dramatically increases type safety: from we-hope-things-wont-get-broken to we'll-know-when-things-get-broken
- make generics much more understandable

Any questions?

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> I am pretty sure you just don't understand what I
> suggest. That just happens sometimes so don't worry
> :-)

This is no slight against you but this is often the case. It is typical that people don't really understand the impact of something until after they've had time to familiarlize themselves with the concerpt. This is usually a very tactile process. So to your proposals, I'd vote no to introducing them into the mainstream Java. That said, it certianly seems fair that Sun (or you for that matter) release an experimential compiler that can deal with experimential syntax. Let people gain that tactile experience and if it works... then release it into the production ready language. Isn't this how we develop systems? Why should we take less care developing the language?

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Now tell me, how comes that Sun released generics implementation which is publicly criticised and hated before it is really used? Did SUN realized impact of their decision?

[i]btw. required changes include both compiler and JVM. That's SUN's job[/i]

[b]I do not change syntax. I don't suggest experiment. I want things finished, that's all.[/b]

Generics already are in mainstream Java so there's no time for playing maybe-we-add-it game. It would be if I requested introducing generics into Java. But this is not the case, they already are IN.

care must be taken, I agree. But if we let generics as weird as they are for a more than a short period of time, people start hating them, especially newbies, because they won't be able to grasp that contradiction and schizophrenia in Java core.

So, it seems quite clear to me that proposed changes MUST be implemented ASAP in some way. However, this does not contradict your requriement that there should be some EAP or experimental technology preview release. Anyway, [b]after all this must get into Java, that's whole point[/b].

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> Now tell me, how comes that Sun released generics
> implementation which is publicly criticised and hated
> before it is really used? Did SUN realized impact of
> their decision?

I beg to differ... I went to the chair of this JSR and asked for a serious round table discussion centered on what the problems where, what solutions were considered and why this one was chosen over the others. I offered a group of strong experienced developers that both agree and disagree with the decision to release generics. I had the backing of the JDJ to publish the results of the discussion. And... I was blown off with the claim that the commite had received overwhelming support for generics and that there was no need for this discussion.

If the JCP is about listening to those that agree with you and ignoring those that don't then what kind of community has been built? I dare say that Aaron Williams has worked hard to open things up. That said, his work has come to late for the "community" to have any real understanding of how we got to where we are today.
>
> [i]btw. required changes include both compiler and
> JVM. That's SUN's job[/i]
>
> [b]I do not change syntax. I don't suggest
> experiment. I want things finished, that's all.[/b]

No offense intended here.. after all, this is an achidemic discussion. How do you know that you're ideas will work? Why should Sun invest time money and effort here? Granted they should be considered and on paper they may look really good. But, should we risk the stability of the language based on something that looks good on paper? IMHO, this is too important an issue to ignore prototyping. AFAIK, no new bytes codes have been introduced to support the new syntax and as such are supported in the compiler. Do you see a need for new bytes codes? If not, you could certainly come up with a proof of concept.

>
> Generics already are in mainstream Java so there's no
> time for playing maybe-we-add-it game.

What is the alternative? Adding it outright? I'm Sorry but I want to slow things down a bit. A bit of ponderance is a good thing.

> But if we let generics
> as weird as they are for a more than a short period
> of time, people start hating them, especially
> newbies, because they won't be able to grasp that
> contradiction and schizophrenia in Java core.
>

Again, another argument for ponderance. Generics were rushed and voices of reason were ignored.

> So, it seems quite clear to me that proposed changes
> MUST be implemented ASAP in some way. However, this
> does not contradict your requriement that there
> should be some EAP or experimental technology preview
> release. Anyway, [b]after all this must get into
> Java, that's whole point[/b].

I think the point is to see if it SHOULD get into Java.. not that it MUST get into Java.

If your proposed solutions really normalize the behavior of generics, then prove it to me. Give me a prototype that I can use to see if it really works. Then after I and many others have had a chance to see if the solutions should be used.

I made a mistake, I ignored the generic compiler that was made available prior to the 1.5 release. I do want to learn from that mistake though ;)

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Now what, guys? is this all you can say about this?

forax
Offline
Joined: 2004-10-07
Points: 0

read this, it explains how generic types calculated
by the compiler could be retreived at runtime.

http://citeseer.ist.psu.edu/viroli00parametric.html

Rémi Forax

afreije
Offline
Joined: 2004-10-14
Points: 0

The need for runtime generic type safety is evident.
[code]
(1) List ln = new ArrayList();
(2) List li = ( List ) ln;
[/code] should not give a compiler warning, but a ClassCastException at runtime at line 2. (The need for) Unchecked cast warnings should be removed.

The need for further explanation on generic inheritance is evident as well. This misunderstanding of the way generics work probably stems from the way arrays were introduced in Java.
Because
[code] Number[] numbers = new Integer[]; [/code]
is allowed, some people think they can do the same with generified lists.

IMHO the best way to compare arrays and generified lists is to regard T[] and ArrayList as (almost) equivalent:
[code] ArrayList numbers = new ArrayList();[/code]

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Thanks for support.

as for generics inheritance and donwcasts, I think we don't need explanation why things do not work but solution how things should/could work. And I think it is feasible.

First of all, you have a good point here:
[code]
Number[] nums = new Integer[]; // allowed
List gnums = new ArrayList(); // disallowed
[/code]

If line 1 is allowed, line 2 should be legal, too. It is all about internal language consistency. With arrays, which have runtime type checking, you can do this:
[code]
Number[] nums = new Integer[] { ... };
Object[] objs = nums;
objs[0] = new Integer(0); // ok
objs[0] = new Float(1.0); // legal but fails at runtime: java.lang.ArrayStoreException: java.lang.Float
[/code]

Consider generics:
[code]
List nums = new ArrayList(); // illegal, incompatible types
List

objs = nums; // same as above objs.add(new Integer(0)); // ok objs.add(new Float(0)); // compiles but no runtime checking [/code] We don't want this difference. We want semantical and logical consistence and equivalence. And we do not want any stupid excuses. Latest example can be optimized as follows: [code] List<[b]? extends[/b] Number> nums = new ArrayList(); // ok List<[b]? extends[/b] Object> objs = nums; // same as above objs.add(new Integer(0)); // [b]illegal but this would and should work like charm at runtime.[/b] objs.add(new Float(0)); // [b]illegal[/b] [/code] Note that objs.add(new Integer(0)) is illegal but would/should work like a charm at runtime. Compiler is being just too smart because runtime is too stupid. Despite this fact, this is just too verbose way to make objects read only (compiler does not allow any add() call). Furthermore, if you want to use this, you must design your generics classes to be useful at least in readonly mode (meaning get(), contains() etc.) otherwise you end up with completely useless object... [i]Now it seems arrays and generic collections were designed by different teams for different languages without each knowing about other.[/i] [b]One final note: compiler should do its best to avoid runtime errors. But it should not be expected to avoid all of them. Furthermore, it should NEVER happen that compiler can detect errors which cannot be detected at runtime.[/b] With generics, we have it: compiler can detect you are attempting to put illegal object into a list. Compiler can be easily fooled, right? But runtime is defenceless now because it cannot verify generic type parameters...
afreije
Offline
Joined: 2004-10-14
Points: 0

Patrik,

My support was for runtime generic type safety, but not for the way you want inheritance to be misformed.
[code]
List objs = new ArrayList();
objs.add(new Integer(0));
[/code]
Of course this is and should remain illegal. You have to cast to the proper type before you do your stuff. Same as
[code]
Object o = new Integer(0);
int i = o.intValue();
[/code]
is illegal.
For typesafe casting we need runtime generic type safety.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

you are so wrong :-(

If you have a method with signature add(Number), you can pass it any Number (Integer, Long, Double, etc). Just fine.

What you cannot do, is call add(new Object()). This is of course illegal.

Now let's take a look at what you say:
[code]
List objs = new ArrayList();
objs.add(new Integer(0)); // why this hould be illegal?
[/code]
objs.add() takes any Object at compile time. it's compile time signature is add(Object) because that's how objs variable is declared.

And is Integer descendant of Object? Sure. So why should this be illegal? It would be incosistent with everything else in Java...

As for your second example, it is wrong again:
You cannot call intValue() because at compile time, 'o' variable is define as Object and it does not have such method.

But in first example, call to objs.add(Object) is perfectly valid because every list has such method. And Integer is a perfectly valid type for such a call. You just should expect this call to fail at runtime because rutnime requirements of 'objs' instance are higher than compile time.

[b]What you cannot and should not be able to do[/b] (neithre at compile time nor at runtime) is this:
[code]
List ints = new ArrayList();
ints.add(new Object()); // fails; must be at least Number to compile, and at least Integer to run...
[/code]

afreije
Offline
Joined: 2004-10-14
Points: 0

Patrick,
[code] List[/code]
is equivalent to
[code] List[/code]
So the type of the parameter in List.add() method is unknown.
It is different compared to
[code] List

[/code] Which defines a list that contain any object.
patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Agreed. So you can use List as ordinary
raw List. Compiler allows everything because compiler does not know. Why not?

But if you are trying to say that List is something like void object which needs to be casted to the type you want to use it like, i can agree.

So:
[code]
/* I can agree with this (although I don't like it): */
List useless = ...;
((List ) useless).add(new Integer(0)); /* needs cast */

/* but i am talking about this: */
List raw = new ArrayList();
raw.add(new Double(0.0)); /* compiles but fails at runtime */

/* and about this */
List nums = new ArrayList(); /* should compile */
nums.add(new Double(1.0)); /* compiles but fails at runtime */

[/code]

Message was edited by: patrikbeno

mthornton
Offline
Joined: 2003-06-10
Points: 0

Consider the following:

List list = new List;

list.add(new Float(1.0));

Now what do you think should happen? To detect that the add is unsafe at compile time you have to disallow the assignment. Unfortunately the 'obvious' inheritance relationships can't be made to work with generics.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

What you describe is an excuse: compiler does not allows this because there is no runtime type checking.

In your case, compiler cannot guarantee type safety. But runtime should.

[b]Without[/b] runtime type checking, you definitely have to forbid things like [i]integers.add(new Float(1.0))[/i] on compiler level. But [b]with runtime type checking[/b], compiler can safely allow this and it can be sure runtime will detect type incompatibilities.

Stop thinking about why things cannot be done; think about how they can.