O que é um 'tipo SAM' em Java?

133

Lendo as especificações do Java-8, continuo vendo referências a 'tipos de SAM'. Não consegui encontrar uma explicação clara sobre o que é isso.

O que é um tipo de SAM e qual é um cenário de exemplo de quando um pode ser usado?

Cody
fonte
4
Veja cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (que encontrei após uma única pesquisa, o que me levou a outra pergunta SO).
31813 Jon Skeet
2
Não encaminhe as pessoas a informações desatualizadas. Uma pesquisa um pouco mais longa levaria você à versão mais atualizada: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , que tem 18 meses e está desatualizada em muitos lugares. A pergunta do OP é respondida em lambdafaq.org/what-is-a-functional-interface , uma página de uma FAQ que tento manter atualizada no que tem sido até recentemente uma linguagem de rápida mudança e desenvolvimento de API.
Maurice Naftalin
1
@MauriceNaftalin Você também pode simplesmente vincular a trilha Java , que é mantida atualizada pela equipe de desenvolvimento.
28713 Brian Brian
1
O termo atual é "interface funcional".
newacct
1
@MauriceNaftalin: Desculpe, perdi essa. Certamente teria vinculado a isso se eu tivesse encontrado isso.
31913 Jon Skeet

Respostas:

142

Para resumir o link Jon postou 1 no caso de ele nunca vai para baixo, "SAM" significa "método abstrato único", e "SAM-tipo" refere-se a interfaces como Runnable, Callable, etc. As expressões lambda, um novo recurso do Java 8, são considerado um tipo SAM e pode ser livremente convertido para eles.

Por exemplo, com uma interface como esta:

public interface Callable<T> {
    public T call();
}

Você pode declarar Callableexpressões lambda usando assim:

Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"

Expressões lambda neste contexto são principalmente apenas açúcar sintático. Eles parecem melhores no código do que as classes anônimas e são menos restritivos à nomeação de métodos. Veja este exemplo no link:

class Person { 
    private final String name;
    private final int age;

    public static int compareByAge(Person a, Person b) { ... }

    public static int compareByName(Person a, Person b) { ... }
}

Person[] people = ...
Arrays.sort(people, Person::compareByAge);

Isso cria um Comparatoruso de um método específico que não compartilha o mesmo nome que Comparator.compare, assim, você não precisa seguir a nomenclatura da interface dos métodos e pode ter várias substituições de comparação em uma classe e criar os comparadores em tempo real via as expressões lambda.

Indo mais fundo ...

Em um nível mais profundo, o Java os implementa usando a invokedynamicinstrução bytecode adicionada no Java 7. Eu disse anteriormente que declarar um Lambda cria uma instância de Callableou Comparablesemelhante a uma classe anônima, mas isso não é rigorosamente verdade. Em vez disso, na primeira vez em que invokedynamicé chamado, ele cria um manipulador de função Lambda usando o LambdaMetafactory.metafactorymétodo e usa essa instância em cache em futuras invocações do Lambda. Mais informações podem ser encontradas nesta resposta .

Essa abordagem é complexa e inclui até mesmo códigos que podem ler valores e referências primitivos diretamente da memória da pilha para passar para o código Lambda (por exemplo, para contornar a necessidade de alocar uma Object[]matriz para chamar seu Lambda), mas permite iterações futuras da implementação do Lambda para substituir implementações antigas sem precisar se preocupar com a compatibilidade do bytecode. Se os engenheiros da Oracle alterarem a implementação subjacente do Lambda em uma versão mais recente da JVM, o Lambdas compilado em uma JVM mais antiga usará automaticamente a implementação mais nova sem nenhuma alteração por parte do desenvolvedor.


1 A sintaxe no link está desatualizada. Dê uma olhada na Trilha Java do Lambda Expressions para ver a sintaxe atual.

Brian
fonte