Please join us at the new JavaWorld Q&A Forums. Your existing login will work there. The discussions here are now read-only.


JavaWorld Talkback >> 959023

Pages: 1
Dimitar P.
Unregistered




Disprove Tarak's arguments for generics
      #34029 - 07/12/06 06:43 AM

I will try to be practical in criticising Tarak's opinion about generics, and I also will criticize Sun's arguments, too. By the way, they are pretty much the same, with different words. I will give examples when arguing (in order to be more convincing). Excuse me if the article is too long. I tried to fight all illusions for generics.

So, Tarak says that this code is trouble:

class A { ... }

class B { ... }

Vector v = new Vector();
v.add(new A());

...

B b = (B) v.get(0); // ClassCastException

This is a problem, he says. I do not agree. What about catching the exception or checking with "if (i instanceof Foo)"? Tight checks make the software robust. Example: assume you write a service thread which does something by client's request, and there are a number of client classes in your application which want to be notified when the thread finishes one iteration; you decide to declare an interface as a parent for all actions which your thread will execute when the time comes.

So, how in the hell a "String" will appear in your "ServiceThreadAction" queue? "Every object must be used only by interested parties" -- software DESIGN concept. If you allow a "String" to be put in forementioned collection, perhaps you should consider a carreer in chasing sheeps. Even further: it is nothing hard to write something like this:

Code:

for (Iterator it = actions.iterator(); it.hasNext(); )
{
Object i = it.next();
if (i instanceof ServiceThreadAction)
{
try
{
((ServiceThreadAction)i).execute();
}
catch (ServiceThreadException exSvc) // Maybe other exception(s), too.
{
logger.warning("Error in executing ServiceThreadAction: " + exSvc.getMessage());
}
}
else
{
logger.warning("An object in action queue is not an instance of 'ServiceThreadAction'. The object is: " + i);
}
}



While some can argue that checking with "instanceof" operator is superfluous, I disagree: First, you eliminate "ClassCastException" with a single line (which makes your execution flow more straight and more easy to trace by eyes), and second, you can trace buggy situations by logging and when you see a message as in the example here, you can go to your code and fix the bug. Even if you omit the logging stuff, the "instanceof" check will save your life anyway by silently dumping the erroneus stuff from your collection. This can lead to a result such as "nothing happens when I press the button X", but the developer will still be aware that the code does something not in the right way. Also, if you do not wish to do "instanceof" check, you should catch "ClassCastException" and either log it or skip it (I do not see why you should exploit try/catch mechanism when you have this good check in the "if" clause, but actually the result is the same -- although performance is not). Continuing...

Further: "the good thing about the generics is that they provide compile-time safety and less typing" -- that's the myth. I disagree with those both, too: compile-time safety will either make people do hacks (removing generification, because they want another approach or want to make a room for their class, and then wondering why the application does not compile -- many novice developers do such things), or it will force them to use in-place inner classes, derived from Thread/Runnable or something (as in our example, when they should implement interface method), which is another stupidity, because it really kills JVM's startup time -- our enterprise have made me once to optimize one small application and after I removed all inner classes (the alternative is reflection, of course), the all classes count degraded from 500 to 390! Believe it or not, but on a computer with 1200 MHz and 256 RAM, the initial starting time decreased from 1100 milliseconds to 700. Nothing so much, you may say, but it is good one to always keep track about scalability -- maybe someday the project will be ten times bigger and then these optimizations will save your reputation. The alternative way in doing such thing in my continuing example is not to use inner classes; use reflection, people! Create an utility class to locate methods/fields you want and then use only one class -- in our example it can be called "ReflectedServiceThreadAction" -- which accepts object/methodName/parameters in constructor and when the time comes, invoke them using reflection: you end up with a SINGLE class, and you may reuse it many times by plugging any methods you want there.

Anyway, this is off-topic, but it is the example I use when arguing with Java 5 fans about generics and when arguing with any Java developer about whether to [ab]use inner classes.

Further: generics provide less typing? How less is that?

List<String> ls = ... ;
String str = ls.get(index);

List ls = ... ;
String str = (String)ls.get(index);

Strictly speaking, new type of declaration is bigger, and accessors are shorter. Since accessors are expected to be many, and the declaration be only one, there is some degree of less typing here, but not so much of a difference to me and many other colleagues I know. After all, if you are a good typist, the amount of time decreased will be ~2 seconds for every type-cast prefix in the accessor code. Those ~2 seconds are arguable advantage. Going further, I claim that developers which neglect type-safety in collections by relying on Java compiler to "shout" on him/her, will eventually make other mistakes, because such features are turning you to a careless developer. Continuing these thoughts, it may turn out that the "old style" developer will (overall) do the job more quickly because the "new one" is neglecting this and this and then spends half a working day more in debugging. So, please, Sun, Tarak, and others, do not play with words so boldly, be fare in your assessments. "New" does not mean "better".

Continuing: do not try to correct me by referencing this Sun doc:

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

The only good argument I was able to dig from this doc (and their site) is the fact that "casting is annoying, because you know what data you have put in the collection". Okay, this is true, but it is also very subjective; consider what happens if you put different types of objects in a queue and then your service thread needs to pull them one by one and deciding what to do next accordingly to the type of the current object? Don't laugh, those are real-world examples, I have done it many times, and it has proven to work faster than using a couple of collections (as you can guess already, I am a performance freak). Bottle-necking inter-thread and inter-process communication objects is a good way for the developer to easily keep track on what is going on and to fix bugs quickly. Another off-topic, but also a real-world example.

Further, Sun claims that they ease the subtyping by doing those:

Collection<?> c = new Collection<String>();
Collection<Something> c = ... ;
Collection<? extends Something> c = ... ;

Is this something new? Yes: the new way is more restrictive! In the first example, you cannot do "c.add(new Object());". So why exploiting new features of Java only to find yourself doing new kinds of hacks? In the old way, you can add anything there and then leave the accessors to figure it out, which is not the way it should be done, but at least is a working solution, if your accessor code does "instanceof" check, right? Another drawback here: In the second example you CANNOT add instance of the class "SomethingElse", if it extends "Something". Man, this is funny! You should use the third example, if you want real subtyping abstraction. Again and again I am reminded of something magical which sounds like "instanceof operator", which is around from a bunch of years now.

Also, take a look of their "fromArrayToCollection" (page 8) example -- this example is a method to convert an arbitrary array to collection using generification. They claim they can write a single method to do this job, no matter of what the data is. Big deal! Using classic way, you can do all the same with less typing (you wanted less typing -- there it is), because your method signature will not include generics, and also your classic code will not require all elements in your collection to be of same type.

Further killing Sun's arguments: you can interoperate with legacy code, but when you assign legacy collection to generic collection, you will get an "unchecked warning". My opiniion? Just providing more space for bugs to flourish.

Further: type erasure. Generics are removed after successful compilation. Perhaps generics are preprocessed to exclude compile-time errors. Great, now the only difference between Java 5 class file and my 1.4.2 class file is the fact that I have "instanceof" check, and if someone manages to hack the generified collection by using reflection (because it is not generified at runtime), my code will not crash, but the other one will crash. Even worse, you will be convinced that you do nothing wrong, and you will waste some time before you discover that you have made a mistake in over-estimating this new feature of Java. Hey, is it not far less painful just to put the "instanceof" check?

I want to be fare in my assessments (Tarak and Sun are not). For me generics are actually a preprocessing mechanism, because the "generification" is removed after successful compilation. Also, they encourage neglecting.

The rest of the document is a crap, in my opinion -- it merely describes how to "play" with generics to achieve more complex compile-time and strong-typed data structures, but nothing, which cannot be achieved with Java 1.4.2 with small amount in written code.

For the end: I challenge developers to argue with me about anything I missed in Sun's documents and everything else which can be a real advantage of generics -- something which you CANNOT ACHIEVE WITH Java 1.4.2 and WHICH IS REASONABLY BETTER FEATURE. It would be interesting for me if someone can beat my arguments here. But please, everyone, do not tell me "we have less typing"; when talking about missing a single type-cast prefix, "less typing" is just not true. I understand, that if we were talking about usage of some tool to generate all needed code for having an EJB, then okay -- I agree, no need to write by hand all those Home, Local and Remote interfaces, so why not code-generate the things you are not so much interested about and which are technical requirements for using the technology? No reason to refuse such comfortability, of course. But if someone is going to tell me that a single "if instanceof" check (or a single type-cast prefix) is "more typing", then in my opinion this human should grow vegetables, not trying to be a programmer.

So, can anyone actually tell me why generics are considered something superior?

Dimitar P.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Pages: 1



Extra information
0 registered and 1 anonymous users are browsing this forum.

Moderator:   

Print Topic

Forum Permissions
      You cannot start new topics
      You cannot reply to topics
      HTML is disabled
      UBBCode is enabled

Rating:
Topic views: 4057

Rate this topic

Jump to

Contact us JavaWorld

Powered by UBB.threads™ 6.5.5