No Real Java Meta Model

[Voting on JavaDesignFlaws page.]


Yes, I am complaining that Java is not Smalltalk.

One of the things that has always annoyed me about Java is the fact that the Class model is so lame when compared to Smalltalk. As a simple for instance - in Java static methods cannot be overridden by subclasses. There are literally dozens of design problems that I've had to painfully work around that could simply have been solved by allowing overriding of static methods. Of course, then static wouldn't mean what it does in C or C++, but why is that a problem? (Anonymous comment: It's not hard to see why Java's designers wanted Java static's to act like C/C++'s. What are the design problems you can't solve? Who knows, maybe you were just trying to write Smalltalk code in Java, and why should that work out well?)

So what I'd like is something more like what we have in Smalltalk, where a "class" method can be overridden, while a "class instance" method cannot be, and thus acts like a Java static method. The same holds for static variables, too.

While we're at it, I'd also like a more meaningful meta-model that allows us some of the flexibility we have in Smalltalk in constructing classes on the fly. For a mind-bending explanation of this (if you're not familiar with Smalltalk's meta-model) read Chapter 16 of Goldberg and Robson.

-- KyleBrown

CostinCozianu has supplied, in JavaArraysShouldBeFirstClassObjects, a very nice code example showing a class (Integer[]) for which the Java reflection code fails to show an accessible field (x.length). This demonstrates that not only is the Java meta-model not meaningful, it is also inconsistent with its own behavior - rendering it worse than useless. -- TomStambaugh

[removed some factually incorrect discussion on whether length is a field or an operator, it is a field, at least according to Java language Specification, see http://java.sun.com/docs/books/jls/second_edition/html/arrays.doc.html#64347 ]

I'll come back on it in JavaTypingWasSimple, trying to prove that these problems derive from an acceptable series of engineering trade-offs, and while it is valid to criticize these decisions, it is not the case to classify them as design flaws or whine about it, or even long for Smalltalk flexibility (like it is the case of this page). If things were so simple I'm sure that Smalltalk fans would have had a type checker by now. Instead they're so stubborn to insist that if they see no way to do it (i.e. providing static type safety and flexibility and expressive power), then it must be bad and/or impossible. -- CostinCozianu

Without getting into static vs dynamic typing, there are implementations of Smalltalk with various typing schemes. StrongTalk is one and BistroLanguage is another.

None of them is actually working or workable.

One WikiWord: CecilLanguage


See JavaTypingWasSimple for a possible reason on why Java doesn't have a real meta model. Smalltalk can have a more expressive (I wouldn't call it real) and flexible meta-model because Smalltalk was designed differently, of course. Smalltalk doesn't have to be concerned with typing at all. If a message hits a target that's fine, if no, no. It's very easy to construct a meta-model on that. -- CostinCozianu


Kyle, can you provide an example when the possibility to override a static method would lead to a more elegant solution?

Well, it would be nice to be able to use FactoryMethod to provide the default value of a Singleton. In this way Class X and all of its subclasses could have the same class-side method (getSingleton()) and each could return an object that is an instance of a subclass. That way I wouldn't care which class I actually have when I ask it for its singleton - most of the gorp would be in the superclass. For lots of other solutions that use inheritance of static methods, see the DesignPatternsSmalltalkCompanion. -- KyleBrown

Kyle, you can write a nice and simple utility class that does what you want by using reflection, especially with the 1.3 proxies it's even easy and convenient to write a very general solution. Otherwise you can solve this problem even without using reflection for invoking a method, but creating a garbage collectable object for every singleton access. This problem is more related to the esthetics of code than a real problem. -- CostinCozianu

But that's the point, isn't it? Why jump through all of these reflective hoops when making the static methods inheritable would solve the problem, and many others to boot? -- KyleBrown

Instead, you're getting a largely type checked language with an extremely simple typing system. With Smalltalk you obviously don't get that anyway, and not a satisfactory metamodel either. Making static methods inheritable is not that simple (in Java), maybe in a better language. But we can try to examine how large and how serious are the problems that we have because of the lack of virtual methods attached to the class (it would be improper to call them static), and see if Smalltalk does it any better.

The heart of the matter is that in Smalltalk you can invoke absolutely anything on the instance of the metaclass, including the singleton returning method, while in Java, after getting the Class.forInstance("..."), you are only left with what is defined in java.lang.Class and with the static type checking. Now what you can do in Java is this line of code

 Object obj= MyUtility.invokeStaticEmptyMethod(desiredClass,"getSingleton" ); // or whatever else method name
Which, of course, bypasses the type checker and can fail at runtime. But that's absolutely the same thing that Smalltalk does, isn't it? We can also design an alternative, more type-safe solution. If we want a general solution that is also statically type safe, we have to have parametric types. And this conflicts with JavaTypingWasSimple, therefore we have to look for a better language. -- CostinCozianu

No, that isn't what you would do. You would do the exact same thing as you would for any other Java object instance, except you would invoke the method on a metaclass instance. The static resolver would remain the same, and the dynamic vtable lookup at runtime would remain the same.

C++ didn't implement this originally because it was adverse to reflection as reflection is bloated and slow; RTTI was added rather late in the game, and thus it never matured far enough to be meaningful. Similarly, Java did not add reflection until 1.1. By then, it was already too late to modify the VMs.

Note that you couldn't really do class-level polymorphism in C++ without passing a pointer to the class vtable, a change that has a significant enough impact to be rejected. The correct solution consistent with the goals of C++ would either be some bizarre template dance or to just create a class yourself.

It's rather bizarre to suggest that any Java decision is based on it being an embedded solution. Java is not an embeddable language. It's more accurate to suggest that Java is a shadow of C++ as C++ was taken as the inspiration for 1.0.

At any rate, this is a non-starter. There will never be a Smalltalk metamodel in C++ and, by extension, Java. -- SunirShah

Sunir, are you suggesting that a Smalltalk metamodel is actually a good thing? I have serious doubts about that, considering the fact that Smaltalk doesn't support types at all, so its metamodel is inherently less useful for me than what Java has. If I am to choose between some minor exceptions with arrays and having no types at all, I'll take Java metamodel any day. As to what regards your alternative solutions, you can have it your way, but you'll be creating an extra "garbage ready" instance (of the metaclass), per each access to a singleton. What I said was not that reflection is the only solution, but the above code is absolutely functionally equivalent to calling the polymorphic method on metaclass in Smalltalk. So there's no Smalltalk functionality that we are really missing in Java. -- CostinCozianu

Say it with me, "Smalltalk is strongly typed." I don't know why people don't understand that. Everything in Smalltalk has a type. The difference is that those types are verified at runtime, not at compile time. We call say those environments are dynamically typed, as opposed to statically typed environments that verify types at parse time.

Java's metamodel is a joke. The Java metamodel resides in java.lang.reflect and it was bolted on after the fact. Indeed, you may not actually have the java.lang.reflect package depending on your Java. I don't understand what array exceptions have to do with Java's metamodel. I can only surmise that you are confused over the term metamodel. The metamodel is not the typing system, although the typing system may be implemented with a metamodel.

Secondly, your implementation of the metaclass does not extend to all possible implementations of the metaclass. Your usage of the term singleton is also confused, as one doesn't make copies of the singleton when you access it, but instead you get the singleton itself. A metaclass should be a singleton due to the definition of class (!= type). Please refer to the GangOfFour book.

Finally, it's further confused to start arguing which language can do more functional things than the other, as both are TuringComplete. Turing-Completeness isn't really a measure of language power when considering general languages (as opposed to domain languages like SQL, which is not TuringComplete). You measure a language's power by how many things you can do with the first-order constructs (or whatever is provided by the language). In this case, the programmer has to manually construct a metaframework in Java and C++ (as I have done a number of times) versus in Smalltalk the language provides this framework itself. Of course, what constitutes a first-order construct is subject to debate, especially if you begin to consider libraries to be first-order (first-and-a-half-order?).

It should be evident that I do not advocate the adoption of the SmalltalkMetaModel? in Java nor C++. Actually, let me rephrase. I do not advocate the adoption of the SmalltalkMetaModel? in C++. I don't advocate anything for Java as I don't think the language is well-suited to improvements as it is already thoroughly idiotic. But if I had to believe that Java was a serious attempt at creating a programming language, I'd have to let it stick with a more C++ approach. -- SunirShah

'Sunir, everything in Smalltalk has a class, and not a type in any useful definition of type in modern ComputerScience. -- CC


For more on types in Smalltalk...

See: http://www.dnsmith.com/SmallFAQ/PDFfiles/LanguageQuestions.pdf (BrokenLink 2005-08-28), from a Smalltalk insider who says :

Saying that all types derive from type Object, as some proponents of other languages do, misses the point entirely. There are no types to derive from. Object is no more a type than Float.

Smalltalk inheritance is not a type inheritance but an implementation sharing inheritance. A subclass is not a subtype but an implementation of a new object that shares some or all of the implementation of the parent class.

This should clarify Smallalk's support for typing (static or dynamic or any other kind) - that is no support whatsoever. This is not a value judgment, it just means that Smalltalk supports the class construct while it does not support any useful type construct, it was a design decision based probably on the argument that types are not strictly necessary for an OO programming language, while they reduce the flexibility and expressiveness of the language.

And to what is the difference between class and type, and essentially subclassing and subtyping the easiest lecture is Kim Bruce "Typing in object-oriented languages: Achieving expressiveness and safety" http://www.cs.williams.edu/~kim/README.html#Static Interesting paper - I'll summarize without the math on StaticTypingWithMatching

This can be indeed seen as an exercise in debunking some prevalent misconceptions about ObjectOrientedProgramming. Everybody talks about it, very few seems to care what that may be.

From a practical point of view, while in Java I can test if an object will conform to a type (either class, or interface - the Java typing system is far from perfect, in any case it is better than nothing), in Smalltalk I can only test an object is an instance of a specific class or of a class derived from a specific class ancestor. Even the class inheritance in Smalltalk is more unlikely to correspond to a subtype relation between the derived class and the base class as opposed to Java mechanism - inheritance and interface implementation, which are more strict. For example, in Java I can create a Collection that will ensure at runtime that the objects added to it conform to let's say java.sql.Connection just by testing: x instanceof java.sql.Connection, while in Smalltalk I can't do something equivalent in an easy way. For me this is clearly a Smalltalk limitation, and therefore I prefer Java's metamodel, which, while far from perfect, is better than Smalltalk's. -- CostinCozianu

What you can't do in Java however, is check that an implementation of an interface conforms to the specification of that interface, except by sending messages to the implementation. The Java type system is just too simple to enforce type safety for anything except primitive types or final types. This is not Java's fault, but is a problem in any language with runtime polymorphism. The language performs type checking by comparing names, but the language only uses a few simple name-comparison checks to test that an implementation actually does conform to a type. Most of the description of the type - the order in which methods must be called, pre/post conditions, object invariants, etc. - ends up in human-readable documentation, not in the programming language itself. Type conformance boils down to a convention agreed upon between programmers, because the language does not and cannot enforce it.


Would it help if we had a page describing the SmalltalkMetaModel??


EditText of this page (last edited December 2, 2005) or FindPage with title or text search