เมื่ออ่านข้อมูลจำเพาะ Java-8 ฉันมักจะเห็นการอ้างอิงถึง 'ประเภท SAM' ฉันยังไม่สามารถหาคำอธิบายที่ชัดเจนได้ว่านี่คืออะไร
ประเภท SAM คืออะไรและสถานการณ์ตัวอย่างของเวลาที่จะใช้คืออะไร?
เมื่ออ่านข้อมูลจำเพาะ Java-8 ฉันมักจะเห็นการอ้างอิงถึง 'ประเภท SAM' ฉันยังไม่สามารถหาคำอธิบายที่ชัดเจนได้ว่านี่คืออะไร
ประเภท SAM คืออะไรและสถานการณ์ตัวอย่างของเวลาที่จะใช้คืออะไร?
คำตอบ:
เพื่อสรุปการเชื่อมโยงจอนโพสต์1ในกรณีที่เคยไปลง "แซม" ย่อมาจาก "วิธีนามธรรมเดียว" และ "SAM ประเภท" หมายถึงการเชื่อมต่อเช่นRunnable
,Callable
ฯลฯ การแสดงออกแลมบ์ดาคุณลักษณะใหม่ใน Java 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
คำสั่ง bytecode ที่เพิ่มใน Java 7 ฉันได้กล่าวไว้ก่อนหน้านี้ว่าการประกาศ Lambda จะสร้างอินสแตนซ์Callable
หรือComparable
คล้ายกับคลาสที่ไม่ระบุชื่อ แต่นั่นไม่เป็นความจริงอย่างเคร่งครัด ในครั้งแรกที่invokedynamic
ถูกเรียกมันจะสร้างตัวจัดการฟังก์ชัน Lambda โดยใช้LambdaMetafactory.metafactory
เมธอดจากนั้นใช้อินสแตนซ์แคชนี้ในการเรียกใช้ Lambda ในอนาคต สามารถดูข้อมูลเพิ่มเติมได้ในคำตอบนี้
วิธีนี้มีความซับซ้อนและยังรวมถึงโค้ดที่สามารถอ่านค่าดั้งเดิมและการอ้างอิงโดยตรงจากหน่วยความจำสแต็กเพื่อส่งผ่านไปยังโค้ดแลมบ์ดาของคุณ (เช่นการไปObject[]
ไหนมาไหนจำเป็นต้องจัดสรรอาร์เรย์เพื่อเรียกใช้แลมบ์ดาของคุณ) แต่จะอนุญาตให้ใช้แลมด้าซ้ำในอนาคตได้ เพื่อแทนที่การใช้งานแบบเก่าโดยไม่ต้องกังวลเกี่ยวกับความเข้ากันได้ของ bytecode หากวิศวกรของ Oracle เปลี่ยนการใช้งาน Lambda พื้นฐานใน JVM เวอร์ชันที่ใหม่กว่า Lambdas ที่คอมไพล์บน JVM รุ่นเก่าจะใช้การใช้งานที่ใหม่กว่าโดยอัตโนมัติโดยไม่มีการเปลี่ยนแปลงใด ๆ ในส่วนของผู้พัฒนา
1ไวยากรณ์ในลิงก์ล้าสมัย ลองดูที่Lambda Expressions Java Trailเพื่อดูไวยากรณ์ปัจจุบัน