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?
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?
Respostas:
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 Callable
expressõ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 Comparator
uso 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 invokedynamic
instrução bytecode adicionada no Java 7. Eu disse anteriormente que declarar um Lambda cria uma instância de Callable
ou Comparable
semelhante 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.metafactory
mé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.