Java-8 사양을 읽으면서 ‘SAM 유형’에 대한 참조가 계속 표시됩니다. 이것이 무엇인지에 대한 명확한 설명을 찾을 수 없었습니다.
SAM 유형이란 무엇이며 언제 사용되는지에 대한 시나리오 예는 무엇입니까?
답변
요약 존 게시 링크 1 은 지금까지 다운 될 경우를, “SAM”은 “하나의 추상 메소드”를 의미하고, “SAM-type은”와 같은 인터페이스를 의미 Runnable
, Callable
있다, 등 람다 표현식, 자바 8의 새로운 기능, SAM 유형으로 간주되며 자유롭게 변환 할 수 있습니다.
예를 들어 다음과 같은 인터페이스가 있습니다.
public interface Callable<T> {
public T call();
}
다음 Callable
과 같이 람다 식을 사용하여 선언 할 수 있습니다 .
Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"
이러한 맥락에서 람다 표현은 대부분 구문 설탕입니다. 익명 클래스보다 코드에서 더 잘 보이고 메소드 이름 지정에 덜 제한적입니다. 링크에서이 예제를 보자.
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);
이것은와 Comparator
같은 이름을 공유하지 않는 특정 메소드를 사용하여 메소드 Comparator.compare
의 인터페이스 이름 지정을 따를 필요가 없으며 클래스에서 여러 비교 대체를 수행 한 다음 즉시 비교기를 작성합니다. 람다 식.
더 깊이 들어가기 …
더 깊은 수준에서 Java invokedynamic
는 Java 7에 추가 된 바이트 코드 명령어를 사용하여 이러한 기능을 구현합니다 . 앞에서 Lambda를 선언 하면 익명 클래스 의 인스턴스를 만들 Callable
거나 Comparable
유사한 클래스를 만들지 만 엄격하게 사실 은 아닙니다 . 대신,를 처음 invokedynamic
호출하면 LambdaMetafactory.metafactory
메소드 를 사용 하여 Lambda 함수 핸들러를 생성 한 다음 이후에 Lambda를 호출 할 때이 캐시 된 인스턴스를 사용합니다. 자세한 내용은 이 답변 에서 확인할 수 있습니다 .
이 접근 방식은 복잡하며 스택 메모리에서 직접 기본 값과 참조를 읽어 Lambda 코드로 전달할 수있는 코드를 포함합니다 (예 : Object[]
Lambda를 호출하기 위해 배열을 할당해야하는 경우 ). 그러나 향후 Lambda 구현을 반복 할 수 있습니다. 바이트 코드 호환성에 대해 걱정할 필요없이 이전 구현을 대체합니다. Oracle의 엔지니어가 최신 버전의 JVM에서 기본 Lambda 구현을 변경하는 경우 이전 JVM에서 컴파일 된 Lambdas는 개발자의 변경없이 자동으로 최신 구현을 사용합니다.
1 링크의 구문이 오래되었습니다. 상기 살펴보세요 람다 표현식 자바 트레일을 현재의 구문을 확인합니다.