¿Qué es un 'tipo SAM' en Java?

133

Leyendo sobre la especificación Java-8, sigo viendo referencias a 'tipos de SAM'. No he podido encontrar una explicación clara de lo que es esto.

¿Qué es un tipo SAM y cuál es un escenario de ejemplo de cuándo se puede usar uno?

Cody
fuente
44
Ver cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (que encontré después de una sola búsqueda, lo que me llevó a otra pregunta SO).
Jon Skeet
2
No remita a las personas a información desactualizada. Una búsqueda un poco más larga lo habría llevado a la versión más actualizada: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , que tiene 18 meses y ahora está desactualizado en muchos lugares. La pregunta del OP se responde en lambdafaq.org/what-is-a-functional-interface , una página en un FAQ que intento mantener actualizado en lo que ha sido hasta hace poco un lenguaje que cambia rápidamente y el desarrollo de API.
Maurice Naftalin
1
@MauriceNaftalin También puede simplemente vincular el rastro de Java , que el equipo de desarrollo mantiene actualizado.
Brian
1
El término actual es "interfaz funcional".
newacct
1
@MauriceNaftalin: Lo siento, me perdí esa. Ciertamente me habría vinculado si lo hubiera encontrado.
Jon Skeet

Respuestas:

142

Para resumir el enlace Jon registró 1 en caso de que alguna vez se cae, "SAM" significa "método abstracto sola", y "SAM-tipo" se refiere a interfaces como Runnable, Callable, etc. Las expresiones lambda, una nueva característica en Java 8, son, se considera un tipo SAM y se puede convertir libremente a ellos.

Por ejemplo, con una interfaz como esta:

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

Puedes declarar Callableusando expresiones lambda como esta:

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

Las expresiones lambda en este contexto son principalmente azúcar sintáctico. Se ven mejor en código que las clases anónimas y son menos restrictivos en la denominación de métodos. Tome este ejemplo del enlace:

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);

Esto crea el Comparatoruso de un método específico que no comparte el mismo nombre que Comparator.compare, de esa manera, no tiene que seguir el nombre de la interfaz de los métodos y puede tener múltiples anulaciones de comparación en una clase, luego cree los comparadores sobre la marcha a través de Las expresiones lambda.

Yendo más profundo ...

En un nivel más profundo, Java los implementa utilizando la invokedynamicinstrucción de código de bytes agregada en Java 7. Dije anteriormente que declarar un Lambda crea una instancia de una clase anónima Callableo Comparablesimilar, pero eso no es estrictamente cierto. En cambio, la primera vez que invokedynamicse llama, crea un controlador de función Lambda utilizando el LambdaMetafactory.metafactorymétodo , luego usa esta instancia en caché en futuras invocaciones de Lambda. Se puede encontrar más información en esta respuesta .

Este enfoque es complejo e incluso incluye código que puede leer valores primitivos y referencias directamente desde la memoria de la pila para pasar a su código Lambda (por ejemplo, para evitar la necesidad de asignar una Object[]matriz para invocar su Lambda), pero permite futuras iteraciones de la implementación de Lambda para reemplazar implementaciones antiguas sin tener que preocuparse por la compatibilidad de bytecode. Si los ingenieros de Oracle cambian la implementación subyacente de Lambda en una versión más nueva de la JVM, Lambdas compilada en una JVM anterior usará automáticamente la implementación más nueva sin ningún cambio por parte del desarrollador.


1 La sintaxis en el enlace no está actualizada. Eche un vistazo al Lambda Expressions Java Trail para ver la sintaxis actual.

Brian
fuente