Skip to content

Latest commit

 

History

History
413 lines (363 loc) · 14.4 KB

File metadata and controls

413 lines (363 loc) · 14.4 KB

Interfaces Funcionais Pré-Construídas (Built-in Interfaces)

Objetivo
Develop code that uses the built-in interfaces included in the java.util.function package, such as Function, Consumer, Supplier, UnaryOperator, Predicate, and Optional APIs, including the primitive and binary variations of the interfaces
-
Desenvolver código que usa as interfaces embutidas no pacote java.util.function, como Function, Consumer, Supplier, UnaryOperator, Predicate e a API de Optional, incluindo as variações de tipos primitivos e binários das interfaces

Interfaces Funcionais

O Java 8 possui algumas Interfaces Funcionais já criadas. Elas provavelmente serão suficientes para a maioria dos cenários onde é usual utilizar expressões lambda, de tal forma que não deve ser muito comum você precisar criar uma nova.

É fundamental entender esses exemplos para dominar a utilização de expressões lambda, e para entender a próxima seção sobre referências a métodos.

Supplier
  1. Supplier é uma interface funcional que não recebe nenhum parâmetro de entrada, mas retorna um valor. Sua definição na JDK é a seguinte:

    java.util.function.Supplier<T>
    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
  2. Uma implementação possível para um Supplier é um gerador da data atual:

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierExample.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierExample.java[role=include]
    Saída no console
    2019-07-08

    A saída no console irá imprimir a data atual em que este código foi escrito.

    Perceba que a expressão lambda está simplificada, sem chaves {} ou return. Caso tenha dúvidas com relação a isso, consulte novamente a seção sobre expressões lambda.

  3. Um Supplier pode ser utilizado para fornecer uma função custosa em termos de processamento, para que seja chamada apenas se for necessário:

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierUseCase.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierUseCase.java[role=include]
    Saída no console
    Menor de idade!
    Maior de idade! Validação realizada às 2019-07-09T00:21:35.488

    Perceba que neste caso o supplier só precisou ser chamado na segunda vez, evitando uma execução desnecessária da expressão lambda.

  4. Existem interfaces Supplier para lidar tipos primitivos: BooleanSupplier, IntSupplier, LongSupplier e DoubleSupplier.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierPrimitive.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierPrimitive.java[role=include]

    O resultado na console será imprimir o int primitivo gerado aleatoriamente.

Consumer e BiConsumer
  1. Consumer é uma interface funcional que recebe um parâmetro de entrada, e não retorna nenhum valor. Sua definição na JDK é a seguinte:

    java.util.function.Supplier<T>
    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }
  2. BiConsumer é uma interface funcional que recebe dois parâmetros de entrada, e não retorna nenhum valor. Sua definição na JDK é a seguinte:

    java.util.function.Consumer<T>
    @FunctionalInterface
    public interface BiConsumer<T, U> {
        void accept(T t, U u);
    }
  3. Implementações possíveis para Consumer ou BiConsumer são funções que imprimem informações no console:

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_ConsumerExample.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_ConsumerExample.java[role=include]
    Saída no console
    2019-07-08
    2019-07-08
    22:37:39.229
  4. Existem interfaces Consumer para lidar tipos primitivos: DoubleConsumer, IntConsumer, LongConsumer, ObjDoubleConsumer, ObjIntConsumer e ObjLongConsumer.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_ConsumerPrimitive.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_ConsumerPrimitive.java[role=include]
Predicate e BiPredicate
  1. Predicate é uma interface funcional que recebe um parâmetro de entrada e retorna um valor booleano. Sua definição na JDK é a seguinte:

    java.util.function.Predicate<T>
    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
  2. BiPredicate é uma interface funcional que recebe dois parâmetros de entrada e retorna um valor booleano. Sua definição na JDK é a seguinte:

    java.util.function.BiPredicate<T>
    @FunctionalInterface
    public interface BiPredicate<T, U> {
        boolean test(T t, U u);
    }
  3. Implementações possíveis para Predicate ou BiPredicate são funções que verificam se o valor de entrada é igual ao valor sorteado:

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_PredicateExample.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_PredicateExample.java[role=include]

    A saída no console é aleatória, pois depende do valor sorteado. Um valor possível seria false e true.

  4. Existem interfaces Predicate para lidar tipos primitivos: DoublePredicate, IntPredicate e LongPredicate.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_PredicatePrimitive.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_PredicatePrimitive.java[role=include]
Function e BiFunction
  1. Function é uma interface funcional que recebe um parâmetro de entrada e retorna um valor. Sua definição na JDK é a seguinte:

    java.util.function.Function<T, R>
    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
    }
  2. BiFunction é uma interface funcional que recebe dois parâmetros de entrada e retorna um valor. Sua definição na JDK é a seguinte:

    java.util.function.BiFunction<T>
    @FunctionalInterface
    public interface BiFunction<T, U, R> {
        R apply(T t, U u);
    }
  3. Implementações possíveis para Function ou BiFunction são funções que multiplicam os valores fornecidos:

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_FunctionExample.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_FunctionExample.java[role=include]
    Saída no console
    7.5
    30.0
  4. Existem várias interfaces Function para lidar tipos primitivos: DoubleFunction, DoubleToIntFunction, DoubleToLongFunction, IntFunction, IntToDoubleFunction, IntToLongFunction, LongFunction, LongToDoubleFunction, LongToIntFunction, ToDoubleBiFunction, ToDoubleFunction, ToIntBiFunction, ToIntFunction, ToLongBiFunction, ToLongFunction.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_FunctionPrimitive.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_FunctionPrimitive.java[role=include]
    Saída no console
    7.5
    30.0
UnaryOperator e BinaryOperator
  1. UnaryOperator é uma interface funcional que recebe um parâmetro de entrada e retorna um valor do mesmo tipo da entrada. Sua definição na JDK é a seguinte:

    java.util.function.Function<T, R>
    @FunctionalInterface
    public interface UnaryOperator<T> extends Function<T, T> {
    
    }

    Perceba que não existe método abstrato declarado, pois ela apenas estende a interface Function já existente.

  2. BinaryOperator é uma interface funcional que recebe dois parâmetros de entrada do mesmo tipo, e retorna um valor do mesmo tipo das entradas. Sua definição na JDK é a seguinte:

    java.util.function.BiFunction<T>
    @FunctionalInterface
    public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    
    }

    Perceba que não existe método abstrato declarado, pois ela apenas estende a interface BiFunction já existente.

  3. Implementações possíveis para UnaryOperator ou BinaryOperator são funções que soma um número fixo ou soma um número ao outro:

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OperatorExample.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OperatorExample.java[role=include]
    Saída no console
    9
    6
  4. Existem interfaces Operator para lidar tipos primitivos: DoubleBinaryOperator, DoubleUnaryOperator, IntBinaryOperator, IntUnaryOperator, LongBinaryOperator, LongUnaryOperator.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OperatorPrimitive.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OperatorPrimitive.java[role=include]
    Saída no console
    9
    6

Optional

O Java 8 possui um tipo específico para representar valores que podem não ter sido informados, que é a classe Optional. A partir do Java 8, ela geralmente é uma opção melhor do que retornar ou armazenar null, pois seus métodos auxiliam em várias situações.

  1. É possível criar uma instância de Optional com valor através do método of.

  2. É possível criar uma instância de Optional sem valor através do método empty.

  3. É possível checar se uma instância de Optional possui um valor através do método isPresent.

  4. É possível recuperar o valor de uma instância de Optional através do método get.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalCreation.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalCreation.java[role=include]
  5. Não é possível chamar o método of passando null como argumento. Para isso existe o método ofNullable.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalNullable.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalNullable.java[role=include]
    Saída no console
    java.lang.NullPointerException
    	at java.util.Objects.requireNonNull(Objects.java:203)
    	at java.util.Optional.<init>(Optional.java:96)
    	at java.util.Optional.of(Optional.java:108)
    	at org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalNullable.main(BuiltInInterfaces_OptionalNullable.java:11)
    false
  6. Com o método ifPresent é possível executar uma expressão lambda apenas se o Optional tiver valor.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalIfPresent.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalIfPresent.java[role=include]
    Saída no console
    Com Valor: valor
  7. É possível recuperar um valor padrão caso o Optional esteja vazio. O método orElse retorna um valor diretamente, e o orElseGet retorna através de uma expressão lambda.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalOrElse.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalOrElse.java[role=include]
    Saída no console
    valor padrao
    valor padrao
    valor
    valor
    Tip
    Observe que esse é um ótimo caso para lembrar de um benefício das expressões lambda. Na utilização de orElseGet a expressão lambda só é executada caso o Optional esteja vazio. No caso do exemplo, como é apenas o retorno de uma String, não faz diferença. Porém, se fosse uma operação mais pesada, você só iria de fato executá-la se o Optional estivesse vazio. Caso houvesse valor, a expressão lambda nem seria executada, evitando o custo de processamento.
  8. Também é possível lançar uma exceção caso um valor não esteja presente no Optional utilizando o método orElseThrow.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalOrElseThrow.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalOrElseThrow.java[role=include]
    Saída no console
    valor
    Exception in thread "main" java.lang.RuntimeException
    	at org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalOrElseThrow.lambda$1(BuiltInInterfaces_OptionalOrElseThrow.java:17)
    	at java.util.Optional.orElseThrow(Optional.java:290)
    	at org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalOrElseThrow.main(BuiltInInterfaces_OptionalOrElseThrow.java:17)
  9. Será lançada uma exceção ao chamar o método get em um Optional vazio.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalGetEmpty.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalGetEmpty.java[role=include]
    Saída no console
    valor
    Exception in thread "main" java.util.NoSuchElementException: No value present
    	at java.util.Optional.get(Optional.java:135)
    	at org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalGetEmpty.main(BuiltInInterfaces_OptionalGetEmpty.java:13)
  10. Existem algumas classes para lidar com valor opcionais de variáveis primitivas, já que elas não podem ser utilizada com generics: OptionalInt, OptionalDouble, OptionalLong.

    src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalPrimitive.java
    link:../../../src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalPrimitive.java[role=include]
    Saída no console
    5
Referências