Generics - good or bad?

Note that when extending C# for generics, Microsoft could not simply redefine the existing interfaces (as has been done in Java), but instead has to create new interfaces and classes. This means that you can only use those new generic collections with new code (or have to use wrappers of some kind). Think about the 3rd party libraries you use such as JDBC drivers. If generics worked like it does in C#, you wouldn’t be able to use generics with these until your supplier had caught up. This may matter less in the Windows world because Microsoft supply so much themselves. In Java, though, such a restriction would probably set back the adoption of generics by several more years.

[quote]Nah… you are allowed to put anything into it. You won’t be able to save it to a properties file anymore - that’s the only price you had to pay for doing so.
[/quote]
Well, yes. But I get the feeling that you were never intended to be able to do that - why would they write a Properties object that half allows non-String entries, and half blows up entirely with non-String entries?

No, IMHO it’s a bug, and one which can now very easily be fixed. So some people’s code will fail to compile - so what? They shouldn’t have used it in the first place. If I depend on non-core packages, it’s my fault if they go away in-between releases. If I exploit a bug, it’s my fault if my application doesn’t work with later JREs.

One of the biggest problems I see with Java today is the increasing amount of crud that’s building up. All the old stuff is being kept along with the new, giving us Hashtable and HashMap, Vector and ArrayList, frame.show() and frame.setVisible(true). I’m all for removing the old crusty APIs that no one should be using anymore, and making Java sleek again.

I was rather hoping that Java would undergo a backwards-compatibility break and cleanup when they finally reached 2.0, but since 1.2 became Java2, and 1.5 became the 5.0 version of Java2, all version numbers have gone out the window. There’s no obvious place that they can plan a complete re-work anymore.

You say that they may decide to make “raw” Generics illegal - that’s not going to happen unless Sun really change policy in this area.

A raw generic is a daft idea. Instead of the friendly and simple Map x; I’d have to change all my code to Map<Object,Object> x; and that’s plain silly.

Cas :slight_smile:

[quote]Think about the 3rd party libraries you use such as JDBC drivers. If generics worked like it does in C#, you wouldn’t be able to use generics with these until your supplier had caught up…such a restriction would probably set back the adoption of generics by several more years.
[/quote]
Interesting idea. In the use-cases I have before me you can’t until the supplier catches up anyway, because generics is pointless in my code until the library supplier alters their code to use the new syntax.

Presumably…it’s just a question of “source”. Where a library is the source of the data, you can’t use generics until the supplier “catches up”, but where the library is a processor of your data you can use it right away?

No. JDBC is specified largely in terms of interfaces which are updated in j2se5, yet it will happily load and use drivers that were compiled using j2se1.4 or even earlier.
Because generics information is erased in the byte code, all you need to be able to use it that the interface (or abstract classes) that you use have been rewritten in terms of generics, while ensuring that the erased version is unchanged. Hence I am also able to use old collection classes via the new generic interfaces even if those implementations haven’t been recompiled.

[quote]Presumably…it’s just a question of “source”. Where a library is the source of the data, you can’t use generics until the supplier “catches up”, but where the library is a processor of your data you can use it right away?
[/quote]
Well, you can easily write a conversion layer on top of whatever API it is to convert the old-style Collections into new-style properly-typed Collections. Then you’ll get all the benefits of compile-time type safety in your own code, without the vendor making any changes at all.

Regarding the library as a processor of your data - sort of. Javac will issue a warning when you pass a generic Collection to a non-generic interface, as the consumer could bugger it up causing your own code to fail. But it’ll still let you do it with that caveat.

[quote]Bruce Eckels has a much shorter commentary, dwelling on far fewer of the problems, here:

http://mindview.net/WebLog/log-0050
[/quote]
Heh. I got quoted in one of Bruce’s other posts about generics. http://mindview.net/WebLog/log-0060

God bless,
-Toby Reyelts

Score: 5, Insightful

I still can’t think of a good reason for type erasure. It didn’t need to be backwards compatible at all! 1.5 code won’t run on a 1.4 VM anyway! Gah! They could have just introduced a new classfile format, bumped the version number and put in a few more bytecodes…

…of course, the syntax doesn’t preclude this actually happening in a future release. We could have arrays of generics, and templated code, etc. etc. So maybe they’ll be enhancing the VM one day to do what we want.

Cas :slight_smile:

[quote]…of course, the syntax doesn’t preclude this actually happening in a future release. We could have arrays of generics, and templated code, etc. etc. So maybe they’ll be enhancing the VM one day to do what we want.
[/quote]
That is my hope — that the current state is a necessary stepping stone to a ‘true’ generics system. If migration to generics was too painful it just wouldn’t happen.

As far as I understand the reason for type erasure:

You have library A which depends on library B. Library A is 1.4 based, library B is also 1.4 based. Now, library B gets some bugfixes and at the same time they decide to use 1.5 generics in their API. You of course will need 1.5 to run this library. Trick is, that you don’t care about B - you care about A. A is still 1.4 and should work without any recompilation with new version of library B, as long as only change is generification(?) of signatures. They had to come up with way to make sure that m(List) call compiled under 1.4 will call m(List) method under 1.5, without recompilation.

If I’m right, than it could be fixed by generating m(List) bridge method under 1.5 (similar to way covariance is done now), which would do neccessary operations to convert 1.4 List to 1.5 List. Don’t know how exactly it would have to be done (this would depend on exact implementation of generics-done-right).

What happens if library A implements a List which it passes to B which is now expected a List? Without erasure you would have to supplement the methods (and fields) on A’s list with the extra generic signatures and additional type information. Except that you don’t know what the type required is (if your lucky it will be encoded in the field name or the documentation).
You can’t automatically add type information to existing code; erasure avoids the issue by making all code equal at runtime.
Once generics is in widespread use, the information will be available at least in the source code (and perhaps in the byte code as attributes which are ignored by the current runtimes). Then it may be possible to move to retaining the type at runtime with relatively little pain.

Criminy, anybody would think that refactoring code is expensive or difficult! It’s possible in just a few days to completely refactor a multi-thousand class application. It’s just fear and laziness holding people back and an inability to cope with actually having to do some work.

Cas :slight_smile:

Interesting conversation, didn’t really want to post and get shot down but anyway… I dislike generics for probably what can be considered a crappy reason. : They’re easy to misuse.

How many times have you actually got a list of things exposes to the outside user of class that is actually just a list of objects? Normally its a list of objects representing something, books on a shelf or players in a game. So, having to put a class in place called Shelf or at worst PlayerList is rather nice. Whats more how many times have I come across C++ code (which I do for a living) where someone has happily used a templated class for something because “it’ll be quicker”. Later they realise that their list of objects actually needs to do something that lists don’t normally do. Now, do they run round their code changing types to some new wrapper class or do they just knock in a function that takes their templated class as a parameter and does the job.

Whats more, how many additional methods do you pick up when you’re exposing your list in this wonderfully convienient method.

In general, Generics just seem to be to easy to get into sloppy coding practice which leads to a lack of refactoring points and a whole set of unexpected client calls…

At this point, feel free to shoot down… let me start you off: But thats not the languages problem that the programmers being sloppy, fix them… yeah sure. One of the nicest things I’ve found about Java is that it tends even terrible programmers in the correct direction. Generic move us closer to the “power” of C/C++ and closer to the misuse and the crapness of the source I get to work with everyday.

Kev

EDIT: Just read through, I’m not in the best of moods am I? Take with a pinch of the proverbial.

[quote]EDIT: Just read through, I’m not in the best of moods am I?
[/quote]
I feel with you: C++ is so depressing!

On the other side: Java 1.5 is sweet. :slight_smile:

[quote]Criminy, anybody would think that refactoring code is expensive or difficult!
[/quote]
It can be impossible if the code is not yours to refactor (especially if it is 3rd party code supplied without source). If you had to wait for all the 3rd party libraries to be updated before you could use generics, then it could be a very long wait.

Seeing as Java is a well-understood bytecode format it shouldn’t have been outside the realms of possibility to have provided a JAR converter in the JDK which added generic type information to all the classes and upgraded them to the new classfile format.

Cas :slight_smile:

This would be solved by bridge methods. B would have syntetic method not visible for normal programmer, which would implement something like:


void method(List l) {
    this.method(cast_to(List<Something>,l));
}

Now, the question is, how to implement specialized_instance cast_to(specialized_type, erased_instance). Please note that all that types are known at compile time of B, so any amount of needed code can be inserted there. Probably something like


List<Something> list = l.getClass().newSpecializedInstance(Something.class);
list.addAll(l);
return list;

This would work only for collections with addAll - but I think that collections are most important thing here. As an alternative, you could require generic classes to implement <specialize_cast> method, which would return specialized instance of given type with data copied from erased type - this would work for all possible classes (with collections inheriting implementation from AbstractMap/AbstractList)

Of course, if caller has passed non-Something element inside array, there will be a runtime exception somewhere in code - but it would probably happen anyway, if method was expecting list of Somethings in previous version of library.

Edit:
Ok, I have reread your issue and now I see that problem is with implementing list, not just with passing it. I will think about it…

Edit:

In case of 1.4 code implementing generic interfaces, cast_to would have to probably create wrapper, which would implement target specialization, while delegating all calls to backing 1.4 implementation. Which could of course lead to some inconsistencies like


List m(List list) { return list; }

changed in 1.5 to


List<String> m(List<String> list) { return list; }

which would cause following 1.4 code to behave in different way


System.out.println(list == PackageB.m(list));

Solution for that would be to unwrap such proxies on return to 1.4 code (done of course by jvm when linking 1.4 classes to 1.5 classes).

At this point it starts to be complicated enough to switch to different dialect… Maybe nice (http://nice.sourceforge.net)?