This is a list of what I consider flaws in the design of the Java language.
- byte is signed. You almost never want a signed
byte. Further, there is no unsigned byte. ubyte would be trivial to
implement to supplement byte. It would require no
changes to the JVM (Java Virtual Machine). It would catch all manner of bugs forgetting to
do & 0xff after a byte load.
- for each requires you to specify the type of the
indexing variable. The compiler knows that from the type of the set it iterates
over. This means needlessly propagating type information in more than one place in
your program. Joerg Meier points out that the Lombok language has the var type which requests the compiler infer the type. That would
solve this problem.
- The syntax to invoke a callback with an anonymous class instance delegate is
embarrassingly verbose. Lambda expressions in Java 1.8+ fix this.
- Java has too many levels of precedence. It is easy to misread what code does.
This could be fixed with an IDE (Integrated Development Environment)
that could display ghost () or variable size type to make clear what the implied
precedence is.
- Java chose random names for methods to convert from one type to another. French
verbs are regular by comparison. There should be a new Convert class with regular names and the deprecate all the goofy
ones. There would be a utility to convert all the crazy old names to the sensible
new ones.
- Conversions require hard coding in the types of the target and source
primitives in many places in the code. You should have to specify the type in only
one place, where the variable is declared.
- Java overuses {}. If you drop a } you can spend 15 minutes trying to get
them balanced again. If methods, classes, loops, etc had unique end markers, there
would be no problem. If methods, classes, loops etc had unique visual markers at
begin and end, (perhaps small icons generated on the fly by the
IDE), code would be much easier to read and scan.
- You should be able to declare a temporary variable as having the same type as
some other variable, so that for example if the original variable changes from
int to long, the
temporary will automatically change too.
- Java uses + for both concatenation and addition. The
ambiguity leads to all kinds of strange bugs.
- You should be able to have arrays of Objects, not
just arrays of references to Objects so you could do an
efficient Complex class.
- import static makes no sense. There is nothing
static about importing individual methods and
variables. Make the word static optional.
- I would like it if methods I write on Strings are
invoked with the same syntax those Sun/Oracle wrote. Obviously, my methods should
not have access to the private variables. But I
should, for example, be able to say s.isNumeric() where isNumeric is a method
I wrote.
- Type erasure to implement generics 100% at compile
time was a cheap trick. It created all kinds of restrictions that make sense in
terms of type erasure, but not in terms of problem solving.
- Java does not have an official ANTLR (Another Tool for Language Recognition)
grammar definition.
- Today == works on interned
Strings but not non-interned, though the syntax does not
stop you from using it on non-interned Strings. It is a
subtle error since == often works anyway. I would hope
there could be a way to tell the compiler which Strings
are interned and which are not and have it enforce
that rule and automatically use a.equals(b) when you specify == for non-interned Strings.
If I were starting over designing a replacement language for Java from
scratch, I would use := with for assignment, = for the equality operator (what we use equals for now), <> for the
inequality operator (what we use !equals for now),
< > <= >= for comparison, (what we use
!compare for now). I would use === for the identity operator, meaning references point to the same
object. === would not be defined on primitives.
== would be undefined so that previous Java programmers
could not use it without thinking.
- I don’t like the way interfaces force all
implementation to be public. The interface should just
specify the minimum scope.
- This is not so much a blunder but a request for a major overhaul. Writing
graphics applications is 100 times more tedious than they
were in DOS (Disk Operating System) using Abundance and they are not nearly as
responsive to the user. The problem is the application specifies incredible detail
in the application. There should be something analogous to
CSS (Cascading Style Sheets), so that layout logic and business logic are
completely separate. Verification and prompting should be automatic, not
requiring any application code. You should just have to specify the fields you
want displayed and like HTML/CSS they should be automatically laid out according to style rules. As real estate grows
and shrinks, the layout should change to show more or less detail, longer or
shorter fields (all without application code). I talk more about how this could
work in my Bali essay. There need to be
auto-verifying types for international and national business objects like
countries, provinces/states, phone number, postal codes, currency, timezones,
dates, surnames. You should have automatic cross checking of address components
and postal code/phone number lookup.
- Java is far too fragile. It requires far too much user competence to install a
JVM, get Applets
configured and get Java Web Start working before you can even think about
installing an app. The end user should just have to install a small C/C++ program.
It then makes sure the latest JVM
is
installed and if not installs it. It can then install a smarter, bigger installer
written in Java. It then sets up the associations for *.jar and *.jnlp files. It then visits each
browser and configures it to run Applets and Java Webstart. If checks to make sure
all files are present and have the expected checksum. If not it downloads files to
repair them. It checks and repairs any registry entries. There should be a freeware
installer so that Java can be made to look like familiar trusted exe installs.
Some of these problems could be fixed with an IDE
or compatible language changes. Others, notably simplifying the precedence, would
require defining a new language or an annotation to declare new syntax rules were in
effect.
Leif Roar Moldskred
Leif Roar Moldskred posted his list of blunders:
- Octal notation (what were they thinking?! Or smoking, for that
matter?)
- Using = for assignment
- Bracket-less conditionals and loops
- In fact — copying the syntax of C in general. I think
Gosling and Joy tried for superficial resemblance to C to suck programmers into
thinking it would snap to learn Java.
- Having an operator for reference equality but not value equality (.equals() )
- Defaulting to package access level instead of
private
- No out parameters
- No named parameter syntax
- System.exit() rather
than just making the main method return an int
- No Elvis-operator or similar
(it would make logging logic a lot more pleasant to write.)
- java.io.File
- java.util.Date
- I/O resources where .close() can throw
IOException.
- No immutable array type or reference type
- HTML (Hypertext Markup Language)
as the markup language for Javadoc
- Basing the OO (Object Oriented)
inheritance mechanism on the type system (not an error limited to Java, of
course.)
Joshua Cramner
Joshua Cramner posted his list of blunders:
- signed byte and signed short to a lesser degree. Sub-integer types should have always
been unsigned even if unsigned types weren’t a fixture of the language.
- 16-bit char. Unicode is a steaming pile of insanity and extremely difficult to
work with properly. 16-bit chars makes the problem even worse. (Granted, Java
predates the Unicode oops-we-screwed-up that made the mess, but it’s a blunder
that would need to be fixed if it could).
- Non-reified generics.
- Default fallthrough switch statements. There are very few times you want to do:
- & | ^ should have higher precedence than
== != et al. Just because C did it doesn’t mean
it’s a good idea.
- List.class should be
Class<List<?>>, not Class<List>. There’s a
difference between those two types and it’s not fun when you discover
it.
Now of the complaints you listed, I’ll make these points:
- I think languages work best if you need to specify a type name exactly once,
e.g.,
Foo x = new Foo();
is one too many times to say Foo.
auto x = 2;
is also one time too few, IMHO (In My Humble Opinion)
.
- Precedence is useful, although the compiler can (and should!) warn you if you
start getting to the confusing parts of precedence†.
- On the balance, I think matching curly braces is superior to begin/end-ish
constructs. If you’re spending too much time trying to figure out where you
have an extra or a missing curly brace, then, quite frankly, your
IDE
is defective. My IDE
(Vim) points out matching curly braces and can even fold them based on appropriate
matching and it has been able to do so since before Java was invented. Possibly
since before I was born, even.
† One quote I remember hearing from a talk is a complaint that too many PL
designers have never quite understood the value of a warning as opposed to an
error.
Here are a few possibly idiosyncratic issues I have. I don’t anticipate
th= at any except the last could conceivably be implemented.
Tom McGlynn
Tom McGlynn posted his list of blunders:
Integer promotion of byte, short and char types: This really
aggravates the problem of bytes being signed. I’ve had to clear
sign-extensions from promoted bytes or shorts countless times. The semi-supported
status of these types has lots of ramifications. The special rules for
x += y;
where x is a byte and y is an integer is just one.
I see the problem with byte, but
you can use char is instead of short if you want on unsigned 16-bit quantity.
- The vast mess that is Java IO and NIO (New Input/Output)
with a myriad of classes that do almost the same thing. Streams and Readers and
Channels and RandomAccessFile that do all off on its own and Buffers and Formatters and Charsets and Selectors and … My
suspicion is the underlying problem here is that we’ve decided that I/O
somehow is read out of the language itself — it’s an add-on library.
Doing I/O is critical to essentially all programs but thinking that some syntax
beyond method calls might be useful in making what how program does I/O clear is
beyond the pale. I suspect that the desire to support printf-like calls was one
of the big drivers for variable length argument lists but that’s not even
scratching the surface.
Statements with side effects: This infection has been virally grafted into
core genes of many C-based languages. While the ability to do:
a += b;
is a tremendous boon and quite clear, the added benefit of:
a += b += c
+= 20;
versus:
b += c ;
a += b ;
is lost to me. The first suggests the wrong order. The second is clear and
unambiguous.
The prime vector in this contagion are the increment and decrement operators,
which I completely eschew. (I use x += 1 or x
-= 1 instead). So
increments, decrements and assignments should (IMHO
) be
statements, not values.
The switch statement. The problems with fall
through have already been noted, but it also seems silly that switches are restricted to a limited set of types. One should be
able to switch on any value: String, double, Objects, enumerations
…, Just use == [for primitives] or equals() [for Objects] to find the
appropriate case. Sometimes this is just syntactic sugar for a nested
if, but that’s fine. The nested if’s
hide the constraints, while a switch can be much clearer. I don’t see why
the compiler can’t easily optimize the integer and enumeration switches but
leave the language more powerful and simpler (i.e., there are fewer special
cases).
For Objects it could create a
perfect HashMap. In my own home brew language,
Abundance, I allowed a switch with a series of Boolean expressions e.g.
x.startsWith and it
evaluated them in turn. I also allowed ranges. They could be evaluated with a
binary search.
Gene Wirchenko
Gene Wirchenko posted his list of blunders:
- unsigned integer types. I once spent a bunch of time debugging code to convert
a.b.c.d to a 32-bit integer for a network program because of this lack.
- a switch that allows for arbitrary expressions.
- a well-performing String type. String has + and +=
which suit what I like to do, but Strings have a
performance penalty.
- less repetition. This is horridly verbose when the typename is long:
SomeType a=new SomeType();
- == for String values that
compare String values. a.equals(b) is
silly.
Wanja Gayk
Wanja Gayk posted his blunder:
- The introduction of final instead of var. It should be exactly the other way round, as the majority of
declarations can be made final anyway (note that
I’m not talking about methods and classes): All declared references and
values should be final by default, only if you declare
them as var should the compiler allow you to change
them.