Java 8 เริ่มปรากฏขึ้นนำคุณลักษณะใหม่: ใช้ Lambda Expression (JSR-335) สำหรับการเขียนโปรแกรมที่ใช้งานได้ วันนี้เราจะหารือเกี่ยวกับส่วนหนึ่งของแลมบ์ดา: ส่วนขยายเสมือนจริงหรือที่เรียกว่าวิธีการของผู้พิทักษ์ คุณลักษณะนี้ช่วยให้คุณสามารถใช้วิธีการให้วิธีการในคำนิยามอินเตอร์เฟส ตัวอย่างเช่นคุณสามารถกำหนดวิธีการสำหรับอินเทอร์เฟซที่มีอยู่ (เช่นรายการและแผนที่) เพื่อให้นักพัฒนารายอื่นไม่จำเป็นต้องใช้วิธีการเหล่านี้อีกครั้งซึ่งเป็นเหมือนนามธรรม แต่จริงๆแล้วมันเป็นอินเทอร์เฟซ แน่นอน Java 8 เข้ากันได้กับห้องสมุดที่มีอยู่ในทางทฤษฎี
วิธีการขยายเสมือนจริงนำคุณลักษณะการสืบทอดหลายอย่างมาสู่ Java บางทีคุณอาจเห็นเงาของการสืบทอดหลายอย่างผ่านคุณสมบัตินี้ แต่คุณยังสามารถจำลองการสืบทอดของสถานะอินสแตนซ์ได้ ฉันจะอธิบายถึงมรดกของรัฐผ่าน Mixin ในบทความถัดไปโดยละเอียด
ผสมใน Mixin คืออะไร?
การผสมเป็นคลาสที่เป็นนามธรรมของชุดค่าผสม ตัวอย่างเช่นหากคุณมีชั้นเรียนเพื่อระบุ "ม้า" คุณสามารถสร้างอินสแตนซ์คลาสนี้เพื่อสร้างตัวอย่างของ "ม้า" แล้วขยายโดยการสืบทอดเช่น "โรงรถ" และ "สวน"
Val MyHouse = บ้านใหม่พร้อมโรงรถพร้อมสวน
จากมิกซ์อินมรดกไม่ใช่ข้อกำหนดเฉพาะนี่เป็นเพียงวิธีการที่ใช้ในการเพิ่มฟังก์ชั่นต่าง ๆ ในหมวดหมู่ที่มีอยู่ ใน OOP ด้วย Mixin คุณมีความสามารถในการอ่านของชั้นเรียนผ่าน
ตัวอย่างเช่นมีวิธีการผสมในโมดูล Socketserver ของ Python
คลาส forkingpserver (forkingmixin, udpserver): passclass forkingtcpserver (forkingmixin, tcpserver): ผ่านคลาส threadingingpserver (threadmixin): passclass threadingtcpserver (threadingmixin, tcpserver): ผ่าน
วิธีการขยายเสมือนคืออะไร?
Java 8 จะแนะนำแนวคิดของการขยายเสมือนจริงหรือที่เรียกว่าวิธีการพิทักษ์สาธารณะ
VEM มีจุดมุ่งหมายเพื่อให้วิธีการเริ่มต้นสำหรับอินเทอร์เฟซ Java ไลบรารีที่สามเช่น Hibernate ไม่จำเป็นต้องทำซ้ำวิธีการทั้งหมดของ APIs คอลเลกชันเหล่านี้เนื่องจากมีวิธีการเริ่มต้นบางอย่าง
ต่อไปนี้เป็นตัวอย่างของวิธีการกำหนดวิธีการในอินเทอร์เฟซ:
การรวบรวมอินเทอร์เฟซสาธารณะ <T> ขยาย ITERABLE <T> {<R> คอลเลกชัน <R> ตัวกรอง (Predicate <t> P) ค่าเริ่มต้น {return collections
การจำลองแบบผสมของ Java 8
ตอนนี้เรามาเพื่อให้ได้เอฟเฟกต์ผสมผ่าน VEM แต่คำเตือนล่วงหน้าคือ: โปรดอย่าใช้ในที่ทำงาน!
การใช้งานต่อไปนี้ไม่ใช่เธรดที่ปลอดภัยและอาจมีปัญหาของการรั่วไหลของหน่วยความจำซึ่งขึ้นอยู่กับวิธีการ HashCode และ Equals ที่คุณกำหนดไว้ในชั้นเรียน
ก่อนอื่นเรากำหนดคำจำกัดความของวิธีการ (state ถั่วจำลอง) และให้วิธีการ:
อินเตอร์เฟสสาธารณะ switchableMixin {บูลีน isactivated () ค่าเริ่มต้น {return switchables.isactivated (นี่);} เป็นโมฆะ setActivated (กิจกรรมบูลีน) ความผิดพลาด {switchables.setActivated (นี้เปิดใช้งาน);}}}
จากนั้นเรากำหนดคลาสเครื่องมือที่มีอินสแตนซ์แผนที่เพื่อบันทึกการเชื่อมโยงของอินสแตนซ์และสถานะ
Public Class Switchables {MAP สุดท้ายคงที่ <SwitchableMixin, SwitchabledEvicEstate> switch_states = new hashmap <() (); กิจกรรม;} public static void setActivated (อุปกรณ์ switchablemixin, เปิดใช้งานบูลีน) {switchabledevicestate = switch_states.get.get (อุปกรณ์); .activated = เปิดใช้งาน;} คลาสคงที่คลาสคงที่ switchabledEvicEstate {บูลีนส่วนตัวเปิดใช้งาน;}}
นี่คือกรณีการใช้งานที่เน้นมรดกของรัฐ:
อุปกรณ์คลาสสแตติกส่วนตัว {} คลาสคงที่คลาสคงที่ devicea ขยายอุปกรณ์ใช้อุปกรณ์สวิตช์ SwitchableMixin {} อุปกรณ์คลาสคงที่ระดับส่วนตัวขยายอุปกรณ์ imp leer leter switchableMixin {}
"สิ่งที่แตกต่างอย่างสิ้นเชิง"
การใช้งานข้างต้นดูเหมือนจะเป็นเรื่องปกติ แต่สถาปนิกภาษา Java ของ Oracle Brian Goetz ถามคำถามฉันว่าการใช้งานในปัจจุบันไม่สามารถทำงานได้ (สมมติว่าความปลอดภัยของเธรดและการรั่วไหลของหน่วยความจำได้รับการแก้ไข)
Interface FakeBrokenMixin {แผนที่คงที่ <FakeBrokenMixin, String> BackingMap = Collections ใส่ (นี่, ชื่อ);}} อินเตอร์เฟส X ขยาย Runnable, FakeBrokenMixin {} x makex () {return () -> {system.println ("x");}; (); x1.setName ("x1");
ผลลัพธ์ใดที่คุณจะเดาว่ารหัสนี้จะปรากฏขึ้นหลังจากดำเนินการ
วิธีแก้ปัญหาข้อสงสัย
เมื่อมองแวบแรกไม่มีปัญหากับรหัสการใช้งานนี้ X เป็นอินเทอร์เฟซที่มีวิธีเดียวเท่านั้นเนื่องจาก GetName และ SetName มีคำจำกัดความเริ่มต้นอยู่แล้ว แต่วิธีการเรียกใช้ของอินเตอร์เฟสที่เรียกใช้ได้ไม่ได้กำหนดไว้ การใช้วิธีการเรียกใช้ ดังนั้นผลลัพธ์ที่คุณต้องการโปรแกรมนี้หลังจากดำเนินการคือ:
x1x2
หากคุณลบการโทรของวิธี getName ผลการดำเนินการจะกลายเป็น:
mytest $ 1@30ae8764mytest $ 1@123acf34
สองบรรทัดนี้แสดงให้เห็นว่าการดำเนินการของวิธีการ Makex มาจากสองอินสแตนซ์ที่แตกต่างกันและในเวลานี้ OpenJDK 8 ปัจจุบันถูกสร้างขึ้น (ที่นี่ฉันใช้ OpenJDK 8 24.0-B07)
ไม่ว่าในกรณีใด OpenJDK 8 ปัจจุบันไม่ได้สะท้อนถึงพฤติกรรม Java 8 สุดท้าย
x2x2
หากคุณไม่เรียกวิธี getName มันจะปรากฏขึ้น:
mytest $ $ lambda $ 1@5506d4eamytest $ $ lambda $ 1@5506d4ea
การโทรแต่ละวิธี Makex ดูเหมือนจะเป็นอินสแตนซ์ซิงเกิ้ลจากคลาสภายในที่ไม่ระบุชื่อเดียวกัน
เพราะในระหว่างการรวบรวมการแสดงออกของแลมบ์ดาไม่ได้รับการแปลอย่างสมบูรณ์ คำสั่งนี้มีข้อมูลเมตาที่จำเป็นทั้งหมดเกี่ยวกับนิพจน์ Labda ที่รันไทม์ รวมถึงชื่อเมธอดประเภทอินพุตและเอาต์พุตและวิธีการที่เรียกว่า bootstrap วิธี Bootstrap ใช้เพื่อกำหนดอินสแตนซ์ของการรับวิธีนี้
กลับไปที่คำถามตอนนี้นิพจน์แลมบ์ดากลายเป็นวิธีการคงที่ส่วนตัว () -> {system.out.println ("x");} ถูกถ่ายโอนไปยัง MyTest:
โมฆะคงที่ส่วนตัว Lambda $ 0 () {System.out.println ("x");}
หากคุณใช้พารามิเตอร์ส่วนตัวกับอุปกรณ์รวบรวมตัวนับ Javap และคุณสามารถดูวิธีนี้ได้คุณยังสามารถใช้พารามิเตอร์ -C เพื่อดูการแปลงที่สมบูรณ์ยิ่งขึ้น
เมื่อคุณเรียกใช้โปรแกรม JVM เรียกวิธี Lambda Metafactory เพื่อพยายามอธิบายคำแนะนำที่เรียกใช้ ในตัวอย่างของเราเมื่อ Makex ถูกเรียกเป็นครั้งแรกวิธี Lambda Metafactory สร้างอินสแตนซ์ของ X และเชื่อมโยงวิธีการเรียกใช้กับวิธี Lambda $ 0 ในหน่วยความจำดังนั้นอินสแตนซ์ของการโทรครั้งที่สองของคุณจึงเหมือนกับครั้งแรก
คุณซ่อมไหม มีทางออกหรือไม่?
ไม่มีการซ่อมแซมหรือแก้ไขปัญหานี้โดยตรง แม้ว่าโปรแกรม Java 8 ของ Oracle จะเริ่มต้น-xdlambdatomethod เนื่องจากพารามิเตอร์นี้ไม่ได้เป็นส่วนหนึ่งของข้อกำหนด JVM การใช้งานของซัพพลายเออร์ที่แตกต่างกันและ JVM นั้นแตกต่างกัน สำหรับนิพจน์แลมบ์ดาสิ่งเดียวที่คุณคาดหวังคือการใช้วิธีการเชื่อมต่อของคุณในชั้นเรียน
วิธีอื่น ๆ
จนถึงตอนนี้ถึงแม้ว่าการเลียนแบบของ MixIn ของเราไม่สามารถเข้ากันได้กับ Java 8 แต่ก็ยังเป็นไปได้ที่จะเพิ่มบริการหลายรายการผ่านการสืบทอดหลายครั้งและการนัดหมายให้มีอยู่ วิธีนี้เป็นรูปแบบฟิลด์เสมือน (โหมดฟิลด์เสมือน)
ลองดูที่เราเปลี่ยนได้
Interface switchable {boolean isactive ();
เราต้องการอินเทอร์เฟซแบบสลับได้และให้วิธีการที่เป็นนามธรรมเพิ่มเติมเพื่อกลับไปที่การใช้งานแบบสลับได้ วิธีการรวมมีคำจำกัดความเริ่มต้น
อินเทอร์เฟซ Public SwitchableView ขยาย {switchable getSwitchable (); Boolean isactive () {return getswitchable ()
ต่อไปเราสร้างการใช้งานแบบสลับได้อย่างสมบูรณ์:
Public Class SwitchableImpl ใช้สวิตช์ {บูลีนส่วนตัวที่ใช้งานอยู่;
นี่คือตัวอย่างของโหมดฟิลด์เสมือนของเรา:
อุปกรณ์สาธารณะ {} คลาสสาธารณะ Devicea ขยายอุปกรณ์ใช้งาน SwitchableView {private switchable switchableImpl (); switchable getSwitchable () {return switchable;}}
สรุปแล้ว
ในบทความนี้เราใช้สองวิธีในการเพิ่มบริการหลายบริการผ่านวิธีการขยายเสมือนจริงของ Java 8 วิธีแรกใช้แผนที่เพื่อจัดเก็บสถานะอินสแตนซ์ อีกวิธีหนึ่งคือการใช้โหมดฟิลด์เสมือนจริงเพื่อส่งคืนตัวอย่างการใช้งานขั้นสุดท้ายผ่านทางนามธรรม Getter วิธีที่สองมีความเป็นอิสระและปลอดภัยกว่า
วิธีการเสริมเสมือนจริงเป็นคุณสมบัติใหม่ของ Java