Software development

Android development has its own Swift

Topics
Android, Functional Programming, programming, RxJava

In Apple's WWDC in June, Swift was announced as their new lean and mean language for developing iOS and OS X apps, meant to eventually replace Objective-C. A few weeks later, it was Google's turn to announce new exciting technology at their Google I/O conference. They had a lot of interesting developments to show, but not a programming language to replace Java for Android development. Although Google is known for creating languages, such as Go and Dart, there is no evidence they are preparing a language for Android, which is primarily Java-only.

In theory, you can develop Android applications in Clojure, Scala, Groovy, and JVM-based languages. However, because Android runs Dalvik bytecode and not Java bytecode, the aforementioned languages must go through an additional compilation step that modifies the program's bytecode. This normally ends up producing performance drawbacks visible to the end user. Even if this is optimized somehow, Android development tools for these languages (such as debugging) are still far behind Java for Android. These problems make those languages not yet ready for production. Except for a special little language called Xtend.

​

It is best understood as "CoffeeScript for Java", or as "Java 10". Xtend looks like Groovy, but it has the special property of being compiled into Java source code instead of bytecode. It is multi-paradigm, supports lambdas, functions are first-class objects, has type inference, optional semicolons, operator overloading, and other features. It is the only language I know which compiles to Java source code, which is its most important property for Android development. A quick teaser of lambdas:

button.onClickListener = [ clickedView |
    Toast.make(context, "Hello world!", Toast.LENGTH_LONG).show()
]

Compiles to:

final View.OnClickListener _function = new View.OnClickListener() {
    @Override
    public void onClick(final View clickedView) {
        Toast.make(context, "Hello world!", Toast.LENGTH_LONG).show();
    }
};
button.setOnClickListener(_function);

Xtend guarantees seamless interoperability with Java, which makes it a perfect match for Android development. The Java code it compiles to can be inspected and debugged as any other Java Android app can be. The generated Java code is not beautiful, but still understandable and maintainable. For these reasons, we decided to adopt Xtend in a new customer project. Our reasoning was that, if our experiment with this language wouldn't work out, we had at all times a plan B: take the generated Java code and continue developing with Java.

We never took plan B. In fact, we fell in love with Xtend. Mark Voit, the other developer in the project with over 15 years of professional Java experience, had some initial struggles learning the new syntax and new shortcuts. But after some weeks, he was well convinced that Xtend is cleaner and quicker.

The documentation gives a good overview of the language's features, but here are some highlights that are useful for developing Android applications.

Lambda

The cherry on the cake of Xtend. Enables a basic functional programming workflow. In Android development, it's common to use anonymous inner classes for click listeners and other similar listeners. As teased above in this article, an Xtend lambda is compiled to an anonymous inner class. The compiler infers which interface (or abstract class) should be implemented. Type inference also happens for the lambda's parameter. The Xtend jar also comes with some basic interfaces to represent generic functions and procedures.

Encourages final variables

You can declare local variables as var foo = 123, but if you are confident that this won't need to be reassigned, you can declare it as val foo = 123. The `val` keyword simply marks the variable as final, in the generated code. The compiler will also infer the type (in this case, integer). The injection of `final` is also used in other places, such as in method arguments, by default. Your code will end up having many final variables, which is a good property for applying functional programming techniques.

Null-safe feature call

Like in Groovy, you can avoid wrapping statements with an if checking for null, simply by adding the character `?`. Very good against `NullPointerExceptions`.

override onPause() {
    super.onPause()
    subscription?.unsubscribe()
}


Compiles to:

@Override
public void onPause() {
    super.onPause();
    if (this.subscription != null) {
        this.subscription.unsubscribe();
    }
}

This is specially important when you have many statements that need to be null-cautious. It can also be used to fallback to a default value, as such:

var zoom = this.cameraPosition?.zoom

Equivalent to:

float zoom = 0f;
if (this.cameraPosition != null) {
    zoom = this.cameraPositon.zoom;
}

Perfect language for RxJava

Reactive Programming has been gaining traction as a good solution to manage event-driven interactive apps. The state of the art library for this is RxJava, which we have used at Futurice. It is very common to see RxJava code using many anonymous inner classes as such:

Observable<Integer> totalSizeObservable = stringObservable
    .map(new Func1<String, Integer>() {
        @Override
        public Integer call(String s) {
            return s.length();
        }
    })
    .filter(new Func1<Integer, Boolean>() {
        @Override
        public Boolean call(Integer i) {
            return i > 0;
        }
    })
    .scan(new Func2<Integer, Integer, Integer>() {
        @Override
        public Integer call(Integer acc, Integer x) {
            return (acc + x);
        }
    });

In Xtend, all that boilerplate code can be hidden away like this:

val totalSizeObservable = stringObservable
    .map([ s | s.length ])
    .filter([ i | i > 0 ])
    .scan([ acc, x | (acc + x) ])

That is 19 lines versus 4 lines. Lambdas make developing Reactive applications a breeze, because they lower the cognitive cost of reading an anonymous function.

But lambdas are not the only Xtend feature good for RxJava. The language's name refers to one of its main features: Extension Methods. These enable you to augment a class with an external method, essentially adding a method to that class even though it has been closed already. An example:

class MyActivity extends Activity {
    // ...
    def void hide(View view) {
        view?.setVisibility(View.GONE);
    }

    override onCreate(Bundle bundle) {
        // ...
        myButton.hide()
    }
}

We used the MyActivity.hide() method as if it were actually View.hide(). This feature is very useful in RxJava, because you can simplify calls to your custom Observable operators. The RxJava way of calling custom operators is:

Observable<Foo> myObservable = originObservable.lift(new myOperator());

In Xtend, you can simply define the custom operator as a method, and call it as if it would belong to the Observable class:

val myObservable = originObservable.myOperator()

Is Xtend perfect?

You might be wondering what are the drawbacks of using Xtend, since there is no free lunch. The obvious drawback is that there is yet an additional compilation step when building the apk. It may or may not be an annoying to you. On a big enough Android project, it takes about 20 seconds to compile the whole project from Xtend to Java.

Perhaps the most disappointing disadvantage is a total lack of IntelliJ IDEA support, and hence also no Android Studio support. Xtend source code looks like plain text in IDEA, not even syntax highlighting is available. Xtend is primarily an Eclipse Foundation effort, and the code intelligence support in Eclipse is of course good and seamless. Xtend "just works" in Eclipse, and "just does not work" in IDEA.

That said, it is not a problem with Xtend language per se. It is an IDEA problem, and the least we can do at the moment is to upvote a feature request in IDEA. If you can use Eclipse, then use Eclipse for Xtend development. If you really dislike Eclipse, then do like me and develop Xtend in Sublime Text. It might sound crazy to program "Java" without an IDE, but I actually managed quite well without one. All I needed was the adb, gradle, a text editor, and the logcat in the DDMS. This gradle plugin makes it easy to compile Xtend for your Android project. Sure, life without an IDE is different, I often forget to import all the classes I need, but I got stuff done and enjoyed coding in Xtend.

Java is one of those languages that makes it hard to code without an IDE. Just think about the sheer amount of keyboard typing needed just to make an anonymous inner class for a click listener. IDEs make it easy by inferring what you want and inserting code for you. Surprisingly, all that magic isn't needed in Xtend programming because it is made to be simple, so it makes sense I managed to do work in a simple text editor.

Conclusion

Xtend resembles Swift. Both are modern multi-paradigm languages, with strong inferred types, functions as first-class objects, optional semicolons, map and filter built-in, simple variable and constant declarations (var and val, or var and let), string interpolation, powerful switch blocks, the `override` keyword, etc. The purpose of both is to speed up development, make it more intuitive, while also providing easy tools for what used to be rather cumbersome in Java or Objective-C: handling null errors and programming in a functional programming style.

Right now, we have Xtend code running in production in an Android app we recently delivered. Although measurements could be better evidence, we believe developing in Xtend was quicker than in Java, and to some extent, safer against null pointer exceptions. It is a nice language I can gladly recommend to anyone doing Android development, and who is open to program in Eclipse or some raw text editor like Sublime Text or Vim or Emacs.

If you zoom out, we also learned one important lesson when adopting new technologies in production: follow your gut feeling when a new technology seems promising and might optimize your development time, but always, always, have a plan B. Even if plan A works out perfectly.