A Language for People who Like to Type

chromatic on 2003-01-31T20:02:23

While editing an article on the Decorator pattern, I came across a paragraph which explained why Decorating was so much nicer than subclassing, in Java. (Besides the fact that you get major points from Pattern geeks, that is.) Apparently, constructors aren't inherited in Java. Yeah, I had to check several references before believing that.

Add to that the strict method signature checking, the requirement that constructors have the same name as the class, and you could find yourself (as in the first example in this article) declaring several constructors that meticulously gather arguments and call super() with them.

Gosling forbid a language should reduce busywork.


Re: A Language for People Who Like to Type

forehead on 2003-01-31T21:23:11

Apparently, constructors aren't inherited in Java. Yeah, I had to check several references before believing that.



You should spend more time working with staticly typed languages. It would be a "very bad thing" for a staticly typed language to inherit a constructor (you'd have a nasty case of memory corruption). If you think that is odd, you should take C++ for a spin. Member functions are not virutal by default. Or better yet, try writing OO-C sometime (no, not with glib. Take the time to actually setup the data structures required to implement virtual functions yourself). It'll give you a much better appriciaition for the power and flexibility provided by dynamic languages. In fact, here's a short example of an OO-C "class", including a simple virtual function implementation...

typedef struct _MyClass MyClass ;

struct MyClass_vtbl
{
  void (*MyClass__dtor)(MyClass*) dtor ;
} ;

struct _MyClass
{
  struct MyClass_dtor* mp_vtbl ;
  int m_memberVar ;
} ;

void MyClass__dtor_body(MyClass* self) ;
#define MyClass_dtor(self) (((self)->mp_vtbl->dtor)(self))
There you have the *minimum* quantity of declaration for a tiny class with a single virtual member function. We haven't even got around to actually *implementing* the function, yet. When you start throwing in things like interitance, and making sure you get clean, warning-free compiles even with C++ compilers, look out.



Add to that the strict method signature checking, the requirement that constructors have the same name as the class, and you could find yourself (as in the first example in this article) declaring several constructors that meticulously gather arguments and call super() with them.



This is "normal" for the vast majority of programmers. In fact, pretty much anyone with a CS degree will spend the vast majority of their undregraduate years working with staticly typed languages. To them, the fact that Perl allows (and encourages) the inheritance of constructors is a bizzare thing. I 100% agree that for the vast majority of programming tasks, raw speed and/or memory efficiency is such a non-issue that it makes a lot of sense to take advantage of the expressive power provided by dynamic languages. However, there are still a large number of tasks that really need to sit closer to the hardware so that developers can squeeze more performance (e.g., video encoding, decoding, device drivers, many embedded systems). This is why I think Inline::C is so damned cool. It allows people to easily write performance critical portions of applications in C, but make use of the expressive power of Perl for the remaining 90%.

Gosling forbid a language should reduce busywork.



This I agree with. Java has all of the performance overhead of a dynamic language, and virtually none of the benefit.

Re: A Language for People Who Like to Type

chromatic on 2003-01-31T22:44:55

It would be a "very bad thing" for a staticly typed language to inherit a constructor (you'd have a nasty case of memory corruption).

Is this because of memory allocation or method dispatch or something else I'm missing? I realize that allocation in C++ is tricky, but that's so rarely a concern in Java...

Re: A Language for People Who Like to Type

forehead on 2003-02-01T00:43:22

Part of the problem with constructors in staticly typed languages is that the compiler determines the size, and offset of every member at compile time. Think C structs. One of the reasons why you have to #inlude headers is so that the compiler can properly calculate the size of any structs you may be using.

The compiler keeps track of a base address and offset of class members (e.g., the struct/class starts at address 0xDEADBEEF and the m_udder member has an offset of 6 bytes). In static languages, the compiler must know this in advance. One of the things that make dynamic languages dynamic is that they use all sorts of additional data structures to find data members at run time. C and C++ compilers use the fact that data members always have a fixed offset to improve performance (it can just hard code in the known offset, rather than doing complicated lookups).

So what does this have to do with constructor inheritance? Base classes do not have any inherent knowledge of their derived classes, and the static languages do not provide any way to look this information up at run-time (well, C++ and Java do have introspection, but that still doesn't really help this situation because they don't provide the infrastructure to insert sub-routines at run-time because their virtual function table is also of a fixed size). Now if a developer wants to do these sorts of things, nothing about the language prevents them from implementing it. The dlopen() family of dynamic linking is good example.

If a programmer was to ask for a Foo object, and the Foo object was derived from the Bar class, and the Foo class inherited a constructor from the Bar class, there is no way for the bar class to A: know what additional members the Foo class added. Because of this, the part of memory containing the Foo members will not be initalized. The author of the Foo class will almost certainly have some behavior that relies on the fact that Foo objects were initalized to some known state. If you don't explicitly initialize memory yourself, the C and C++ spec says that its contents are undefined. The memory allocation routines may zero out the memory. They may fill it with 0xDEADBEEF, it may be data left over by whomever last had data on that page. The Java language guarantees that variables intialize to a known state, so this isn't as much of a problem.

If Foo actually implemented a constructor, the Bar constructor would ever actually get called unless the implementor of Foo explicitly allowed it. This deprives the Bar class of its opportunity to initialize its data. Sure, the Foo class could. If you throw in C++-style multiple inheritance, diamond shaped inheritance trees present a whole new problem (the root class constructor may be invoked twice). One of the goals with C was to keep language imposed overhead to a minimum. One of the intial goals with C++ was to simplify object-oriented programming for C programmers (C being a very dominant language). The only thing Java has going for it is its portability. When push comes to shove, it's still interpreted byte-code. There is tons of language overhead. But they still decided to limit themselves to many of the same constraints imposed by C and C++. At least C and C++ had reasons for it.

Rather than allowing for inheritance, C++ and Java both A: provide a default constructor that does nothing but call its parents default constructor, and B: if you don't explicitly call one of your parents constructors, C++ and Java will ensure that the parents default constructor gets called. C++ and Java both need to be able to detect at compile time, wether you do or do not call one of your parents constructors. Doing this in the general case is not possible (thank you Mr. Turing). Instead, they created a few rules. One, the name of the constructors is limited. Two, your constructor has to call your parents constructor directly. If you call a function that calls your parents constructor, the compiler will not notice. Three, the parents constructor must be called first thing so that the derived class can A: rely on values already setup by their parent, and B: can over-ride / change / tweak any of their parents values.

I can provide an example in OO-C that makes the relationships explicit, if you'd like.