Para que são utilizadas as interfaces funcionais no Java 8?


154

Me deparei com um novo termo no Java 8: "interface funcional". Eu só consegui encontrar um uso dele enquanto trabalhava com expressões lambda .

O Java 8 fornece algumas interfaces funcionais integradas e, se quisermos definir qualquer interface funcional, podemos usar a @FunctionalInterfaceanotação. Isso nos permitirá declarar apenas um único método na interface.

Por exemplo:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

Quão útil é no Java 8 além de apenas trabalhar com expressões lambda ?

(A pergunta aqui é diferente da que eu perguntei. Está perguntando por que precisamos de interfaces funcionais ao trabalhar com expressões lambda. Minha pergunta é: por que outros usos as interfaces funcionais têm além das expressões lambda?)


1
Parece duplicado para este link. Eles também falam sobre por que deveria haver apenas um método na Interface Funcional. stackoverflow.com/questions/33010594/…
Kulbhushan Singh

1
@KulbhushanSingh eu vi esta pergunta antes de postar ... Ambas as perguntas sentir diferença ...
Madhusudan

Respostas:


127

@FunctionalInterfaceA anotação é útil para verificar o tempo de compilação do seu código. Você não pode ter mais de um método static, além de defaultmétodos abstratos que substituem os métodos Objectna sua @FunctionalInterfaceou em qualquer outra interface usada como interface funcional.

Mas você pode usar lambdas sem essa anotação, bem como substituir métodos sem @Overrideanotação.

De documentos

uma interface funcional possui exatamente um método abstrato. Como os métodos padrão têm uma implementação, eles não são abstratos. Se uma interface declara um método abstrato substituindo um dos métodos públicos de java.lang.Object, isso também não conta para a contagem de métodos abstratos da interface, pois qualquer implementação da interface terá uma implementação de java.lang.Object ou em outro local

Isso pode ser usado na expressão lambda:

public interface Foo {
  public void doSomething();
}

Isso não pode ser usado na expressão lambda:

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Mas isso dará erro de compilação :

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Anotação '@FunctionalInterface' inválida; Foo não é uma interface funcional


43
Para ser mais preciso, você precisa ter exatamente um método abstrato que não substitui um método java.lang.Objectem uma interface funcional.
Holger

9
... e é ligeiramente diferente para “não tem mais do que um publicmétodo além statice default” ...
Holger

4
Ainda não entendo nenhum ponto de tê-lo. Por que alguém no mundo se incomoda em verificar quantos métodos sua interface possui? As interfaces de marcadores ainda têm um ponto e uma finalidade específica. A documentação e a resposta apenas explicam o que ele faz, e não como é de alguma forma útil. E "use" é exatamente o que o OP pediu. Então, eu não recomendaria esta resposta.
saran3h

1
@VNT o erro de compilação obtém os clientes dessa interface, mas não a própria interface pode mudar. Com esta anotação, o erro de compilação está na interface, portanto, verifique se ninguém quebrará os clientes da sua interface.
Sergii Bishyr

2
Isso mostra como usá-los, mas não explica por que precisamos deles.
sheikh

14

A documentação realmente faz a diferença entre o objetivo

Um tipo de anotação informativa usado para indicar que uma declaração de tipo de interface se destina a ser uma interface funcional, conforme definido pela Java Language Specification.

e o caso de uso

Observe que instâncias de interfaces funcionais podem ser criadas com expressões lambda, referências de método ou referências de construtor.

cuja redação não exclui outros casos de uso em geral. Como o objetivo principal é indicar uma interface funcional , sua pergunta real se resume a "Existem outros casos de uso para interfaces funcionais que não sejam expressões lambda e referências de método / construtor?"

Como a interface funcional é uma construção da linguagem Java definida pela Especificação da Linguagem Java, somente essa especificação pode responder a essa pergunta:

JLS §9.8. Interfaces Funcionais :

...

Além do processo usual de criação de uma instância de interface, declarando e instanciando uma classe (§15.9), instâncias de interfaces funcionais podem ser criadas com expressões de referência de método e expressões lambda (§15.13, §15.27).

Portanto, a Java Language Specification não diz o contrário, o único caso de uso mencionado nessa seção é o de criar instâncias de interface com expressões de referência de método e expressões lambda. (Isso inclui referências de construtor, pois elas são anotadas como uma forma de expressão de referência de método na especificação).

Portanto, em uma frase, não, não há outro caso de uso para ele no Java 8.


Pode ser que você esteja pedindo um pouco demais ou seja irrelevante (você pode optar por não responder), mas o que você sugeriria quando alguém criou um utilitário em public static String generateTaskId()vez de torná-lo mais "funcional" que alguém escolheu escrevê-lo como public class TaskIdSupplier implements Supplier<String>no getmétodo usando o implementação de geração existente. Isso é um uso indevido de interfaces funcionais, especialmente reutilizando o Supplierdo JDK interno? PS: Não consegui encontrar um lugar melhor / perguntas e respostas para perguntar isso. É um prazer migrar, se você puder sugerir.
Naman

1
@ Naman, você não está tornando o método utilitário mais funcional ao criar uma classe nomeada TaskIdSupplier. Agora, a questão é por que você criou a classe nomeada. Existem cenários em que esse tipo nomeado é necessário, por exemplo, quando você deseja apoiar a localização da implementação via ServiceLoader. Não há nada errado em deixá-lo implementar Supplierentão. Mas quando você não precisar, não crie. Quando você precisa apenas de um Supplier<String>, já é suficiente usar DeclaringClass::generateTaskIde eliminar a necessidade de uma classe explícita é o ponto desse recurso de idioma.
Holger

Para ser sincero, estava procurando uma justificativa para uma recomendação que estava passando. Por alguma razão no trabalho, eu realmente não senti que a TaskIdSupplierimplementação valeu o esforço, mas o conceito de ServiceLoadertotalmente saiu da minha cabeça. Encontrou algumas perguntas durante estas discussões que estávamos tendo, como O que é o uso de Supplier's publicexistência quando se pode ir em frente e desenvolver suas próprias interfaces? e Por que não ter public static Supplier<String> TASK_ID_SUPPLIER = () ->...como constante global? . (1/2)
Naman

1
@ Naman, a maneira idiomática de representar funções em Java é um método e avaliar essas funções é idêntico a invocá-las. Nunca um desenvolvedor deve ser forçado a fazer em variable.genericMethodName(args)vez de meaningfulMethodName(args). Usar um tipo de classe para representar uma função, seja através da expressão lambda / referência de método ou de uma classe criada manualmente, é apenas um veículo para transmitir a função (na ausência de tipos de função verdadeiros em Java). Isso só deve ser feito quando necessário.
Holger

1
Quando você tem apenas um pequeno fragmento de código sendo passado, você pode criar uma expressão lambda que o encapsula. Sempre que houver necessidade de chamá-lo como um método (isso inclui cenários com necessidade de teste, quando o fragmento de código não é trivial), crie um método nomeado que possa ser chamado e use uma referência de método ou uma expressão lambda / classe explícita encapsular uma chamada, para distribuí-la quando necessário. As constantes são úteis apenas quando você não confia na eficiência das expressões lambda ou nas referências de método incorporadas ao seu código, ou seja, quase nunca são necessárias.
Holger

12

Como outros já disseram, uma interface funcional é uma interface que expõe um método. Pode ter mais de um método, mas todos os outros devem ter uma implementação padrão. A razão pela qual é chamada de "interface funcional" é porque ela atua efetivamente como uma função. Como você pode transmitir interfaces como parâmetros, isso significa que as funções agora são "cidadãos de primeira classe", como nas linguagens de programação funcionais. Isso tem muitos benefícios, e você os verá bastante ao usar a API de fluxo. Obviamente, as expressões lambda são o principal uso óbvio para elas.


10

De modo nenhum. As expressões lambda são o único ponto dessa anotação.


6
Bem, os lamdbas funcionam sem a anotação também. É uma afirmação, assim como @Overridedeixar o compilador saber que você pretendia escrever algo "funcional" (e obter um erro se você escorregasse).
Thilo

1
Direto ao ponto e à resposta correta, embora um pouco curto. Aproveitei o tempo para adicionar uma resposta mais elaborada dizendo a mesma coisa com mais palavras ...
Holger

5

Uma expressão lambda pode ser atribuída a um tipo de interface funcional, mas o mesmo pode fazer referências a métodos e classes anônimas.

Uma coisa agradável sobre as interfaces funcionais específicas em java.util.functioné que eles podem ser compostas para criar novas funções (como Function.andThene Function.compose, Predicate.and, etc.) devido aos métodos padrão úteis que eles contêm.


Você deve elaborar mais esse comentário. E as referências de métodos e novas funções?
27918 K. K. Nicolas

5

Uma interface com apenas um método abstrato é chamada de Interface Funcional. Não é obrigatório usar o @FunctionalInterface, mas é uma prática recomendada usá-lo com interfaces funcionais para evitar a adição acidental de métodos extras. Se a interface é anotada com a anotação @FunctionalInterface e tentamos ter mais de um método abstrato, ela gera um erro do compilador.

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }

3

Interface funcional:

  • Introduzido em Java 8
  • Interface que contém um método "único resumo".

Exemplo 1:

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

Exemplo 2:

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

Exemplo 3:

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Anotação Java8 - @FunctionalInterface

  • Anotação verifique se a interface contém apenas um método abstrato. Caso contrário, aumente o erro.
  • Mesmo que o @FunctionalInterface esteja ausente, ainda é a interface funcional (se houver um único método abstrato). A anotação ajuda a evitar erros.
  • A interface funcional pode ter métodos estáticos e padrão adicionais.
  • por exemplo, Iterável <>, Comparável <>, Comparador <>.

Aplicações da interface funcional:

  • Referências de método
  • Expressão Lambda
  • Referências do construtor

Para aprender interfaces funcionais, aprender os primeiros métodos padrão na interface e depois de aprender a interface funcional, será fácil entender a referência do método e a expressão lambda


Seus dois primeiros exemplos devem ter uma palavra-chave "abstrata"?
sofs1

1
@ sofs1 Os métodos declarados nas interfaces são por padrão públicos e abstratos. Você deve usar a palavra-chave abstract no caso de métodos na classe abstract. No entanto, é bom usar palavras-chave abstratas para métodos na interface também. Eles permitiram a compatibilidade da versão java mais antiga, mas isso é desencorajado.
Ketan

2

Você pode usar o lambda no Java 8

public static void main(String[] args) {
    tentimes(inputPrm - > System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
}

public static void tentimes(Consumer myFunction) {
    for (int i = 0; i < 10; i++)
        myFunction.accept("hello");
}

Para mais informações sobre Java Lambdas e FunctionalInterfaces


1

@FunctionalInterface é uma nova anotação lançada com Java 8 e fornece tipos de destino para expressões lambda e é usada na verificação do tempo de compilação do seu código.

Quando você quiser usá-lo:

1- Sua interface não deve ter mais de um método abstrato, caso contrário, será gerado um erro de compilação.

1- Sua interface deve ser pura, o que significa que a interface funcional deve ser implementada por classes sem estado, por exemplo, pure é Comparatorinterface porque não depende do estado dos implementadores, neste caso Nenhum erro de compilação será fornecido, mas em muitos casos você não será capaz de usar lambda com este tipo de interfaces

O java.util.functionpacote contém várias interfaces funcionais para fins gerais, tais como Predicate, Consumer, Function, eSupplier .

Observe também que você pode usar lambdas sem esta anotação.


1

Além de outras respostas, acho que o principal motivo para "por que usar a Interface Funcional que não seja diretamente com expressões lambda" pode estar relacionado à natureza da linguagem Java, que é Orientada a Objetos.

Os principais atributos das expressões Lambda são: 1. Eles podem ser passados ​​em torno de 2. e podem ser executados no futuro em tempo específico (várias vezes). Agora, para suportar esse recurso em idiomas, alguns outros idiomas lidam simplesmente com esse assunto.

Por exemplo, em Java Script, uma função (função anônima ou literal de função) pode ser endereçada como um objeto. Assim, você pode criá-los de forma simples e eles também podem ser atribuídos a uma variável e assim por diante. Por exemplo:

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

ou via ES6, você pode usar uma função de seta.

const myFunction = ... => ...

Até agora, os designers de linguagem Java não aceitaram lidar com os recursos mencionados por esse meio (técnicas de programação funcional). Eles acreditam que a linguagem Java é orientada a objetos e, portanto, devem resolver esse problema por meio de técnicas orientadas a objetos. Eles não querem perder a simplicidade e a consistência da linguagem Java.

Portanto, eles usam interfaces, pois quando um objeto de uma interface com apenas um método (quero dizer interface funcional) é necessário, você pode substituí-lo por uma expressão lambda. Tal como:

ActionListener listener = event -> ...;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.