This page is an attempt to collect the worst Java virtual machine deficiencies into one place.
Also called genericity by some.
There is an effort by people to define a system of Java source language genericity which can be compiled to standard Java byte code. This forces certain speed versus space tradeoffs to be made by the programmer doing the initial compile, not by class loader which much better understands the tradeoff in this instance of the virtual machine.
Gilad Bracha (now leading the genericity effort at Sun) defined mixins for object orientd programs.
As Bracha defined them mixins are effectively a generalisation of single inheritance which gets close to multiple inheritance but avoids many of the problems of multiple inheritance.
Single inheritance forces too many system implemented features to be defined on java.lang.object because arbitrary combinations may be required in objects in the system. Striking examples are hashcodes, locks and wait/notify lists.
The definition of classes in Java implicitly define an interface. While the existance of a primary interface allows the overhead of interface redirection to be avoided for many calls and is therefore a good thing(tm). The fact that this primary interface is anonymous and cannot be referred to makes it almost impossible to automatically verify that two classes implement the same interface.
The obvious use for multiple implementations is to create simple secure virtual machines without stack inspection - different security contexts get different implementations. The limitations of single inheritance stops abstract classes being an effective solution.
The Java VM has no support for inner classes so compilers totally break security by providing generally accessible external accessor functions for private internal fields in order to allow inner classes access.
This is a serious security problem which should be able to be addressed at the class file format level (ie without bytecode changes) by mutual acknowledgement of the relationship by each pair of inner and outer classes. Such a design probably requires that mutual acknowledgement include some notion of principal to avoid spoofing attacks.
Packages are necessary because there are some associations or relationships between classes that cannot be captured by inheritance. This is not unique to Java, for example: Oberon has modules (cleaner than packages) and C++ has friend functions (much uglier that packages).
Since packages are a necessary part of the Java security model. Proper care should have been given at the class file level to the proper definition of packages. The manifests in Jar files are an ugly patch.
Because packages are a critical part of the security model it is doubtful that renaming and multiple instantion of isolated classes outside their package is secure. Consequently packages need to be sufficiently integrated that manipulation of packages is not so complex as to be error prone.
Since packages consist of a collection of typed classes, each package can be thought of as having a type consisting of the public interfaces of the set of publicly accessible classes. In particular the package interface does not include the package private methods of the classes which make up the package (as the class interface does not include the private methods which are part of the class).
Given that packages are so poorly handled it shoudl come as no surprise that there is no easy way to manipulate, use or even access the type of a package. Which makes multiple instantiation very complicated.
The idea of tail-call optimisation is that any function whose return result is the result of some second function, should not call the second function and then return the result, but call the second function in a way that the second function returns the result to the caller of the first function.
The benefit is that from the point of calling the second function the stack frame of the first function is no longer required and its space can be re-used; typically it is reused by the second function.
This can save lots of stack space in recursive functions.
Supporting tail-call optimisation would remove an unnecessary implementation restriction stopping programmers from using a natural problem solving approach.
The implementation of tail-call optimisation in such a dynamic language as Java is problematic, so it is likely to always be restricted to methods for which the stack requirements (not just the parameter list) are known in advance. In general these methods are the non-virtual methods of the current (self) object and the (final ?) class methods of the current objects class or its ancestors.