Front End — Back End Interactions As Events In Java 8+ | Method References, Functional Interfaces

This article is a continuation of a previous one on modelling UI Interactions with events. If you are interested in applying Event Driven Programming to your code, please read that article first for a more complete picture: Simplify Your UI Interactions With Events | Java, Kotlin, Any Language

Image for post
Image for post

The picture above is from my early notes (circa 2017) on software architecture, asynchronous data streams, and event driven programming. I was in the midst of struggling to implement RxJava 2 in a multi-module clean architecture Android application; while at the same time trying desparately trying to practice test driven development in my code.

If that sounds quite esoteric, rest assured that the general principles and small patterns I will discuss in this article are much simpler. Keep that picture in mind though; all of these concepts can work together.

In this article, we will look at:

  • How to model front end to back end communication with a Continuation Interface (a pattern we see in tools like Kotlin Coroutines or RxJava)
  • How to combine some powerful features of Java 8+ (we can now use them in Android too; keep reading) such as Functional Interfaces, Method References, and Lambda Expressions to achieve the same goal

Functions Instead Of Models

As I mentioned in the previous article, a simple approach to modelling events is to use some kind of a fixed data structure, such as classes and enums…

…or purpose built language features like Kotlin’s sealed classes:

I find this pattern particularly useful in modelling front end UI interactions, but when it comes to interactions between the back end and front end (FE-BE), I like to do things differently.

With some notable exceptions which might require paging or managing buffered data streams, most of the FE-BE code that we write is pretty straight forward:

  1. Ask one or more IO devices for some data (local databases or a remote REST APIs are all IO devices)
  2. Successfully receive the data requested or fail with an exception

This is a different problem than UI events, which implies that different patterns might help.

A continuation is just another word for a callback. To achieve this simple approach, define some kind of interface which is capable of representing FE-BE interactions:

Using this approach is pretty straight forward. Assuming you understand interfaces and Generic Types (what the T is). If any of this is fuzzy, I cover all of this and much more in my comprehensive introduction to Java programming.

Here we have an IO device…

…and the interface that it implements, which uses Continuation as a method parameter:

When we want to actually execute a FE-BE request, our client/caller, which is probably a front end logic class (could be a Controller, Presenter, ViewModel etc.), calls the interface function and supplies a Continuation:

As you can see here, the two events we are concerned about, whether we got the data from the IO Device(s) or not, are modelled as functions instead of data structures (if you will allow me some liberty with definitions).

In this example, I used the Java Executors framework to manage background/mainThread concurrency; let us see how this works on the other end:

Technical details of concurrency tools aside, the point is that we “continue” the execution of the program in the front end, by having the back end call back to it via this continuation.

Java 8+ has some cool features that did not play nice with Android for a number of years. If you have avoided using lambda expressions, functional interfaces, method references, and useful types like Optional, this article will help you sort that problem out.

In this case, we will combine functional interfaces with method references to achieve a very similar outcome; that arguably looks nicer. I did not have any real source code handy for this, so this will be bordering on pseudocode but still compilable.

First, our interface now uses the Consumer<T> functional interface for success and failure events:

You will need to pick the correct functional interface for the problem; but this can be worked out by reading the documentation. A Consumer<T> represents “an operation that accepts a single input argument and returns no result.” If it needed an argument like a taskId, then we would want to use Function<T,R>.

On the back end, our storage device calls the appropriate function based on whatever events occur:

Finally, in our calling class, we make use of method reference syntax to supply our functions:

If you are fond of lambda expressions, you could of course use them instead:

At the end of the day, the differences between these approaches are pretty minimal, but Java is a HORRENDOUSLY VERBOSE language, so anything we can do to fix that is a bonus.

Or just switch to Kotlin.

Social Media | Support

This article was written by Ryan Michael Kay. I am a self-taught programmer/engineer who creates educational content for on a wide variety of topics, on a wide variety of platforms. The best way to support me is to follow me on various platforms and join in with my developer community (we have hundreds of members!):


Tutorials & Courses:

Free Tutorials, Live Q&A, Live Coding:

Java Desktop Programming w/ JavaFX (Intermediate) —

Complete Beginner Introduction To Java Programming (Beginner — Intermediate) —

Android Apps With Kotlin & Android Studio (Beginner) —

Material Design Android Programming w/ Kotlin (Intermediate) —


Discord Group —

Slack Group —


Written by

Self-taught software developer & student of computer science.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store