'SAM type' ใน Java คืออะไร?


133

เมื่ออ่านข้อมูลจำเพาะ Java-8 ฉันมักจะเห็นการอ้างอิงถึง 'ประเภท SAM' ฉันยังไม่สามารถหาคำอธิบายที่ชัดเจนได้ว่านี่คืออะไร

ประเภท SAM คืออะไรและสถานการณ์ตัวอย่างของเวลาที่จะใช้คืออะไร?


4
ดูcr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (ซึ่งฉันพบหลังจากการค้นหาครั้งเดียวซึ่งนำฉันไปสู่คำถาม SO อื่น)
Jon Skeet

2
โปรดอย่าอ้างถึงข้อมูลที่ล้าสมัย การค้นหาที่ยาวขึ้นเล็กน้อยจะนำคุณไปสู่เวอร์ชันล่าสุด: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.htmlซึ่งมีอายุ 18 เดือนและตอนนี้ล้าสมัยไปแล้วในหลาย ๆ สถานที่ คำถามของ OP มีคำตอบอยู่ที่lambdafaq.org/what-is-a-functional-interfaceหนึ่งหน้าในคำถามที่พบบ่อยซึ่งฉันพยายามติดตามสิ่งที่เป็นอยู่จนกระทั่งเมื่อไม่นานมานี้มีการเปลี่ยนแปลงภาษาและการพัฒนา API อย่างรวดเร็ว
Maurice Naftalin

1
@MauriceNaftalin คุณยังสามารถเชื่อมโยงเส้นทาง Javaซึ่งทีมพัฒนาได้รับการอัปเดตอยู่เสมอ
Brian

1
คำปัจจุบันคือ "อินเทอร์เฟซการทำงาน"
newacct

1
@MauriceNaftalin: ขออภัยพลาดอันนั้นไป แน่นอนจะเชื่อมโยงกับมันถ้าฉันพบว่า
Jon Skeet

คำตอบ:


142

เพื่อสรุปการเชื่อมโยงจอนโพสต์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เพื่อดูไวยากรณ์ปัจจุบัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.