Programming languages have advanced a lot in recent years. There's a countless number of flavours and concepts, and it's increasing every day. I've seen and actively worked with quite a lot of languages, but I find myself coming back to Java after a while. Even though I personally believe that Java is one of the cleanest out there with extremely strict and clear semantics, from time to time it feels a bit outdated and I wish Java had a couple of features from other languages. In this article, I want to present a run-down of "language features done right". The list is of course stronlgy opinionated, but maybe it contains a concept you haven't been aware of so far.
This is the first post in a series of posts. I decided to split them up for easier readability.
Features from Xtend
Xtend is a really interesting language. It considers itself to be an extension of Java, and it actually compiles to Java source (!) code. Xtend brings a lot of new ideas to the table. Here's a selection of my personal highlights.
Syntactical Sugar for Getters & Setters
We all know getters and setters in Java. We all know why we need them, and we also all know that they can sometimes be a pain, especially on the caller side. Why can't I just write obj.name
instead of obj.getName()
and let the compiler figure out the rest? Well, that's exactly what you can do with Xtend. If it encounters a member access (e.g. obj.name
) it first checks if there is a matching public member, and if there isn't, it looks for a get
property()
method and calls it. It gets even better if your property is a number, because then you can use obj.value++
and others, which will be compiled into suitable getter/setter constructions. Plain, simple, effective. It's entirely a compiler feature, no need to change existing bytecode or runtimes anywhere. Why Java isn't doing this today is a mystery to me.
Extension Methods
The Java Runtime library is really quite good. However, traditionally this library has been known to be rather... conservative in what it offers. "If in doubt, leave it out" seems to have been the guiding principle. The consequence is that we have an entire army of "productivity libraries" out there which offer the infamous XYUtils
classes (e.g. StringUtils
, IOUtils
...). The most well-known ones are the Apache Commons packages as well as Google Guava. These helpers are great as far as functionality is concerned, but the syntax is a bit counter-intuitive. For example, StringUtils.leftPad(myString, 10)
is so much less intuitive than myString.leftPad(10)
, and also in the first case the programmer needs to know about the existence of StringUtils
, whereas in the second case, the code completion of the IDE will just tell them. Again, this is an entirely syntactical matter. Compilers to the rescue! Xtend allows you to import a static method as an extension
(here's the documentation ):
import static extension java.util.Collections.*; // note the 'extension' keyword
// ... later in your code ...
myList.max(); // compiles to Collections.max(myList)
So far, Xtend is the only language that I have ever seen which got this feature nailed down right. That's how you deal with utils. It might seem like a ridiculously small change, but once you get used to this, you can't do without it. For javac
, this would really be a low-hanging fruit. I would even take it one step further and drop the extension
keyword. Any static method with a matching signature in my current scope should be eligible to be used as an extension method, with methods defined on the object taking precedence in case of name clashes.
Talking to classes without .class
One of the greatest (syntactical) pain points when writing reflective code is the fact that you are constantly writing .class
, for example in MyClass.class.getName()
. Well, duh. Of course want to talk about the class object when I write MyClass
. So why do I have to even write .class
here? Because the parser requires one look-ahead less? Come on. Xtend helps out here as well. Just writing MyClass.getName()
is enough and it compiles to MyClass.class.getName()
. Of course, MyClass.getName()
could also refer to a public static String getName()
method defined in the class. In Xtend, this static method would take precedence (you can qualify the access in Xtend to make it explicit). But let's be honest here: I've yet to define my first public static Field[] getDeclaredFields()
method in any Java class. Another small change, another step towards developer happyness.
The almighty dispatch
There's an issue with method dispatching in Java (and plenty of other languages, including C#) that goes unnoticed 99% of the time, but when it happens, it's really going to cause headaches for you. Imagine you have several overloads of a method like this:
public void print(Person p) { ... }
public void print(Student s) { ... }
public void print(Object o) { ... }
(Let's assume that Student
extends Person
). Quick quiz: which overload is being called by the following code:
Object me = new Student();
print(me);
The answer is print(Object o)
. Because Java selects the method overload to call based on the static type (i.e. the type of the variable, in this case Object
), rather than the dynamic one (the type of the actual value stored in the variable, in this case Student
). This is mostly for efficiency and alright in most cases, it's called "single dispatch". But what if you want the other case? You would have to do this:
public void print(Object o) {
if(o instanceof Student) {
print((Student)o);
}else if(o instanceof Person) {
print((Person)o);
}else{
// print the object like before
}
}
This code realizes what is called a "double dispatch", and writing this code is a pain. It's repetitive and overall something that I feel the compiler should do. Also, you need to take care to check the most specific type first. In Xtend, all you have to do to achieve the behaviour above is using the dispatch
keyword:
public dispatch void print(Person p) { ... }
public dispatch void print(Student s) { ... }
public dispatch void print(Object o) { ... }
... which will compile to exactly the instanceof
-cascade in print(Object)
as outlined above. Thank you compiler for saving me some typing!
Features from Ceylon
Ceylon is a JVM alien, i.e. a language which compiles to JVM byte code. It has a lot of features (some of them debatable), but it also has some gems in its language toolset.
Flow Typing
Flow typing is a feature which is used by many languages nowadays (hello, TypeScript!), but it was in Ceylon where I first witnessed this feature in its full form, and I was blown away. It's easy to explain what it does. We all have written code like this in Java:
for(Object element : myCollection){
if(element instanceof Dog) {
((Dog)element).bark()
} else {
// ... do something else
}
}
Did it ever strike you why you have to do the down-cast to Dog
in the example above after checking that element
is indeed of type Dog
? It's repetitive, it often means introducing new variables (for which you have to pick a proper name...), and overall it's just plain annoying to write. In Ceylon, the above cast is not necessary. At all. Once you verified that the object stored in a variable is of a certain class via a type-check, the variable will be treated as that type by the compiler while you are inside the conditional block. We don't even need new snytax for this feature. We just get rid of noise. Seeing this in practice for the first time was like magic to me - I never knew I wanted this, but now I do, and I frown about every down-cast I have to do after an instanceof
check.
Nullability and strict-nullness via ?
You know the first thing I do in 99% of my method implementations in Java? It goes like this...
public void save(User u){
if(u == null){
throw new IllegalArgumentException("User must not be NULL!");
}
// do ACTUAL work
}
This is widely considered to be good practice, and it is. Tracing a null
value backwards through the code base in the debugger is no fun at all. However, in the method above, I specify that I want a User
instance. Strictly speaking, null
is no User
(which you can tell, because null instanceof User
actually returns false
in Java). And yet, calling save(null)
is legal in Java. Ceylon provides an elegant way to fix this. By default, you cannot pass null
as argument, except if the argument specifically allows a null
value. This is done by appending a ?
to the parameter:
public void save(User? u){ ... } // accepts nulls
public void save(User u){ ... } // compiler rejects (potential) null values
The boon here is that in the second case, I as the programmer do not need to perform the manual null
-ness check at all. The compiler does it at compile time. So this method is not only safer, it is also faster at runtime and catches errors at the earliest possible point in time (the compile phase). Win-win.
Compiler-checked reflection
This is a unique feature that I haven't really seen anywhere else other than in Ceylon. Here's the original blog post where I discovered this gem:
value d = `value Movie.year`;
print( d.name); //prints "year"
In this case, Movie
is a class. We reflectively access the field Movie.year
. The Java equivalent would be Movie.getClass().getField("year")
. So how is Ceylon better? Well, if Movie
actually has no field named year
, the compiler will complain. This is a game-changer for reflective coding. How many times did your hibernate templates not work because a field name was mis-spelled? Or lost during a refactoring? There you go.
This concludes the first part of this series. I hope you enjoyed it, and maybe learned about a few new language features. Xtend and Ceylon have many other things to offer, and I strongly suggest checking them out, at least for the experience if not for production.
(The second part is now online.)