Create classes taking advantage of lambda

Asked

Viewed 782 times

19

After reading this question made by utluiz, I could understand, even if in a more superficial way, a little of the usefulness of the expressions lambda added to JDK 8.

However, in addition to the cited examples of how to simplify resources that the language already has (such as sorting using Collections and the use of Runnables, where netbeans itself already suggests modification in some cases), I can’t imagine a scenario where I could make own implementations that take advantage of lambda.

In what kind of scenario could I take advantage of the lambda, implementing my own classes, making use of this resource?

  • 3

    I don’t know what you want as an answer but, briefly, "Both are constructs that make it possible to pass a code as a parameter." (jpkrohling, in this answer), then use them in that scenario.

  • 3

    Lambdas are the last cookie in the package and a language without them is poor, took too long to enter Java and as a result has a legion of devs who do not understand. The utilities are endless and the codes of one of the classic books of computing Structure and Interpretation of computer Programs is almost entirely done over Ambdes (although the base language is Lisp the examples for any one that has Ambdes).

  • Good question. It’s one thing to know what it is and how to use it with the existing API. It’s quite another to make use of the resource in your own API. However, understanding Latin is very simple even for those who have never heard of it, as long as the concept of listeners or callbacks and anonymous classes is familiar. I’ll add an answer soon.

  • Last month I made some changes to the code to remove an ill-defined inheritance. It was pretty cool, I’ll see if I post tomorrow here

1 answer

23


Understanding and using Java is very simple even for those who have never heard of it, as long as you have a good foundation on some aspects of the language, concept of listeners or callbacks and anonymous classes.

Initial consideration

A lot of people say that Ambdas were missed a lot in Java until version 8, but I have two placements to make about it:

  1. Lambdas are syntactic sugar, the same could and was done using interfaces with a method.
  2. Syntax is not everything. See the case of Javascript, for example, you can simulate Windows passing functions, but there is no ease in syntax.

I will explain below some types of problems that were solved using interfaces and then I will describe equivalent solutions using Amblas.

Later, I will describe some scenarios where Both are useful when designing and using Apis.

At the beginning there were interfaces

I’ll use two examples.

Time-consuming processing

At first we have a routine that does a time-consuming processing and needs to do something custom at some point.

The interface:

interface MeChameQuandoAcabar {
    void acabei();
}

The method:

void processamentoDemoradoAssimcrono(MeChameQuandoAcabar callback) {
    new Thread() {
        @Override
        public void run() {
            //faz algo muito demorado
            callback.acabei();
        }
    }.start();
}

Basically the method executes something in a thread and, at the end, calls the method acabei() of our functional interface.

To call the processing method we must create a class implementing the interface. However, it is worth remembering that, in addition to the regular classes, Java still has internal classes and anonymous classes.

With this in mind, we can do something close to the Ambdas using anonymous classes:

processamentoDemoradoAssimcrono(new MeChameQuandoAcabar() {
    @Override
    public void acabei() {
        System.out.println("fui...");
    }
});

And then the code above will print fui... when processing is over.

Custom processing

Another possibility for you to use Windows is when you want to implement something that resembles the Strategy Pattern, that is, you have some algorithm and one or more steps of it can be customized.

The interface:

interface FacaIssoTambem {
    String fazerAlgoAMais(String s);
}

The method:

static List<String> facaAlgumaCoisa(List<String> lista, FacaIssoTambem m) {
    List<String> result = new ArrayList<>();
    for (String s : lista) {
        s = "prefixo:" + s;
        s = m.fazerAlgoAMais(s);
        result.add(s);
    }
    return result;
} 

Basically the method takes a list of strings and adds a prefix to each item, returning a new list.

With our functional interface, you can do something else. Example:

facaAlgumaCoisa(Arrays.asList("1", "2", "3"), new FacaIssoTambem() {
    @Override
    public String fazerAlgoAMais(String s) {
        return s + ":sufixo";
    }
});

The above call will return a list with the items [prefixo:1:sufixo, prefixo:2:sufixo, prefixo:3:sufixo].

Then came the Ambids

A lambda in Java is nothing more than a friendlier way to declare an anonymous class that implements an interface called functional interface.

A functional interface contains only one method and it is good practice to write it down with @FunctionalInterface, but only to make the intention explicit in the code as it works even without it.

Thus, the two examples above could have the calls to methods replaced by Ambs.

Time-consuming processing

The new call could be like this:

processamentoDemoradoAssimcrono(() -> System.out.println("fui...")); 

The lambda, () -> System.out.println("fui...") means the following:

  • () says there are no parameters
  • -> separates the declaration from the "body" parameters of the method
  • the rest is the body of the method, which if it is only a command does not need to be declared as a block using { and }.

Basically, this is the same as instantiating an anonymous class and implementing the existing method.

Custom processing

The new call could be like this:

facaAlgumaCoisa(Arrays.asList("1", "2", "3"), s -> s + ":sufixo")

In that case, the lambda is s -> s + ":sufixo".

Just as in the previous example, it is nothing more than an anonymous implementation of the interface FacaIssoTambem and its method fazerAlgoAMais.

So we have the parameter s lambda being equivalent to the method parameter on the interface. Similarly, the expression s + ":sufixo" returns a String, which is the type expected by the return of the method.

Do this differently and you will see a compiler error. lambda has to declare the parameters and return value in a way compatible with the interface method, as if you were actually overwriting the method.

Other applications

Decorate execution of a code snippet

Another interesting application includes in "decorate" code snippets with some common functionality.

An example that I find very interesting is to execute commands within a transaction without soiling your code with a lot of try/catch/finally:

executarComTransacao(() -> {
    updateTabela1();
    updateTabela2();
});

And then you implement the above method by creating a transactional control logic that will be used across the system.

Anyway, whenever you have a specific form of code execution that involves a common logic (asynchronous, demarcated, conditional), Amblas are interesting to facilitate the use.

Conditional execution

Another example involves conditional code execution.

Let’s assume that in several locations of a system you have to perform conditionally certain actions that are different but all based on the same condition.

You could repeat ifs everywhere like this:

if (condicao) fazIsso();
...
if (condicao) fazAquilo();

An alternative with Lambdas would be:

executarCondicionalmente(() -> fazIsso());
...
executarCondicionalmente(() -> fazAquilo());

The method could be something like this:

public void executarCondicionalmente(Runnable r) {
    if (condicao) r.run();
}

Note that the type of interface or method name only matters where you are declaring. From a lambda’s point of view, it doesn’t matter.

Postpone execution

You can also use Bins to express values you don’t want to be calculated before the call to method.

A simple example is in the API Optional<T>.

Suppose you want to upload data from somewhere and this can be a time consuming operation. If the routine does not find the data, the system should then load a backup.

The traditional approach would be something like this:

String data = loadHugeData();
if (data == null) {
    data = loadHugeDataBackup();
}

Note that we do not want to call the backup method unless the return of the first method is null.

If we think a little more about functional programming, we can refactor the methods using Optional, thus:

Optional<String> loadHugeData() {
    return Optional.ofNullable(...);
}

So main logic can be simplified like this:

String data = loadHugeData().orElseGet(() -> loadHugeDataBackup());

The idea of Optional is that you never return null. If there is no data, it must return an empty value.

When you call the method orElseGet passing a lambda, the Optional will check if it has a value and, if it has, will return it immediately. Otherwise, it will then run the lambda and recover the backup data.

  • 1

    If it is necessary to pass 2 parameters, it has to be within the () thus: (param1, param2) -> System.out.println(acabou...)? Or there is no such thing?

  • 2

    @Diego That’s exactly it. Just use parentheses.

  • Thank you very much for the idea of executarComTransacao!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.