lang Aktuell Seminare Reports Homepage Software
printer / text mode version
university-logo
draheim
@informatik.hu-berlin.de

Reports
- postindustr.CC
- XML/Ti Report
- pTA StudienArbeit  .
- sch_llf study
- Geschichte des PC

TechDocs
- Perl Objects
- Installing Oracle
- shell cmds in python
- Using css for xml
    defs   tricks
- Unsafe mono  [x]  !
- Docbook Manpages
- Java Bean   Code
rpm-suse
 
- schema-mappingen
  ig cv hg re dv ev
  zz mk pr
- java problemsen
  lang swing ext gtk jjtree xul
 
boot
-grub-netboot
-grub-gtk
-partclone freshmeat
-partimage links
 
-releaseuploader


sitemap


-guidod-pygtk
sitemap             *offsite link

2004-04-21
(C) Guido Draheim
guidod@gmx.de

 
generated by mksite.sh

java language problems

Java was born in the early nineties quite close to C++ becoming mainstream. It is said that in the first days the java language was supposed to be typeless similar to what python is today but that was dropped in favour of proper method overloading. A thing that can not be achieved unless the compiler knows a class name for each variable around.

That latter scheme makes for the variant that you can build lots and lots of copies for a referenced object with diverse class names on each of the reference variables - and effectivly call different methods with the very same object instance. What is most interesting to C++ programmers: it is perfectly okay to assign references of incompatible class attribution as long as the (dynamic) runtime object has them both. Oh yes, java will throw an exception if a (dynamic) cast fails at runtime.

After getting the idea about the type relations of objects, most of the pecularities of java become clear. And no wonder some java programmers have problems with other languages which work on a different "type checking" model. Just keep in mind that it is one object oriented model in a group of many valid interpretations of object orientation.

Beyond that model pecularities, I came across a few things that I think to be problematic for this language and without a special need. They limit the readability and maintainability of java code as well as consume power to build a project from scratch. It is too often necessary to use a feature-ful IDE like eclipse to get at a decent development power.

missing lambda objects

In java, functions are no first class items. No, I am not speaking about

`static function x = lambda (x) { ... }`
. That would be nice as well but java had always been to not have global functions and only allow static methods of classes. However, we see that C++ can take a method reference of classes `class::*` and Objective-C to have selectors for virtuals as well, and also languages like python to use plain strings as callables of an object.

Of course, java can be used with callable objects but that works a bit differently. There are atleast two approaches. In one way it is possible to use plain strings via the reflection api where the virtual table of an object can be scanned and a method bing called. However the amount of code is enormous to achieve such - and it should be limited to special cases and probably wrapped in a server instance. Examples are loadable plugins where some entries can be looked up at runtime whether they exist or not.

The other widespread variant is with anonymous classes. That looks like

 object.addCallback (new CallbackClass {
        runCallback(Object o, int param) {
               another.trigger(o,param) ....; } } 
which actually has a number of little problems. First of all, the addCallback does not actually check for a matching call signature of the embedded function. Instead the CallbackClass has an abstract method that requires to implement the runCallback - yes it must be under that name and no other.

The problem is with dynamic slot assignment - if you want to connect a signal of some sort of object to trigger of another instance (as above) you are required to write down a number of lines instead of just doing `object.addCallback(another.trigger)`. Note that the shorthand in the prior section did actually lie to us (and most java docs do as well...) as it did not list how some "another" instance comes about to call a trigger at. If the other instance is just "this" then the following idiom would need to be used:

   interface CallbackClass { void runCallback(); } 
   class MyClass {
      final MyClass _this = this;
      object.addCallback (new CallbackClass {
        MyClass self = _this;
        runCallback(Object o, int param) {
               self.trigger(o,param) ....; } } 
   } 

Now look again at it - it is not anymore obvious that it is supposed to be a lambda callback. The actual intention of calling a method "trigger" upon a signal "callback" is buried in a lot of stuff that the programmer has to get right. That is suboptimal especially in situations where a lot of callback channels are attached to class. Building a VFS (virtual file system) wrapper or an optimizing AST (abstract syntax tree) compiler - that is getting very long for a code.

no implicit iterators

A great benefit of object oriented languages (with garbage collection) is the available of container types that hide most of its implementation details. Still they must be usable by third party code on an item by item basis. There are usually two approaches - walking the container with an iterator item variable of the current item (and putting it as subject to some function calls) - or giving a lambda to an "apply" routine that calls the function on all the objects (usually filtered by some condition). In the prior section we saw why lambda-apply is not the usual way to do it in java.

Walking the tree is done explicitly with container functions and it looks quite often this way:

   for (Iterator iter = map.keySet().iterator() ; iter.hasNext();) {
         MyObject item = (MyObject) list.next(); 
and note that you have do downcast the MyObject since the original type is not expressed with the iterator. In a language like python it would look like
   for item in map.keySet(): 
which is really all about it since the python interpreter will have a look at the keySet (via reflection) for an iterator() method and use it to construct the iterator checking also whether the returned object has the needed methods (again via reflection) and calls them for each loop round implicitly setting the "item".

Some might have heard about Java-1.5 and its for-loop shorthand syntax which looks like

    for( item : map.keySet().values()) 
and which is really short and nice - but it suffers a problem that python had as well before. The problem is in the actual contruction of the item space! Supposed you want to run through all numbers of 1...100000000 then one could say "range(1,10000000)" which returns an array with one million objects being actually allocated in main memory. Only after construction of that array the for-loop shorthand will walk over each item in that array.

That is different to the earlier iterator-model which only constructs one item per each step. And a language like python had seen such problems come up in real code (with blowing main memory) and leading to the definition of an "xrange(1,1000000)" object which does not return an array at all but instead constructs an object implementing the iterator model that is catched implicitly by the "for"-operator. In other word, it would call an xrange.next() and not xrange[i++]. That's about implicit iterator detection on for-loops.

template collections

As seen in the previous section, it would be great to avoid the dynamic downcast of the objects of a container. Each downcast can yield a throw with an unmatched object class - which can really happen when some third party code had been adding an object to the container. The third party code will not see a warning about it since an Object is an Object.

The Java-1.5 mode has been helping here with allow to specify item restrictions for the containers. In its syntax it does look very much like the c++ idiom for templates even that it actually isn't. A "Map" will not specifically create a subtype of a "Map" instead it just adds additional arguments on "Map"-object construction. Now, on every "insert" operation to the container the given "class-references" are checked with the "instanceof" operator, i.e.

   insert(Object k) {
       if (!(k instanceof _MetaType1)) throw BadMetaType

Luckily howevever we do not need to write that down everytime since the java compiler can do it for us - just add the "instanceof" attribution to the declaration types of your function arguments, i.e.

    insert(Object instanceof _MetaType1 k) {
and we get the benefit that it is not only checked at runtime on the (dynamic) objects but the compiler will also check the class attribution of the object (static) class giving an early indicator if something has gone wrong.

Just be sure however that this variant of type collections is quite different than other ones. In c++ a new class is generated and linked, they even have different static constructions and such. In java we do not have generation and instead we have a number of "type contraints" on an object instance being of the general type "Map". That allows for some backward compatibility since you can easily add a type-contrained Map to a free Map. And of course, you can reassign the Map reference to a Map reference with a different type-constraints but still being the same object instance (note: assumed detail, 1.5 does just exist as alpha code at the time of writing).

meta variable members of objects

Whoever has been using python for a compiler construction work has come to love the idiom - all members are actually names just listed in dictionary in the instances. Therefore, the source code `object.member` happens to be strictly identical to the call `object.__dict__["member"]`. There are no source level type attributions here anyway.

The real benefit in compiler construction comes from the usual mode of usage of an AST (abstract syntax tree) as input being step by step transformed into another tree being subject for output generation (e.g. gcc RTL). In each step we have to walk the tree and look at each node and then go to reorder some nodes and add memorize additional attributes about the nodes and the tree somewhere.

If there are meta variables around then it is quite easy to memorize additional attributes - just store them on the nodes themselves. The meta variables are not needed to be predeclared in any way. While that can be achieved with a local Map it is about the syntax extension to access them with a shorthand notation that just says `access attribute`.

Similar but more beyond is the wish to reorder the tree step by step. In some cases the data structure changes so much that the member functions must be adapted as well to give reasonable results. In java one would have to create a new Node object of a different class with newer methods being actually a copy of the old Node with older methods. The copy() operation needs to be implementated for each transition phase.

In a language like python one can simply override the member functions on the live object. That is quite often easier and time- and space- efficient. For those who wonder: in java the class method table is effectivly a const object compiled into a code segment (same in c++) but in python the method table is copied for each object instance - into the __dict__ which every object has anyway.

One can simulate the latter with an embedded object of each node being the operation wrapper. Then we can exchange the operation wrapper being often similar to the anynomous class objects seen above with the callback mechanism explanations. It just looks wrong since the optable object (i.e. partial vtable) needs to store a backpointer to the parenting object in order to achieve the intended effect on the actual object, and the implementation of each of the worker functions can not rely on "this" but needs to use an explicit (back)reference.

Missing Typedefs

In some article about java it was argued that it is not actually needed. However real life shows a different matter of fact. The problem comes in when two packages declare the same class name. That is not so rare, a "Window" and a "Frame" are quite generic words - just as "Page" and "Item". If there would be "typedef" then it would be possible to declare local name from the fully qualified name:

  typedef java.awt.Frame WFrame;
  typedef net.phone.Frame PFrame;

The java language does not even support such a thing as special syntax upon "import" - most other languages allow that renaming upon import but in java you are bound to an error message when trying to import both names from the two packages - java does not want to have two different symbols under the same name.

  // import java.awt.Frame WFrame;
  // import net.phone.Frame PFrame;
  import net.phone.Frame; // can only import one of the Frame names

Note that the makers of Java/Swing were very much aware of this problem since a java.awt.Frame would hardly be usable in the same context as javax.swing.Frame - and therefore it is really called javax.swing.JFrame, and that's no joke. Yes, we see a doubled prefix. What if we run out of 26 single chars for differentiation, or two different third party modules pick up the same singlechar prefix for the names?

Oh btw, the same problem is not valid for constants or members of imported packages - the reference mechanism allows us to delcare a local reference variable and give it the actual reference to the single object by its fully qualified name. The local reference variable may have a truly differet name than the object within the ouside package.