เหตุใดจึงไม่อนุญาต“ ขั้นสุดท้าย” ในวิธีส่วนต่อประสาน Java 8


335

หนึ่งในคุณสมบัติที่มีประโยชน์ที่สุดของ Java 8 คือdefaultวิธีการใหม่ในส่วนต่อประสาน มีสองเหตุผลหลัก (อาจมีเหตุผลอื่น) ทำไมพวกเขาถึงได้รับการแนะนำ:

  • ให้การใช้งานเริ่มต้นจริง ตัวอย่าง:Iterator.remove()
  • อนุญาตให้ใช้กับวิวัฒนาการ JDK API ตัวอย่าง:Iterable.forEach()

จากมุมมองของนักออกแบบ API ของผมจะชอบที่จะสามารถใช้การปรับเปลี่ยนวิธีการอื่น ๆ finalบนอินเตอร์เฟซเช่น สิ่งนี้จะเป็นประโยชน์เมื่อเพิ่มวิธีอำนวยความสะดวกป้องกันการแทนที่ "โดยไม่ตั้งใจ" ในการใช้คลาส:

interface Sender {

    // Convenience method to send an empty message
    default final void send() {
        send(null);
    }

    // Implementations should only implement this method
    void send(String message);
}

ข้างต้นเป็นวิธีปฏิบัติทั่วไปแล้วถ้าSenderเป็นคลาส:

abstract class Sender {

    // Convenience method to send an empty message
    final void send() {
        send(null);
    }

    // Implementations should only implement this method
    abstract void send(String message);
}

ตอนนี้defaultและfinalจะเห็นได้ชัดขัดแย้งกับคำหลัก แต่เริ่มต้นคำหลักที่ตัวเองจะไม่ต้องได้รับอย่างเคร่งครัดดังนั้นฉันสมมติว่าความขัดแย้งนี้เป็นเจตนาที่จะสะท้อนให้เห็นถึงความแตกต่างระหว่าง"วิธีการเรียนกับร่างกาย" (เพียงวิธีการ) และ"อินเตอร์เฟซ วิธีการกับร่างกาย " (วิธีการเริ่มต้น) คือความแตกต่างคือฉันยังไม่เข้าใจ

ในบางช่วงเวลาการสนับสนุนตัวดัดแปลงเช่นstaticและfinalวิธีการเชื่อมต่อยังไม่ได้รับการสำรวจอย่างเต็มที่อ้างว่า Brian Goetz :

อีกส่วนคือว่าเราจะไปสนับสนุนเครื่องมือการสร้างคลาสในอินเทอร์เฟซเช่นวิธีสุดท้ายวิธีการส่วนตัววิธีการป้องกันวิธีคงที่และอื่น ๆ อีกมากมายคำตอบคือ: เรายังไม่รู้

ตั้งแต่เวลาดังกล่าวในช่วงปลายปี 2011 เห็นได้ชัดว่ามีการเพิ่มการสนับสนุนstaticวิธีการในอินเทอร์เฟซ ชัดเจนนี้เพิ่มจำนวนมากของมูลค่าให้กับห้องสมุด JDK Comparator.comparing()ตัวเองเช่นเดียวกับ

คำถาม:

อะไรคือเหตุผลfinal(และstatic final) ที่ไม่เคยทำให้เป็นอินเทอร์เฟซ Java 8?


10
ฉันขอโทษที่เป็นผ้าห่มเปียก แต่วิธีเดียวที่คำถามที่แสดงในชื่อเรื่องจะได้รับคำตอบภายในเงื่อนไขของ SO คือผ่านใบเสนอราคาจาก Brian Goetz หรือกลุ่มผู้เชี่ยวชาญ JSR ฉันเข้าใจว่า BG ได้ขอให้มีการอภิปรายสาธารณะ แต่นั่นเป็นสิ่งที่ขัดต่อข้อกำหนดของ SO เนื่องจากเป็น 'ความคิดเห็นเป็นหลัก' สำหรับฉันแล้วดูเหมือนว่าความรับผิดชอบกำลังตกอยู่ที่นี่ เป็นหน้าที่ของกลุ่มผู้เชี่ยวชาญและกระบวนการชุมชน Java ที่กว้างขึ้นเพื่อกระตุ้นให้เกิดการอภิปรายและมีเหตุผล ไม่ใช่ของ ดังนั้นฉันจึงลงคะแนนให้ปิดเป็น 'อิงตามความคิดเห็นเป็นหลัก'
มาร์ควิสแห่ง Lorne

2
ในขณะที่เราทุกคนรู้finalป้องกันวิธีการที่ถูกแทนที่และเห็นว่าคุณจะต้องแทนที่วิธีการที่สืบทอดมาจากอินเตอร์เฟซที่ฉันไม่เห็นว่าทำไมมันจะทำให้รู้สึกว่ามันจะทำให้สุดท้าย เว้นแต่จะมีความหมายว่าวิธีการเป็นขั้นสุดท้ายหลังจากที่เอาชนะมันครั้งเดียว .. ในกรณีนี้อาจมีปัญหา qas? ถ้าฉันไม่เข้าใจเรื่องนี้โปรดให้ฉัน kmow ดูเหมือนว่าน่าสนใจ
Vince Emigh

22
@EJP: "ถ้าฉันสามารถทำเช่นนั้นได้คุณและตอบคำถามของคุณเองซึ่งไม่จำเป็นต้องถาม" นั่นจะใช้กับคำถามทุกข้อในฟอรัมนี้ใช่ไหม ฉันสามารถใช้ Google เป็นเวลา 5 ชั่วโมงด้วยตนเองและเรียนรู้สิ่งที่ทุกคนต้องเรียนรู้ด้วยวิธีที่ยากพอ ๆ กัน หรือเราจะรออีกสองสามนาทีเพื่อให้ใครสักคนตอบคำถามที่ดียิ่งขึ้นว่าทุกคนในอนาคต (รวมถึง 12 upvotes และ 8 ดาวจนถึงตอนนี้) สามารถได้รับประโยชน์เพราะ Google มีการอ้างอิงอย่างดี ใช่. คำถามนี้สามารถเข้ากับคำถามและคำตอบของ SO ได้อย่างสมบูรณ์แบบ
Lukas Eder

5
@VinceEmigh " ... การเห็นว่าคุณจะต้องแทนที่วิธีการที่สืบทอดมาจากส่วนต่อประสาน ... " นั่นไม่เป็นความจริงใน Java 8 Java 8 ช่วยให้คุณสามารถใช้วิธีการในส่วนต่อประสานได้ซึ่งหมายความว่าคุณไม่จำเป็นต้องติดตั้ง ชั้นเรียน ที่นี่finalจะมีการใช้งานในการป้องกันการใช้คลาสจากการแทนที่การใช้งานเริ่มต้นของวิธีการอินเทอร์เฟซ
awksp

28
@EJP คุณไม่มีทางรู้: Brian Goetz อาจตอบกลับ !
assylias

คำตอบ:


419

คำถามนี้มีความเกี่ยวข้องกับเหตุผลที่ว่าทำไม "การซิงโครไนซ์" ไม่ได้รับอนุญาตในวิธีการเชื่อมต่อ Java 8?

สิ่งสำคัญที่ต้องเข้าใจเกี่ยวกับวิธีการเริ่มต้นคือเป้าหมายการออกแบบหลักคือวิวัฒนาการของส่วนต่อประสานไม่ใช่ "เปลี่ยนอินเทอร์เฟซให้เป็น (ลักษณะปานกลาง)" ในขณะที่มีการทับซ้อนกันระหว่างทั้งสองและเราพยายามที่จะรองรับหลังที่มันไม่ได้เข้าไปในอดีตคำถามเหล่านี้เข้าใจได้ดีที่สุดเมื่อดูในแสงนี้ (โปรดทราบด้วยว่าวิธีการเรียนคือไปจะแตกต่างจากวิธีการอินเตอร์เฟซที่ไม่ว่าสิ่งที่ตั้งใจโดยอาศัยอำนาจตามความจริงที่ว่าวิธีการอินเตอร์เฟซที่สามารถสืบทอดคูณ.)

แนวคิดพื้นฐานของวิธีการเริ่มต้นคือ: มันเป็นวิธีการติดต่อกับการใช้งานเริ่มต้นและชั้นเรียนที่ได้รับสามารถให้การดำเนินงานที่เฉพาะเจาะจงมากขึ้น และเนื่องจากศูนย์ออกแบบเป็นวิวัฒนาการของส่วนต่อประสานงานจึงเป็นเป้าหมายการออกแบบที่สำคัญที่วิธีการเริ่มต้นสามารถเพิ่มไปยังอินเทอร์เฟซหลังจากข้อเท็จจริงในลักษณะที่เข้ากันได้กับแหล่งที่มาและเข้ากันได้กับไบนารี

คำตอบที่ง่ายเกินไปสำหรับ "ทำไมไม่ใช่วิธีการเริ่มต้นขั้นสุดท้าย" ก็คือร่างกายจะไม่เพียง แต่เป็นการเริ่มต้นการใช้งานมันจะเป็นการดำเนินการเพียงอย่างเดียว ในขณะที่นั่นเป็นคำตอบที่ง่ายเกินไปมันให้เบาะแสว่าคำถามนั้นมุ่งไปในทิศทางที่น่าสงสัยอยู่แล้ว

อีกเหตุผลหนึ่งที่วิธีการอินเตอร์เฟสสุดท้ายเป็นที่น่าสงสัยก็คือพวกเขาสร้างปัญหาที่เป็นไปไม่ได้สำหรับผู้ดำเนินการ ตัวอย่างเช่นสมมติว่าคุณมี:

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

ที่นี่ทุกอย่างดี Cสืบทอดจากfoo() Aตอนนี้สมมติว่าBมีการเปลี่ยนแปลงfooวิธีการโดยมีค่าเริ่มต้น

interface B { 
    default void foo() { ... }
}

ตอนนี้เมื่อเราไปคอมไพล์ใหม่Cคอมไพเลอร์จะบอกเราว่ามันไม่รู้ว่าพฤติกรรมใดที่จะสืบทอดfoo()ดังนั้นCต้องลบล้างมัน (และสามารถเลือกที่จะมอบหมายให้A.super.foo()ถ้ามันต้องการที่จะรักษาพฤติกรรมเดิม) แต่จะเกิดอะไรขึ้นBได้ทำให้เป็นค่าเริ่มต้นfinalและAไม่อยู่ภายใต้การควบคุมของผู้เขียนCหรือไม่ ตอนนี้Cหักอย่างแก้ไขไม่ได้; มันไม่สามารถคอมไพล์โดยไม่ต้องเอาชนะfoo()แต่มันไม่สามารถแทนที่foo()ถ้ามันเป็นครั้งสุดท้ายBถ้ามันเป็นครั้งสุดท้ายใน

นี่เป็นเพียงตัวอย่างเดียว แต่ประเด็นก็คือว่าวาระสุดท้ายของวิธีการเป็นเครื่องมือที่ทำให้เกิดความรู้สึกมากขึ้นในโลกของคลาสที่สืบทอดเดี่ยว (โดยทั่วไปจะเป็นคู่ที่ระบุพฤติกรรม) มากกว่าอินเทอร์เฟซที่สนับสนุนพฤติกรรมเพียงอย่างเดียว ได้รับการถ่ายทอด. มันยากเกินไปที่จะให้เหตุผลเกี่ยวกับ "สิ่งที่อินเทอร์เฟซอื่น ๆ อาจถูกนำไปผสมในตัวดำเนินการในที่สุด" และการอนุญาตให้วิธีการอินเทอร์เฟซเป็นขั้นสุดท้ายอาจทำให้เกิดปัญหาเหล่านี้ (และพวกเขาจะระเบิดขึ้น ผู้ใช้ที่ไม่ดีซึ่งพยายามใช้งาน)

อีกสาเหตุที่ทำให้พวกเขาไม่อนุญาตคือพวกเขาไม่ได้หมายความว่าคุณคิดว่าพวกเขาหมายถึงอะไร การใช้งานเริ่มต้นจะได้รับการพิจารณาหากคลาส (หรือซูเปอร์คลาส) ไม่ได้มีการประกาศ (เป็นรูปธรรมหรือนามธรรม) ของวิธีการ หากวิธีการเริ่มต้นเป็นขั้นสุดท้าย แต่ superclass ดำเนินการแล้ววิธีการเริ่มต้นจะถูกละเว้นซึ่งอาจไม่ใช่สิ่งที่ผู้เขียนเริ่มต้นคาดหวังเมื่อประกาศว่ามันเป็นครั้งสุดท้าย (พฤติกรรมการสืบทอดนี้เป็นภาพสะท้อนของศูนย์การออกแบบสำหรับวิธีการเริ่มต้น - วิวัฒนาการของอินเทอร์เฟซมันเป็นไปได้ที่จะเพิ่มวิธีการเริ่มต้น (หรือการใช้งานเริ่มต้นกับวิธีการอินเทอร์เฟซที่มีอยู่) ไปยังอินเทอร์เฟซที่มีอยู่แล้ว พฤติกรรมของคลาสที่มีอยู่ที่ใช้อินเตอร์เฟส


88
ยอดเยี่ยมมากที่เห็นคุณตอบคำถามเกี่ยวกับคุณสมบัติภาษาใหม่! มันมีประโยชน์มากในการทำความกระจ่างเกี่ยวกับเจตนาและรายละเอียดของการออกแบบเมื่อค้นหาว่าเราควรใช้คุณลักษณะใหม่อย่างไร คนอื่น ๆ ที่เกี่ยวข้องกับการออกแบบมีส่วนร่วมใน SO หรือเปล่าหรือคุณแค่ทำสิ่งนี้ด้วยตัวเอง? ฉันจะทำตามคำตอบของคุณภายใต้แท็ก java-8 - ฉันต้องการทราบว่ามีคนอื่นทำเช่นเดียวกันเพื่อให้ฉันสามารถปฏิบัติตามพวกเขาด้วย
Shorn

10
@Shorn Stuart Marksมีการใช้งานในแท็ก java-8 Jeremy Mansonได้โพสต์ในอดีต ฉันจำได้ว่าได้เห็นข้อความจาก Joshua Bloch แต่ไม่สามารถค้นหาได้ในขณะนี้
assylias

1
ขอแสดงความยินดีที่มาพร้อมกับวิธีการอินเทอร์เฟซที่เป็นค่าเริ่มต้นซึ่งเป็นวิธีที่ยอดเยี่ยมกว่าในการทำสิ่งที่ C # ทำด้วยวิธีการขยายที่ไม่เข้าใจ สำหรับคำถามนี้คำตอบเกี่ยวกับความเป็นไปไม่ได้ที่จะแก้ไขปัญหาความขัดแย้งในเรื่องชื่อ แต่เป็นเหตุผลที่เหลืออยู่ก็คือปรัชญาที่ไม่น่าเชื่อ (หากฉันต้องการให้วิธีการอินเทอร์เฟซเป็นที่สิ้นสุดคุณควรสันนิษฐานว่าฉันต้องมีเหตุผลที่ดีสำหรับฉันที่จะไม่อนุญาตให้ผู้อื่นนำเสนอการใช้งานที่แตกต่างจากของฉัน)
Mike Nakis

1
อย่างไรก็ตามการแก้ไขข้อขัดแย้งของชื่ออาจทำได้ในลักษณะเดียวกับที่ทำใน C # โดยเพิ่มชื่อของอินเทอร์เฟซเฉพาะที่นำไปใช้กับการประกาศวิธีการใช้งาน ดังนั้นสิ่งที่ฉันนำกลับบ้านจากทั้งหมดนี้คือวิธีการอินเทอร์เฟซเริ่มต้นไม่สามารถสุดท้ายใน Java เพราะที่จะต้องมีการแก้ไขเพิ่มเติมในไวยากรณ์ซึ่งคุณไม่รู้สึกดีเกี่ยวกับ (อาจจะแหลมเกินไป?)
Mike Nakis

18
@Trying ชั้นเรียนบทคัดย่อยังคงเป็นวิธีเดียวที่จะแนะนำรัฐหรือใช้วิธีการวัตถุหลัก วิธีการเริ่มต้นสำหรับพฤติกรรมบริสุทธิ์ ; คลาสนามธรรมสำหรับพฤติกรรมควบคู่กับรัฐ
Brian Goetz

43

ในรายการจดหมายแลมบ์ดามีการพูดคุยมากมายเกี่ยวกับเรื่องนี้ หนึ่งในคนที่ดูเหมือนจะมีการถกเถียงกันมากมายเกี่ยวกับสิ่งนั้นคือ:แตกต่างกันวิธีการอินเตอร์เฟซการมองเห็น (เป็นป้อมปราการสุดท้าย)

ในการสนทนา Talden ผู้เขียนคำถามดั้งเดิมถามสิ่งที่คล้ายกับคำถามของคุณ:

การตัดสินใจที่จะทำให้สมาชิกทุกคนในอินเทอร์เฟซสาธารณะเป็นการตัดสินใจที่โชคร้าย การใช้อินเทอร์เฟซในการออกแบบภายในแสดงให้เห็นถึงการใช้งานข้อมูลส่วนตัวเป็นเรื่องใหญ่

มันเป็นเรื่องยากที่จะแก้ไขโดยไม่เพิ่มความชัดเจนหรือความเข้ากันได้ที่แตกต่างให้กับภาษา การแบ่งความเข้ากันได้ของขนาดและความละเอียดอ่อนที่อาจเกิดขึ้นนั้นไม่อาจมองข้ามได้ดังนั้นจึงต้องมีวิธีแก้ปัญหาที่ไม่ทำลายรหัสที่มีอยู่

ไม่สามารถแนะนำคำหลัก 'แพ็คเกจ' อีกครั้งในฐานะตัวระบุการเข้าถึงได้ การไม่มีตัวระบุในอินเทอร์เฟซจะบอกเป็นนัยถึงการเข้าถึงสาธารณะและการไม่มีตัวระบุในคลาสหมายถึงการเข้าถึงแพ็กเกจ ตัวระบุชนิดใดที่สมเหตุสมผลในส่วนต่อประสานนั้นไม่ชัดเจน - โดยเฉพาะอย่างยิ่งหากเพื่อลดภาระความรู้ให้กับนักพัฒนาเราต้องมั่นใจว่าตัวระบุการเข้าถึงหมายถึงสิ่งเดียวกันทั้งในชั้นเรียนและส่วนต่อประสาน

ในกรณีที่ไม่มีวิธีการเริ่มต้นฉันได้สันนิษฐานว่าตัวระบุของสมาชิกในอินเทอร์เฟซจะต้องมีอย่างน้อยที่สุดเท่าที่ปรากฏเป็นอินเทอร์เฟซตัวเอง (ดังนั้นอินเทอร์เฟซสามารถนำมาใช้จริงในบริบททั้งหมดที่มองเห็น) แน่นอน

มีการสื่อสารที่ชัดเจนว่าการสนทนาในขอบเขตนี้เป็นไปได้หรือไม่? ถ้าไม่ควรจัดที่อื่น

ในที่สุดคำตอบของ Brian Goetzก็คือ:

ใช่มันกำลังถูกสำรวจแล้ว

อย่างไรก็ตามให้ฉันตั้งความคาดหวังที่เป็นจริง - คุณสมบัติภาษา / VM มีระยะเวลารอคอยนานแม้กระทั่งสิ่งที่ดูเหมือนเล็กน้อยเช่นนี้ เวลาสำหรับการเสนอแนวคิดคุณสมบัติภาษาใหม่สำหรับ Java SE 8 ได้ผ่านไปแล้ว

ดังนั้นส่วนใหญ่แล้วมันไม่เคยถูกนำมาใช้เพราะมันไม่เคยเป็นส่วนหนึ่งของขอบเขต มันไม่เคยถูกนำเสนอในเวลาที่จะต้องพิจารณา

ในการอภิปรายที่ร้อนแรงอีกครั้งเกี่ยวกับวิธีการป้องกันขั้นสุดท้ายในเรื่องไบรอันกล่าวอีกครั้ง :

และคุณได้สิ่งที่คุณต้องการอย่างแน่นอน นั่นคือสิ่งที่คุณสมบัตินี้เพิ่ม - การสืบทอดหลายลักษณะ แน่นอนเราเข้าใจว่าผู้คนจะใช้พวกเขาเป็นลักษณะ และเราทำงานอย่างหนักเพื่อให้แน่ใจว่ารูปแบบของการสืบทอดที่พวกเขาเสนอนั้นเรียบง่ายและสะอาดพอที่ผู้คนจะได้รับผลลัพธ์ที่ดีในสถานการณ์ที่หลากหลาย ในขณะเดียวกันเราเลือกที่จะไม่ผลักพวกเขาออกไปนอกขอบเขตของสิ่งที่ใช้งานได้ง่ายและหมดจดและนั่นนำไปสู่การ "ตอบสนองคุณไม่ได้ไปไกลพอ" ในบางกรณี แต่จริงๆแล้วกระทู้นี้ส่วนใหญ่ดูเหมือนจะบ่นว่าแก้วนั้นเต็มไปเพียง 98% ฉันจะเอา 98% และไปกับมัน!

ดังนั้นสิ่งนี้ตอกย้ำทฤษฎีของฉันว่ามันไม่ได้เป็นส่วนหนึ่งของขอบเขตหรือส่วนหนึ่งของการออกแบบของพวกเขา สิ่งที่พวกเขาทำคือให้ฟังก์ชั่นเพียงพอที่จะจัดการกับปัญหาของวิวัฒนาการ API


4
ฉันเห็นว่าฉันควรจะรวมชื่อเก่า "วิธีการป้องกัน" ใน Odyssey googling ของฉันเมื่อเช้านี้ +1 สำหรับขุดสิ่งนี้
Marco13

1
ยินดีขุดข้อเท็จจริงประวัติศาสตร์ ข้อสรุปของคุณสอดคล้องกับคำตอบอย่างเป็นทางการ
ลูคัสอีเดอร์

ฉันไม่เห็นว่าทำไมมันจะย้อนกลับเข้ากันได้ สุดท้ายไม่ได้รับอนุญาตก่อน ตอนนี้อนุญาตสำหรับไพรเวต แต่ไม่ได้รับการป้องกัน myeh ... ส่วนตัวไม่สามารถใช้ ... ส่วนต่อประสานที่ขยายส่วนต่อประสานอื่นอาจใช้ส่วนของส่วนติดต่อผู้ปกครอง แต่มันต้องเปิดเผยความสามารถในการบรรทุกเกินพิกัดแก่ผู้อื่น ...
mmm

17

มันจะยากที่จะค้นหาและระบุคำตอบ "THE" สำหรับ resons ที่กล่าวถึงในความคิดเห็นจาก @EJP: มีคนประมาณ 2 (+/- 2) ในโลกที่สามารถให้คำตอบที่แน่นอนได้เลย และมีข้อสงสัยคำตอบอาจเป็นเช่น "การสนับสนุนวิธีการเริ่มต้นขั้นสุดท้ายดูเหมือนจะไม่คุ้มค่ากับความพยายามในการปรับโครงสร้างกลไกการแก้ปัญหาการโทรภายใน" แน่นอนว่านี่คือการเก็งกำไร แต่อย่างน้อยก็มีการสนับสนุนจากหลักฐานที่ละเอียดอ่อนเช่นข้อความนี้(โดยหนึ่งในสองคน) ในรายการส่งเมลของ OpenJDK :

"ฉันคิดว่าถ้าอนุญาตให้ใช้วิธี" เริ่มต้นขั้นสุดท้าย "พวกเขาอาจต้องการการเขียนใหม่จากการเรียกใช้ภายในถึงการเรียกใช้อินเทอร์เฟซการโต้ตอบ

และข้อเท็จจริงเล็ก ๆ น้อย ๆ เช่นนั้นวิธีการนั้นไม่ถือเป็นวิธีสุดท้าย (จริงๆ) เมื่อเป็นdefaultวิธีการที่นำมาใช้ในปัจจุบันในวิธีการ :: is_final_methodวิธีใน OpenJDK

นอกจากนี้ข้อมูล "ผู้มีอำนาจ" จริงๆแล้วหายากจริงๆแม้จะมีการค้นหาเว็บมากเกินไปและโดยการอ่านบันทึกการกระทำ ฉันคิดว่ามันอาจเกี่ยวข้องกับความกำกวมที่อาจเกิดขึ้นในระหว่างการแก้ปัญหาของการเรียกใช้วิธีการเชื่อมต่อกับinvokeinterfaceคำสั่งและและการเรียกใช้วิธีการเรียนที่สอดคล้องกับinvokevirtualคำสั่ง: สำหรับinvokevirtualคำสั่งนั้นอาจมีการค้นหาvtableอย่างง่าย ๆจากซูเปอร์คลาสหรือดำเนินการโดยคลาสโดยตรง ในทางตรงกันข้ามกับที่มีinvokeinterfaceการเรียกร้องต้องตรวจสอบเว็บไซต์โทรที่เกี่ยวข้องเพื่อหาที่อินเตอร์เฟซที่สายนี้จริงหมายถึง (ซึ่งจะมีการอธิบายในรายละเอียดมากขึ้นในInterfaceCallsหน้าของ HotSpot วิกิพีเดีย) อย่างไรก็ตามfinalเมธอดจะไม่ถูกแทรกลงในvtableเลยหรือแทนที่รายการที่มีอยู่ในvtable (ดูklassVtable.cpp. สาย 333 ) และในทำนองเดียวกันวิธีการเริ่มต้นจะแทนที่รายการที่มีอยู่ในvtable (ดูklassVtable.cpp, Line 202 ) ดังนั้นเหตุผลที่แท้จริง (และคำตอบ) จะต้องถูกซ่อนอยู่ลึกลงไปในกลไกการแก้ปัญหาที่ซับซ้อน (แต่ค่อนข้างซับซ้อน) แต่บางทีการอ้างอิงเหล่านี้อาจถูกพิจารณาว่าเป็นประโยชน์ไม่ว่าจะเป็นสำหรับคนอื่น ๆ ที่จัดการคำตอบจริงเท่านั้น จากนั้น.


ขอบคุณสำหรับข้อมูลเชิงลึกที่น่าสนใจ ชิ้นส่วนโดย John Rose เป็นร่องรอยที่น่าสนใจ ฉันยังไม่เห็นด้วยกับ @EJP ในฐานะที่เป็นตัวอย่างที่เคาน์เตอร์ตรวจสอบคำตอบของฉันให้เป็นที่น่าสนใจมากคำถามที่คล้ายกันสไตล์มากโดยปีเตอร์ Lawrey มันเป็นไปได้ที่จะขุดออกข้อเท็จจริงทางประวัติศาสตร์และฉันมักจะดีใจที่ได้พบพวกเขาที่นี่ในกองมากเกิน (ที่อื่น?) แน่นอนคำตอบของคุณยังคงเป็นการเก็งกำไรและฉันไม่เชื่อ 100% ว่ารายละเอียดการดำเนินการของ JVM จะเป็นเหตุผลสุดท้าย (ตั้งใจให้เล่น) เพื่อให้ JLS จดบันทึกด้วยวิธีใดวิธีหนึ่ง ...
Lukas Eder

@LukasEder แน่นอนว่าชนิดของคำถามเหล่านี้มีความน่าสนใจและ IMHO พอดีกับ Q & A รูปแบบ ฉันคิดว่ามีสองจุดสำคัญที่ทำให้เกิดการโต้แย้งที่นี่: ประการแรกคือคุณถามหา "เหตุผล" ในหลายกรณีอาจไม่ได้ทำเป็นเอกสารอย่างเป็นทางการ เช่นไม่มี "เหตุผล" ที่กล่าวถึงใน JLS ทำไมไม่มีints ที่ไม่ได้ลงชื่อแต่ดูstackoverflow.com/questions/430346 ... ...
Marco13

... ... ข้อที่สองคือคุณถามเฉพาะ "การอ้างอิงเชิงอำนาจ" ซึ่งจะลดจำนวนผู้ที่กล้าเขียนคำตอบจาก "ไม่กี่โหล" เป็น ... "ประมาณศูนย์" นอกเหนือจากนั้นฉันไม่แน่ใจว่าการพัฒนา JVM และการเขียนของ JLS นั้นมีการผสมผสานกันอย่างไรเช่นการพัฒนามีอิทธิพลต่อสิ่งที่เขียนลงใน JLS มากแค่ไหน แต่ ... ฉันจะหลีกเลี่ยงการเก็งกำไรที่นี่ -)
Marco13

1
ฉันยังคงพักคดีของฉัน ดูว่าใครตอบของฉันคำถามอื่น ๆ :-) มันจะตอนนี้ตลอดไปมีความชัดเจนกับคำตอบเผด็จการที่นี่ในกองมากเกินทำไมมันก็ตัดสินใจที่จะไม่สนับสนุนsynchronizedในdefaultวิธีการ
Lukas Eder

3
@ LukasEder ฉันเห็นเหมือนกันที่นี่ ใครจะคิดเช่นนั้น เหตุผลค่อนข้างน่าเชื่อถือโดยเฉพาะอย่างยิ่งสำหรับfinalคำถามนี้และมันค่อนข้างที่จะถ่อมใจว่าไม่มีใครดูเหมือนจะคิดตัวอย่างที่คล้ายกัน (หรืออาจจะมีบางคนคิดเกี่ยวกับตัวอย่างเหล่านี้ แต่ไม่รู้สึกมีอำนาจพอที่จะตอบ) ดังนั้นตอนนี้ (ขออภัยฉันต้องทำเช่นนี้) พูดคำสุดท้าย
Marco13

4

ฉันไม่คิดว่ามันเป็นสิ่งที่จำเป็นในการระบุfinalวิธีการอินเทอร์เฟซ convienience ฉันสามารถยอมรับว่ามันอาจจะเป็นประโยชน์ แต่ดูเหมือนว่าค่าใช้จ่ายมีประโยชน์มากกว่าประโยชน์

สิ่งที่คุณควรทำอย่างใดอย่างหนึ่งคือการเขียน javadoc ที่เหมาะสมสำหรับวิธีการเริ่มต้นแสดงให้เห็นว่าวิธีการคืออะไรและไม่ได้รับอนุญาตให้ทำ ด้วยวิธีดังกล่าวคลาสที่ใช้อินเตอร์เฟส "ไม่ได้รับอนุญาต" เพื่อเปลี่ยนการใช้งานแม้ว่าจะไม่มีการรับประกัน

ทุกคนสามารถเขียนสิ่งCollectionที่ยึดติดกับส่วนต่อประสานจากนั้นทำสิ่งต่าง ๆ ในวิธีการที่ใช้ในการโต้ตอบอย่างแท้จริงไม่มีทางที่จะป้องกันตัวคุณเองจากสิ่งนั้น


2
สัญญา Javadoc เป็นวิธีแก้ปัญหาที่ถูกต้องสำหรับตัวอย่างที่เป็นรูปธรรมที่ฉันได้ระบุไว้ในคำถามของฉัน แต่คำถามนั้นไม่ได้เกี่ยวกับวิธีการใช้งานอินเทอร์เฟซของความสะดวกสบาย คำถามนี้เกี่ยวกับเหตุผลที่เชื่อถือfinalได้ว่าทำไมจึงมีการตัดสินใจว่าจะไม่อนุญาตให้ใช้กับinterfaceวิธีการจาวา 8 อัตราส่วนต้นทุน / ผลประโยชน์ไม่เพียงพอเป็นตัวเลือกที่ดี แต่จนถึงตอนนี้นั่นเป็นการเก็งกำไร
Lukas Eder

0

เราเพิ่มdefaultคำหลักในวิธีการของเราภายในinterfaceเมื่อเรารู้ว่าชั้นเรียนที่ขยายinterfaceอาจหรือไม่อาจoverrideดำเนินการของเรา แต่ถ้าเราต้องการเพิ่มวิธีการที่เราไม่ต้องการให้คลาสที่ใช้งานใด ๆ มาแทนที่ เรามีทางเลือกสองทางให้เรา:

  1. เพิ่มdefault finalวิธีการ
  2. เพิ่มstaticวิธีการ

ตอนนี้ Java กล่าวว่าหากเรามีการclassใช้งานตั้งแต่สองวิธีขึ้นไปinterfacesที่พวกเขามีdefaultวิธีการที่มีชื่อและลายเซ็นของวิธีการที่เหมือนกันทุกประการพวกเขาจะทำซ้ำแล้วเราจะต้องให้การใช้งานของวิธีการนั้นในชั้นเรียน ตอนนี้ในกรณีของdefault finalวิธีการเราไม่สามารถให้บริการได้และเราติดขัด และนั่นคือสาเหตุที่finalไม่ใช้คำหลักในอินเทอร์เฟซ

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