Oops. I originally posted this a while ago, but forgot to update Advogato.
Holidays n'at
Holidays were good. Extremely busy, but good. I hate Christmas shopping
more and more each year. The thing is I'm not a "gift card" kind of
person. I just don't like it, it's barely better than giving money.
There's a lot to be said for really thinking about someone you know,
and what they'd like. It's a good contrast to the rest of the year,
when most people are thinking "me me me," and I think the whole gift
card phenomenon is basically a cop-out. Of course, when you do decide
to actually get gifts for everyone, it gets much more time consuming.
But it worked itself out in the end; people seemed to like the gifts
I got them.
Owen and Shane at the fiddle, New Year's eve
Waterloo
Back in the 'loo now. This place just has a way of sucking the life
right out of you. Between the dreary days and spending lots of time
in MC, it's enough to make you just feel tired and apathetic all the
time. Hoh well, only 4 more months and then this stinkin' town can
kiss my ass.
On Java
So I spent a fair bit of my last term coding (gasp!) Java. To be sure,
there was a fair amount of C++, Python, Perl and even some more Ruby.
But the majority of my work was in Java. Java gets lots of C++ fanboys
screaming about how it sucks because it's slow or it has GC or it
doesn't have pointers. I'm not going to take the fanboy approach, but
will instead, for your amusement (or horror, depending on how you see
it) describe my "hit list" for Java.
There's no const. The way this is typically worked-around
is using interfaces. Let's say you have the interface IFoo.
You write an implementation UnmodifiableFoo that wraps an
IFoo and throws UnsupportedOperationException
for all the mutation operations (BTW, this is the decorator
design pattern). What's wrong with this? First, you have to write
UnmodifiableFoo yourself, which is of course error-prone.
Second, because UnsupportedOperationException is an unchecked
exception, there will likely be lots of code out there that calls the
mutation operations without catching that exception. So you pass an
UnmodifiableFoo into a method taking an IFoo, the
compiler happily does that for you, and things blow up at runtime. You
can't throw a checked exception without changing the interface,
so basically you're hosed if you want to try to add immutable
implementations after writing the interface. In C++, the contract of
the method that the compiler checks can specify that it wants
a non-const reference because it will be doing mutations.
AFAIK, there is no way to have the Java compiler do anything similar
for you.
References are not explicit. This is easy to get used to, but it
inevitably confuses newcomers. Also, there's no way to have a reference
to a primitive type (most of what you want that for is accomplished
with "out" parameters in C#).
While C++ has them as well, exceptions are much more heavily used
in Java. I tend to prefer return codes. The argument for exceptions
is that you force the programmer to do something when
an exceptional condition occurs. In fact, you do not:
try {
methodThatThrowsStupidException();
} catch (StupidException ex) {
}
Sure, you made them write a catch block. Who cares? The
other thing that inevitably occurs is that programmers, fed up
with writing try and catch blocks, inevitably
end up having gigantic, 500-line try blocks. Then when
an exception occurs, you have no real idea what went wrong. I
guess I prefer return codes because I feel that it's pretty easy
to tell by looking at the code whether you're checking them or not.
Exceptions essentially de-localise error-handling code, and I
don't like that.
Finally, the generics in Java leave a lot to be desired. I've written
about this before so I won't go into the
gory details here. Suffice it to say that writing a generic class in
Java can be quite the pain in the rump.
Now, I know I promised that I'd steer clear of "fanboy" tactics,
but I actually will say something about GC. It's not appropriate
in a lot of situations, like embedded or real-time systems, but
I do think that C++ programmers spend way too much time tracking
down segfaults and memory corruption. Of course I agree with Joel
that it's important to understand pointers and have experience
debugging that kind of thing. But so much time is spent tracking
these things down, and it's only when you have good common
practices (smart pointers, or auto_free semantics, or
whatever else) and your development team adheres to them
religiously that it becomes much less worrisome. Since most projects
end up using external code and add new programmers, that situation
of common best practices and strict adherence is extremely rare,
so the memory management problem remains a big one, and GC is one
solution that is appropriate in many situations.