Adv

Retrolambda still going strong

Retrolambda is a library that has existed for more than 6 years and it is still actively maintained. While Android these days can support Java8 easily just by enabling it in the gradle scripts, there are alot of projects android and non-android that still rely on legacy code using older versions of Java.

The idea behind Retrolambda is to allow developers still using these old versions of Java to take advantage of the powerful functional features introduced a few years ago in Java8. These include:

  1. Lambda Expressions – Lambda Expressions are nameless functions given as constant values.
  2. Method References – they help point to methods by their names.
  3. try-with-resources etc

With Retrolambda you can use these features on Java 5,6 and 7. Retrolambda transforms the compiled Java8 bytecode into a format capable compatible with an older Java runtime.

With Retrolambda you can do something like this:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView)findViewById(R.id.textView);
        textView.setOnClickListener(view -> clickTextView(view));
        textView.setOnClickListener(this::clickTextView);
    }

    private void clickTextView(View view) {

    }
}

Now wait…if you’ve been using Java8 in your code then this isn’t a big deal. However if you will not find anything like above in legacy code.

In the line below we are using Lambda expression:

        textView.setOnClickListener(view -> clickTextView(view));

While in this one we are using method references:

        textView.setOnClickListener(this::clickTextView);

Installation

To run Retrolambda using Maven, add the following to your pom.xml:

<plugin>
    <groupId>net.orfjackal.retrolambda</groupId>
    <artifactId>retrolambda-maven-plugin</artifactId>
    <version>2.5.6</version>
    <executions>
        <execution>
            <goals>
                <goal>process-main</goal>
                <goal>process-test</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Features

Hera are the features Retrolambda will allow you take advantage of in your legacy code:

No. Feature Description
1. Lambda expressions are backported by converting them to anonymous inner classes. This includes the optimization of using a singleton instance for stateless lambda expressions to avoid repeated object allocation.
2. Method references are basically just syntax sugar for lambda expressions and they are backported in the same way.
3. Try-with-resources statements are backported by removing calls to Throwable.addSuppressed if the target bytecode version is below Java 7. If you would like the suppressed exceptions to be logged instead of swallowed, please create a feature request and we’ll make it configurable.
4. Objects.requireNonNull calls are replaced with calls to Object.getClass if the target bytecode version is below Java 7. The synthetic null checks generated by JDK 9 use Objects.requireNonNull, whereas earlier JDK versions used Object.getClass.

Optionally also:

Default methods are backported by copying the default methods to a companion class (interface name + “$”) as static methods, replacing the default methods in the interface with abstract methods, and by adding the necessary method implementations to all classes which implement that interface.

Static methods on interfaces are backported by moving the static methods to a companion class (interface name + “$”), and by changing all methods calls to call the new method location.[1]

More Snippets

Lambda Expression

findViewById(R.id.go).setOnClickListener(v -> {
   final int index = mActionSpinner.getSelectedItemPosition();
   if (index != Spinner.INVALID_POSITION) {
       action(actions[index]);
   }
});

Method References

int cmp = Objects.compare(word, other.word, String::compareToIgnoreCase);

with StreamAPI

return Stream.of(lines)
     .map(str -> str.split("t"))
     .filter(arr -> arr.length == 2)
     .map(arr -> new Word(arr[0], arr[1]))
     .collect(Collectors.toList());

Switch statement for string

switch (action) {
    case "filter 1":
        // Filter one word
        stream = stream.filter(p -> p.getWord().split(" ").length == 1);
        break;
    case "filter 2+":
        // Filter two and more words
        stream = stream.filter(p -> p.getWord().split(" ").length >= 2);
        break;
    // ...
}

try-with-resources

final List<String> lines = new ArrayList<>();
try (final InputStream is = context.getAssets().open("words.txt");
     final InputStreamReader isr = new InputStreamReader(is, "UTF-8");
     final BufferedReader reader = new BufferedReader(isr)) {
    String line;
    while ( (line = reader.readLine()) != null ) {
        lines.add(line);
    }
}

Objects

@Override
public boolean equals(Object o) {
    // ...
    final Word other = (Word) o;
    return Objects.equals(translate, other.translate) &&
           Objects.equals(word, other.word);
}

@Override
public int hashCode() {
    return Objects.hash(word, translate);
}

Exceptional

return Exceptional.of(() -> {
    final InputStream is = context.getAssets().open("words.txt");
    // ... operations which throws Exception ...
    return lines;
}).ifException(e -> Log.e("Java 8 Example", "Utils.readWords", e))
  .getOrElse(new ArrayList<>());

Download Full Android Example. The app shows how to use Java 8 features with Retrolambda and Lightweight-Stream-API.

Share
Adv