วิธีการเริ่มต้นคืออะไร?
หลังจากการเปิดตัว Java 8 คุณสามารถเพิ่มวิธีการใหม่ให้กับอินเทอร์เฟซได้ แต่อินเทอร์เฟซจะยังคงเข้ากันได้กับคลาสการใช้งาน นี่เป็นสิ่งสำคัญเนื่องจากไลบรารีที่คุณพัฒนาอาจมีการใช้งานอย่างกว้างขวางโดยนักพัฒนาหลายคน ก่อน Java 8 หลังจากที่อินเทอร์เฟซถูกเผยแพร่ในคลาสไลบรารี หากมีการเพิ่มวิธีการใหม่ให้กับอินเทอร์เฟซ แอปพลิเคชันที่ใช้อินเทอร์เฟซนี้อาจตกอยู่ในอันตรายจากการหยุดทำงานโดยใช้อินเทอร์เฟซเวอร์ชันใหม่
ด้วย Java 8 ไม่มีอันตรายเช่นนั้นหรือ? คำตอบคือไม่
การเพิ่มวิธีการเริ่มต้นให้กับอินเทอร์เฟซอาจทำให้คลาสการใช้งานบางคลาสไม่พร้อมใช้งาน
ขั้นแรก มาดูรายละเอียดของวิธีการเริ่มต้นกันก่อน
ใน Java 8 สามารถใช้วิธีการในอินเทอร์เฟซได้ (วิธีการแบบคงที่ใน Java 8 สามารถนำไปใช้ในอินเทอร์เฟซได้ แต่นั่นเป็นหัวข้ออื่น) วิธีการที่ใช้ในอินเทอร์เฟซเรียกว่าวิธีการเริ่มต้น ซึ่งระบุด้วยค่าเริ่มต้นของคำหลักเป็นตัวแก้ไข เมื่อคลาสนำอินเทอร์เฟซไปใช้ ก็สามารถใช้วิธีที่มีอยู่แล้วในอินเทอร์เฟซได้ แต่ไม่จำเป็น คลาสนี้จะสืบทอดวิธีการเริ่มต้น นี่คือสาเหตุที่เมื่ออินเทอร์เฟซเปลี่ยนไป ไม่จำเป็นต้องเปลี่ยนคลาสการใช้งาน
แล้วมรดกหลายรายการล่ะ?
เมื่อคลาสใช้อินเทอร์เฟซมากกว่าหนึ่ง (เช่น สอง) และอินเทอร์เฟซเหล่านี้มีวิธีการเริ่มต้นที่เหมือนกัน สิ่งต่างๆ จะซับซ้อนมาก คลาสสืบทอดวิธีการเริ่มต้นใด ไม่ใช่ทั้งสองอย่าง! ในกรณีนี้ ตัวคลาสเอง (โดยตรงหรือคลาสที่สูงกว่าแผนผังการสืบทอด) จะต้องใช้วิธีการเริ่มต้น
เช่นเดียวกับเมื่ออินเทอร์เฟซหนึ่งใช้วิธีการเริ่มต้นและอินเทอร์เฟซอื่นประกาศวิธีการเริ่มต้นเป็นนามธรรม Java 8 พยายามหลีกเลี่ยงความคลุมเครือและรักษาความเข้มงวด หากมีการประกาศวิธีการในหลายอินเทอร์เฟซ จะไม่มีการใช้งานเริ่มต้นใดๆ สืบทอดมา และคุณจะได้รับข้อผิดพลาดขณะคอมไพล์
อย่างไรก็ตาม หากคุณคอมไพล์ชั้นเรียนของคุณ ก็จะไม่มีข้อผิดพลาดเวลาคอมไพล์ ณ จุดนี้ Java 8 ไม่สอดคล้องกัน มันมีเหตุผลของตัวเองและมีเหตุผลหลายประการ ฉันไม่ต้องการอธิบายอย่างละเอียดหรือพูดคุยเชิงลึกที่นี่ (เพราะ: เวอร์ชันออกแล้ว เวลาสนทนายาวเกินไป และแพลตฟอร์มนี้ไม่เคยมีมาก่อน การอภิปรายดังกล่าว)
1. สมมติว่าคุณมีสองอินเทอร์เฟซและคลาสการใช้งาน
2. หนึ่งในอินเทอร์เฟซใช้เมธอดเริ่มต้น m()
3. รวบรวมอินเทอร์เฟซและคลาสการใช้งานเข้าด้วยกัน
4. แก้ไขอินเทอร์เฟซที่ไม่มีเมธอด m() และประกาศเมธอด m() ให้เป็นนามธรรม
5. คอมไพล์อินเทอร์เฟซที่แก้ไขใหม่แยกกัน
6. รันคลาสการใช้งาน
1. แก้ไขอินเทอร์เฟซที่มีเมธอดนามธรรม m() และสร้างการใช้งานเริ่มต้น
2. รวบรวมอินเทอร์เฟซที่แก้ไข
3. เรียกใช้คลาส: ล้มเหลว
เมื่ออินเทอร์เฟซสองตัวจัดเตรียมการใช้งานเริ่มต้นสำหรับวิธีการเดียวกัน จะไม่สามารถเรียกใช้วิธีนี้ได้เว้นแต่คลาสการใช้งานจะใช้วิธีการเริ่มต้นด้วย (ไม่ว่าจะโดยตรงหรือโดยคลาสระดับที่สูงกว่าในแผนผังการสืบทอด)
รหัสตัวอย่าง:
เพื่อสาธิตตัวอย่างข้างต้น ฉันได้สร้างไดเร็กทอรีทดสอบสำหรับ C.java และมีไดเร็กทอรีย่อย 3 ไดเร็กทอรีอยู่ข้างใต้สำหรับจัดเก็บ I1.java และ I2.java ไดเร็กทอรีทดสอบมีซอร์สโค้ด C.java ของคลาส C ไดเร็กทอรีฐานมีเวอร์ชันของอินเทอร์เฟซที่สามารถคอมไพล์และรันได้ I1 มีเมธอด m() พร้อมการใช้งานเริ่มต้น และ I2 ไม่มีเมธอดใดๆ
คลาสการประยุกต์ใช้งานมีเมธอดหลักเพื่อให้เราสามารถดำเนินการในการทดสอบของเราได้ มันจะตรวจสอบว่ามีพารามิเตอร์บรรทัดคำสั่งหรือไม่ เพื่อให้เราสามารถทดสอบการเรียก m() และไม่เรียก m() ได้อย่างง่ายดาย
คัดลอกรหัสรหัสดังต่อไปนี้:
~/github/test$ cat C.java
คลาสสาธารณะ C ใช้ I1, I2 {
โมฆะคงที่สาธารณะ main (String [] args) {
C c = ใหม่ C();
ถ้า (args.length == 0) {
ซม.();
-
-
-
~/github/test$ cat base/I1.java
อินเทอร์เฟซสาธารณะ I1 {
ค่าเริ่มต้นเป็นโมฆะ m(){
System.out.println("สวัสดีอินเทอร์เฟซ 1");
-
-
~/github/test$ ฐานแมว/I2.java
อินเทอร์เฟซสาธารณะ I2 {
-
ใช้บรรทัดคำสั่งต่อไปนี้เพื่อคอมไพล์และรัน:
คัดลอกโค้ดดังนี้:~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
สวัสดีอินเทอร์เฟซ 1
ไดเร็กทอรีที่เข้ากันได้ประกอบด้วยอินเทอร์เฟซ I2 พร้อมวิธีนามธรรม m() และอินเทอร์เฟซ I1 ที่ยังไม่ได้แก้ไข
คัดลอกโค้ดดังนี้:~/github/test$ cat Compatible/I2.java
อินเทอร์เฟซสาธารณะ I2 {
โมฆะ m();
-
สิ่งนี้ไม่สามารถใช้ในการรวบรวมคลาส C:
คัดลอกโค้ดดังนี้:~/github/test$ javac -cp .:เข้ากันได้กับ C.java
C.java:1: ข้อผิดพลาด: C ไม่ใช่นามธรรมและไม่ได้แทนที่วิธีนามธรรม m() ใน I2
คลาสสาธารณะ C ใช้ I1, I2 {
-
1 ข้อผิดพลาด
ข้อความแสดงข้อผิดพลาดมีความแม่นยำมาก เนื่องจากเรามี C.class ที่ได้รับในการคอมไพล์ครั้งก่อน ถ้าเราคอมไพล์อินเทอร์เฟซในไดเร็กทอรีที่เข้ากันได้ เราจะยังคงได้รับอินเทอร์เฟซสองตัวที่สามารถรันคลาสการใช้งานได้:
คัดลอกรหัสรหัสดังต่อไปนี้:
~/github/test$ javac เข้ากันได้/I*.java
~/github/test$ java -cp .:เข้ากันได้กับ C
สวัสดีอินเทอร์เฟซ 1
ไดเร็กทอรีที่สามที่เรียกว่าผิดมีอินเทอร์เฟซ I2 ที่กำหนดเมธอด m() ด้วย:
คัดลอกรหัสรหัสดังต่อไปนี้:
~/github/test$ cat ผิด/I2.java
อินเทอร์เฟซสาธารณะ I2 {
ค่าเริ่มต้นเป็นโมฆะ m(){
System.out.println("สวัสดีอินเตอร์เฟส 2");
-
-
เราควรนำปัญหามารวบรวม แม้ว่าเมธอด m() จะถูกกำหนดไว้สองครั้ง แต่คลาสการใช้งานยังคงสามารถทำงานได้ตราบใดที่ไม่ได้เรียกเมธอดที่กำหนดไว้หลายครั้ง อย่างไรก็ตาม ตราบใดที่เราเรียกเมธอด m() ก็จะล้มเหลวทันที นี่คือพารามิเตอร์บรรทัดคำสั่งที่เราใช้:
คัดลอกรหัสรหัสดังต่อไปนี้:
~/github/test$ javac ผิด/*.java
~/github/test$ java -cp .:wrong C
ข้อยกเว้นในเธรด "main" java.lang.Inเข้ากันได้กับClassChangeError: ขัดแย้งกัน
วิธีการเริ่มต้น: I1.m I2.m
ที่ Cm(C.java)
ที่ C.main(C.java:5)
~/github/test$ java -cp .:ผิด C x
~/github/ทดสอบ$
สรุปแล้ว
เมื่อคุณพอร์ตไลบรารีคลาสที่เพิ่มการใช้งานเริ่มต้นให้กับอินเทอร์เฟซกับสภาพแวดล้อม Java 8 โดยทั่วไปจะไม่มีปัญหา อย่างน้อยนั่นคือสิ่งที่นักพัฒนาไลบรารีคลาส Java8 คิดเมื่อพวกเขาเพิ่มวิธีการเริ่มต้นให้กับคลาสคอลเลกชัน แอปพลิเคชันที่ใช้ไลบรารีของคุณยังคงใช้ไลบรารี Java 7 ที่ไม่มีวิธีการเริ่มต้น เมื่อใช้และแก้ไขไลบรารีคลาสต่างๆ จำนวนมาก มีโอกาสเล็กน้อยที่ความขัดแย้งจะเกิดขึ้น สิ่งนี้สามารถหลีกเลี่ยงได้อย่างไร?
ออกแบบห้องสมุดชั้นเรียนของคุณเหมือนเมื่อก่อน อย่ามองข้ามเมื่อต้องอาศัยวิธีการเริ่มต้น อย่าใช้เป็นทางเลือกสุดท้าย เลือกชื่อวิธีการอย่างชาญฉลาดเพื่อหลีกเลี่ยงความขัดแย้งกับอินเทอร์เฟซอื่นๆ เราจะเรียนรู้วิธีใช้คุณสมบัตินี้เพื่อการพัฒนาในการเขียนโปรแกรม Java