เหตุใดจึงมีการเพิ่มวิธีเริ่มต้นและแบบคงที่ลงในอินเตอร์เฟสใน Java 8 เมื่อเรามีคลาสแบบนามธรรมแล้ว?


99

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

ในมุมมองของฉัน (อาจไร้เดียงสา) ไม่จำเป็นต้องละเมิดส่วนต่อประสานเช่นนี้ การเชื่อมต่อเป็นสัญญาที่คุณต้องปฏิบัติตามเสมอและนี่คือแนวคิดที่เรียบง่ายและบริสุทธิ์ ตอนนี้มันเป็นการผสมผสานของหลายสิ่งหลายอย่าง ในความเห็นของฉัน:

  1. วิธีการคงที่ไม่ได้เป็นของอินเตอร์เฟซ พวกมันอยู่ในคลาสยูทิลิตี้
  2. ไม่ควรอนุญาตวิธีการ "เริ่มต้น" ในอินเทอร์เฟซเลย คุณสามารถใช้คลาสนามธรรมสำหรับวัตถุประสงค์นี้ได้เสมอ

ในระยะสั้น:

ก่อน Java 8:

  • คุณสามารถใช้คลาสนามธรรมและคลาสปกติเพื่อจัดเตรียมเมธอด static และ default บทบาทของอินเตอร์เฟสชัดเจน
  • วิธีการทั้งหมดในอินเทอร์เฟซควรจะ overriden โดยการใช้คลาส
  • คุณไม่สามารถเพิ่มวิธีการใหม่ในอินเทอร์เฟซโดยไม่ต้องปรับเปลี่ยนการใช้งานทั้งหมด แต่นี่เป็นสิ่งที่ดีจริงๆ

หลังจาก Java 8:

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

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


30
คำถามโบนัส: ทำไมพวกเขาไม่แนะนำมรดกหลายอย่างให้กับชั้นเรียนแทน?

2
วิธีการคงที่ไม่ได้เป็นของอินเตอร์เฟซ พวกเขาอยู่ในชั้นเรียนยูทิลิตี้ ไม่มีพวกเขาอยู่ใน@Deprecatedหมวดหมู่! วิธีการคงที่เป็นหนึ่งในโครงสร้างที่ถูกทารุณกรรมมากที่สุดใน Java เนื่องจากความไม่รู้และความเกียจคร้าน วิธีการคงที่จำนวนมากมักจะหมายถึงโปรแกรมเมอร์ที่ไร้ความสามารถเพิ่มการมีเพศสัมพันธ์โดยคำสั่งหลายขนาดและเป็นฝันร้ายที่จะทดสอบหน่วยและ refactor เมื่อคุณตระหนักถึงสาเหตุที่พวกเขาคิดไม่ดี!

11
@JarrodRoberson คุณสามารถให้คำแนะนำเพิ่มเติม (ลิงก์จะดีมาก) เกี่ยวกับ "เป็นฝันร้ายที่จะทดสอบหน่วยและ refactor เมื่อคุณรู้ว่าทำไมพวกเขาเป็นความคิดที่ไม่ดี!"? ฉันไม่เคยคิดอย่างนั้นและฉันต้องการทราบเพิ่มเติมเกี่ยวกับเรื่องนี้
น้ำ

11
@Chris การสืบทอดหลายสถานะทำให้เกิดปัญหาโดยเฉพาะกับการจัดสรรหน่วยความจำ ( ปัญหาเพชรแบบดั้งเดิม ) การสืบทอดพฤติกรรมหลายอย่างขึ้นอยู่กับการดำเนินการตามสัญญาที่กำหนดไว้แล้วโดยส่วนต่อประสาน (ส่วนต่อประสานสามารถเรียกใช้วิธีอื่นที่ประกาศและต้องการ) ความแตกต่างที่ลึกซึ้ง แต่น่าสนใจมาก
ssube

1
คุณต้องการที่จะเพิ่มวิธีการในอินเตอร์เฟซที่มีอยู่เป็นเรื่องยุ่งยากหรือแทบจะเป็นไปไม่ได้ก่อนที่จะตอนนี้ java8 ตอนนี้คุณสามารถเพิ่มมันเป็นวิธีการเริ่มต้น
VdeX

คำตอบ:


59

ตัวอย่างแรงจูงใจที่ดีสำหรับวิธีการเริ่มต้นอยู่ในไลบรารีมาตรฐาน Java ซึ่งตอนนี้คุณมี

list.sort(ordering);

แทน

Collections.sort(list, ordering);

ฉันไม่คิดว่าพวกเขาจะทำอย่างนั้นได้หากไม่มีการใช้งานเหมือนกันมากกว่าหนึ่งList.sortครั้ง


19
C # เอาชนะปัญหานี้ด้วยวิธีการขยาย
Robert Harvey

5
และอนุญาตให้รายการที่เชื่อมโยงใช้พื้นที่พิเศษ O (1) และการรวมเวลา O (n log n) เนื่องจากรายการที่เชื่อมโยงสามารถผสานได้ในสถานที่ใน java 7 จะทิ้งไปยังอาร์เรย์ภายนอกแล้วเรียงลำดับ
ratchet freak

5
ฉันพบบทความนี้ที่ Goetz อธิบายถึงปัญหา ดังนั้นฉันจะทำเครื่องหมายคำตอบนี้เป็นวิธีแก้ปัญหาสำหรับตอนนี้
มิสเตอร์สมิ ธ

1
@RobertHarvey: สร้างรายการ <Byte> สองร้อยล้านรายการใช้IEnumerable<Byte>.Appendเพื่อเข้าร่วมพวกเขาแล้วโทรCountจากนั้นบอกวิธีแก้ปัญหาส่วนขยายให้ฉันทราบได้อย่างไร ถ้าCountIsKnownและCountเป็นสมาชิกIEnumerable<T>แล้วผลตอบแทนจากการAppendโฆษณาก็สามารถโฆษณาได้CountIsKnownหากคอลเลกชันที่เป็นส่วนประกอบทำ แต่ไม่มีวิธีดังกล่าวที่เป็นไปไม่ได้
supercat

6
@supercat: ฉันไม่ได้มีความคิดแม้แต่น้อยสิ่งที่คุณกำลังพูดถึง
Robert Harvey

48

คำตอบที่ถูกต้องเป็นจริงพบในเอกสาร Javaซึ่งระบุ:

[d] วิธีการ efault ช่วยให้คุณสามารถเพิ่มฟังก์ชันการทำงานใหม่ให้กับส่วนต่อประสานของไลบรารีของคุณและตรวจสอบให้แน่ใจถึงความเข้ากันได้ของไบนารีด้วยรหัสที่เขียนขึ้นสำหรับส่วนต่อประสานรุ่นเก่าเหล่านั้น

นี่เป็นแหล่งที่มาของความเจ็บปวดมายาวนานใน Java เนื่องจากส่วนต่อประสานมีแนวโน้มที่จะเป็นไปไม่ได้ที่จะวิวัฒนาการเมื่อมีการเปิดเผยต่อสาธารณะ (เนื้อหาในเอกสารประกอบนั้นเกี่ยวข้องกับกระดาษที่คุณเชื่อมโยงในความคิดเห็น: วิวัฒนาการส่วนต่อประสานผ่านวิธีการขยายเสมือน ) นอกจากนี้การใช้คุณสมบัติใหม่อย่างรวดเร็ว (เช่น lambdas และ stream API ใหม่) สามารถทำได้โดยการขยาย อินเตอร์เฟสคอลเล็กชันที่มีอยู่และจัดเตรียมการประยุกต์ใช้ดีฟอลต์ การทำลายความเข้ากันได้ของไบนารีหรือการแนะนำ API ใหม่หมายความว่าหลายปีผ่านไปก่อนที่คุณสมบัติที่สำคัญที่สุดของ Java 8 จะเป็นการใช้งานทั่วไป

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

เมื่อทราบเหมือนกันตัวอย่างของวิธีการคุณสมบัติใหม่เหล่านี้สามารถเป็นประโยชน์คือการพิจารณาระดับหนึ่งที่ได้รำคาญเมื่อเร็ว ๆ java.util.UUIDนี้ผม มันไม่ได้ให้การสนับสนุนUUID ประเภท 1, 2 หรือ 5 และไม่สามารถแก้ไขได้อย่างง่ายดาย มันยังติดอยู่กับตัวสร้างแบบสุ่มที่กำหนดไว้ล่วงหน้าซึ่งไม่สามารถเขียนทับได้ การติดตั้งโค้ดสำหรับประเภท UUID ที่ไม่รองรับต้องอาศัยการพึ่งพาโดยตรงกับ API ของบุคคลที่สามแทนส่วนต่อประสานหรือการบำรุงรักษารหัสการแปลงและค่าใช้จ่ายในการรวบรวมขยะเพิ่มเติม ด้วยวิธีการคงที่UUIDอาจได้รับการกำหนดให้เป็นอินเตอร์เฟซแทนช่วยให้การใช้งานบุคคลที่สามจริงของชิ้นส่วนที่ขาดหายไป (ถ้าUUIDเดิมถูกกำหนดเป็นอินเทอร์เฟซเราอาจมี clunky อยู่บ้างUuidUtil ชั้นเรียนที่มีวิธีการแบบคงที่ซึ่งจะน่ากลัวเกินไป) API หลักของ Java จำนวนมากจะลดลงโดยไม่สามารถเชื่อมต่อกับอินเตอร์เฟส แต่ Java 8 จำนวนข้อแก้ตัวสำหรับพฤติกรรมที่ไม่ดีนี้ได้ลดลงอย่างมาก

มันไม่ถูกต้องที่จะบอกว่า[t] ที่นี่แทบไม่มีความแตกต่างระหว่างอินเทอร์เฟซและคลาสนามธรรมเนื่องจากคลาสนามธรรมสามารถมีสถานะ (นั่นคือประกาศฟิลด์) ในขณะที่อินเทอร์เฟซไม่สามารถ ดังนั้นจึงไม่เทียบเท่ามรดกหลายรายการหรือแม้กระทั่งการสืบทอดสไตล์มิกซ์อิน mixins ที่เหมาะสม (เช่นลักษณะของ Groovy 2.3 ) สามารถเข้าถึงสถานะได้ (Groovy รองรับวิธีการขยายแบบคงที่)

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


1
สำหรับการต่อไปติดตามในย่อหน้าสุดท้ายของฉัน (คือไม่บังคับใช้สัญญาในอินเตอร์เฟซ ) ก็คุ้มค่าที่ชี้ให้เห็นว่าdefaultวิธีการที่ไม่สามารถแทนที่equals, และhashCode toStringการวิเคราะห์ค่าใช้จ่าย / ผลประโยชน์ที่เป็นประโยชน์อย่างมากเกี่ยวกับสาเหตุที่ทำให้ไม่สามารถพบได้ที่นี่: mail.openjdk.java.net/pipermail/lambda-dev/2013-March/ ......
ngreen

มันแย่มากที่ Java มีเพียง virtual virtual equalsและ single hashCodemethod เนื่องจากมีความเท่าเทียมกันสองแบบที่คอลเลกชันอาจต้องทดสอบและรายการที่จะใช้หลายอินเตอร์เฟสอาจติดอยู่กับข้อกำหนดทางสัญญาที่ขัดแย้งกัน ความสามารถในการใช้รายการที่จะไม่เปลี่ยนแปลงเนื่องจากhashMapคีย์มีประโยชน์ แต่ก็มีบางครั้งที่จะมีประโยชน์ในการจัดเก็บคอลเลกชันในสิ่งhashMapที่จับคู่กันโดยยึดตามความเสมอภาคมากกว่าสถานะปัจจุบัน [ความเทียบเท่าหมายถึงสถานะที่ตรงกันและไม่เปลี่ยนแปลง ] .
supercat

การเรียงลำดับของ Java มีวิธีแก้ปัญหาด้วยอินเทอร์เฟซ Comparator และ Comparable แต่ฉันคิดว่ามันน่าเกลียด
ngreen

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

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

44

เพราะคุณสามารถสืบทอดชั้นหนึ่งเท่านั้น หากคุณมีอินเทอร์เฟซสองตัวที่มีการใช้งานที่ซับซ้อนพอที่คุณต้องการคลาสฐานนามธรรมทั้งสองอินเทอร์เฟซเหล่านั้นเป็นพิเศษในทางปฏิบัติร่วมกัน

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


เป็นตัวอย่างที่สร้างแรงจูงใจว่าทำไมการให้การใช้งานในอินเทอร์เฟซมีประโยชน์ให้พิจารณาอินเทอร์เฟซกองซ้อนนี้:

public interface Stack<T> {
    boolean isEmpty();

    T pop() throws EmptyException;
 }

ไม่มีทางที่จะรับประกันได้ว่าเมื่อมีคนใช้อินเทอร์เฟซpopจะโยนข้อยกเว้นถ้าสแต็กว่างเปล่า เราสามารถบังคับใช้กฎนี้ได้โดยแยกออกpopเป็นสองวิธี: public finalวิธีการบังคับใช้สัญญาและprotected abstractวิธีที่ดำเนินการ popping จริง

public abstract class Stack<T> {
    public abstract boolean isEmpty();

    protected abstract T pop_implementation();

    public final T pop() throws EmptyException {
        if (isEmpty()) {
            throw new EmptyException();
        else {
            return pop_implementation();
        }
    }
 }

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

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

สำหรับการใช้งานเริ่มต้นของวิธีการ overridable ฉันถือว่าการใช้งานเริ่มต้นใด ๆ ที่เพิ่มไปยัง Java APIs ขึ้นอยู่กับสัญญาของอินเตอร์เฟสที่เพิ่มเข้าไปดังนั้นคลาสใด ๆ ที่ใช้อินเทอร์เฟซที่ถูกต้องจะทำงานได้อย่างถูกต้อง

ยิ่งไปกว่านั้น

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

สิ่งนี้ไม่เป็นความจริงเนื่องจากคุณไม่สามารถประกาศเขตข้อมูลในอินเทอร์เฟซ วิธีการใด ๆ ที่คุณเขียนในอินเทอร์เฟซไม่สามารถพึ่งพารายละเอียดการใช้งานใด ๆ


เป็นตัวอย่างสำหรับวิธีคงที่ในอินเตอร์เฟสพิจารณาคลาสยูทิลิตี้เช่นCollectionsใน Java API คลาสนั้นมีอยู่เพียงเพราะวิธีการคงที่เหล่านั้นไม่สามารถประกาศในอินเทอร์เฟซที่เกี่ยวข้องของพวกเขา Collections.unmodifiableListสามารถประกาศได้เช่นกันในListอินเทอร์เฟซและจะหาได้ง่ายกว่า


4
เคาน์เตอร์ - โต้แย้ง: เนื่องจากวิธีการคงที่ถ้าเขียนอย่างถูกต้องมีอยู่ในตัวเองพวกเขามีเหตุผลมากขึ้นในระดับคงที่แยกที่พวกเขาสามารถรวบรวมและจัดหมวดหมู่ตามชื่อชั้นเรียนและความรู้สึกน้อยลงในอินเทอร์เฟซ ที่เชิญการละเมิดเช่นการถือครองสถานะคงที่ในวัตถุหรือก่อให้เกิดผลข้างเคียงทำให้วิธีการคงที่ไม่สามารถทดสอบได้
Robert Harvey

3
@RobertHarvey มีอะไรที่จะห้ามไม่ให้คุณทำสิ่งที่โง่เขลาอย่างเท่าเทียมกันถ้าวิธีการคงที่ของคุณอยู่ในชั้นเรียน นอกจากนี้วิธีการในอินเทอร์เฟซอาจไม่ต้องการสถานะใด ๆ เลย คุณอาจจะพยายามบังคับใช้สัญญา สมมติว่าคุณมีStackอินเทอร์เฟซและคุณต้องการให้แน่ใจว่าเมื่อpopถูกเรียกพร้อมกับสแต็กเปล่าข้อยกเว้นจะถูกส่งออกไป กำหนดวิธีการเชิงนามธรรมboolean isEmpty()และprotected T pop_impl()คุณสามารถใช้final T pop() { isEmpty()) throw PopException(); else return pop_impl(); }สิ่งนี้บังคับให้สัญญากับผู้ดำเนินการทั้งหมด
Doval

รออะไร? วิธี Push และ Pop บนสแต็กจะไม่เป็นstaticเช่นนั้น
Robert Harvey

@RobertHarvey ฉันจะชัดเจนถ้าไม่ใช่สำหรับการ จำกัด ตัวอักษรในความคิดเห็น แต่ฉันทำกรณีสำหรับการใช้งานเริ่มต้นในอินเทอร์เฟซไม่ใช่วิธีการคงที่
Doval

8
ฉันคิดว่าวิธีการอินเตอร์เฟซเริ่มต้นค่อนข้างแฮ็คที่ได้รับการแนะนำให้สามารถขยายไลบรารีมาตรฐานโดยไม่จำเป็นต้องปรับเปลี่ยนรหัสที่มีอยู่ตามนั้น
Giorgio

2

บางทีความตั้งใจที่จะให้ความสามารถในการสร้างคลาสมิกซ์อินโดยการแทนที่ความต้องการในการฉีดข้อมูลคงที่หรือฟังก์ชั่นผ่านการพึ่งพา

ดูเหมือนว่าความคิดนี้จะเกี่ยวข้องกับวิธีการที่คุณสามารถใช้วิธีการขยายใน C # เพื่อเพิ่มฟังก์ชันการใช้งานให้กับอินเตอร์เฟส


1
วิธีการขยายไม่ได้เพิ่มฟังก์ชันการทำงานให้กับส่วนต่อประสาน วิธีการขยายเป็นเพียงน้ำตาลประโยคสำหรับการเรียกวิธีการคงที่ในชั้นเรียนโดยใช้list.sort(ordering);แบบฟอร์มที่สะดวก
Robert Harvey

ถ้าคุณดูที่IEnumerableอินเตอร์เฟสใน C # คุณสามารถดูว่าการใช้วิธีการขยายไปยังอินเทอร์เฟซนั้น (เช่นLINQ to Objectsนั้น) เพิ่มฟังก์ชันการทำงานสำหรับทุกคลาสที่ใช้งานIEnumerableได้อย่างไร นั่นคือสิ่งที่ฉันหมายถึงโดยการเพิ่มฟังก์ชั่น
rae1

2
นั่นเป็นสิ่งที่ยอดเยี่ยมเกี่ยวกับวิธีการขยาย พวกมันให้ภาพลวงตาที่ว่าคุณกำลังใช้งานกับคลาสหรืออินเทอร์เฟซ อย่าเพิ่งสับสนว่าด้วยการเพิ่มวิธีการที่เกิดขึ้นจริงในชั้นเรียน; วิธีการเรียนมีการเข้าถึงสมาชิกส่วนตัวของวัตถุวิธีการขยายไม่ (เพราะพวกเขาเป็นเพียงวิธีการโทรวิธีคงที่)
Robert Harvey

2
ตรงและนั่นเป็นเหตุผลที่ฉันเห็นความสัมพันธ์บางอย่างเกี่ยวกับวิธีการแบบคงที่หรือเป็นค่าเริ่มต้นในส่วนต่อประสานใน Java การนำไปใช้นั้นขึ้นอยู่กับสิ่งที่มีอยู่ในอินเตอร์เฟสไม่ใช่คลาสเอง
rae1

1

วัตถุประสงค์หลักสองประการที่ฉันเห็นในdefaultวิธีการ (บางกรณีใช้เพื่อวัตถุประสงค์ทั้งสอง):

  1. น้ำตาลทราย คลาสยูทิลิตี้สามารถตอบสนองวัตถุประสงค์นั้นได้ แต่วิธีการอินสแตนซ์นั้นดีกว่า
  2. ส่วนต่อขยายของอินเทอร์เฟซที่มีอยู่ การนำไปใช้นั้นเป็นเรื่องทั่วไป แต่บางครั้งก็ไร้ประสิทธิภาพ

Predicateถ้ามันเป็นเพียงเกี่ยวกับวัตถุประสงค์ที่สองคุณจะไม่เห็นว่าในอินเตอร์เฟซใหม่ยี่ห้อเช่น @FunctionalInterfaceอินเทอร์เฟซที่มีคำอธิบายประกอบทั้งหมดจะต้องมีวิธีนามธรรมหนึ่งวิธีเพื่อให้แลมบ์ดาสามารถนำไปใช้ได้ เพิ่มdefaultวิธีการเช่นand, or, negateเป็นเพียงยูทิลิตี้และคุณไม่ควรจะแทนที่พวกเขา แต่บางครั้งวิธีการแบบคงที่จะทำดี

สำหรับส่วนต่อขยายของอินเทอร์เฟซที่มีอยู่ - แม้จะมีวิธีการใหม่บางอย่างเป็นเพียงไวยากรณ์น้ำตาล วิธีการCollectionเช่นstream, forEach, removeIf- พื้นก็แค่ยูทิลิตี้ที่คุณไม่จำเป็นที่จะแทนที่ spliteratorแล้วมีวิธีการเช่น การใช้งานเริ่มต้นเป็นสิ่งที่ไม่ดี แต่อย่างน้อยก็คอมไพล์โค้ด ใช้วิธีนี้เฉพาะเมื่ออินเทอร์เฟซของคุณเผยแพร่ไปแล้วและใช้กันอย่างแพร่หลาย


สำหรับstaticวิธีการที่ฉันเดาคนอื่น ๆ ครอบคลุมค่อนข้างดี: มันช่วยให้อินเตอร์เฟซที่จะเป็นชั้นยูทิลิตี้ของตัวเอง บางทีเราสามารถกำจัดได้Collectionsในอนาคตของ Java? Set.empty()จะโยก

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