วิธีการเริ่มต้น Java 8 เป็นลักษณะ: ปลอดภัยหรือไม่


116

วิธีปฏิบัติที่ปลอดภัยในการใช้ วิธีการเริ่มต้นเป็นลักษณะเฉพาะของคนยากจนใน Java 8 หรือไม่?

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

ฉันมีกรณีการใช้งานจริงดังต่อไปนี้ :

public interface Loggable {
    default Logger logger() {
        return LoggerFactory.getLogger(this.getClass());
    }
}

หรือบางทีกำหนดPeriodTrait:

public interface PeriodeTrait {
    Date getStartDate();
    Date getEndDate();
    default isValid(Date atDate) {
        ...
    }
}

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

ดังนั้นการใช้วิธีการเริ่มต้นเป็นลักษณะพื้นฐานเป็นเรื่องปกติหรือไม่ปกติหรือไม่หรือฉันควรกังวลเกี่ยวกับผลข้างเคียงที่คาดไม่ถึง?

หลายคำถามเกี่ยวกับ SO เกี่ยวข้องกับลักษณะ Java และ Scala นั่นไม่ใช่ประเด็นที่นี่ ฉันไม่ได้ขอแค่ความคิดเห็นเช่นกัน แต่ฉันกำลังมองหาคำตอบที่เชื่อถือได้หรืออย่างน้อยก็ให้ข้อมูลเชิงลึก: หากคุณใช้วิธีการเริ่มต้นเป็นลักษณะในโครงการขององค์กรของคุณมันกลายเป็นระเบิดเวลาหรือไม่?


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

1
ฉันเห็นด้วยกับ @ infosec812 เกี่ยวกับการขยายคลาสนามธรรมที่กำหนดฟิลด์คนตัดไม้แบบคงที่ของตัวเอง วิธีการของคุณจะไม่สร้างอินสแตนซ์คนตัดไม้ () อินสแตนซ์ใหม่ทุกครั้งที่เรียก
Eidan Spiegel

สำหรับการบันทึกคุณอาจต้องการดู Projectlombok.org และคำอธิบายประกอบ @ Slf4j
Deven Phillips

คำตอบ:


120

คำตอบสั้น ๆ คือปลอดภัยถ้าใช้อย่างปลอดภัย :)

คำตอบที่น่ากลัว: บอกฉันว่าคุณหมายถึงอะไรจากลักษณะและบางทีฉันอาจจะให้คำตอบที่ดีกว่า :)

ในความร้ายแรงคำว่า "ลักษณะ" ไม่ได้กำหนดไว้อย่างชัดเจน นักพัฒนา Java หลายคนคุ้นเคยกับลักษณะที่แสดงออกมาใน Scala มากที่สุด แต่ Scala ยังห่างไกลจากภาษาแรกที่มีลักษณะไม่ว่าจะในชื่อหรือผล

ตัวอย่างเช่นใน Scala ลักษณะจะเป็นสถานะ (สามารถมีvarตัวแปรได้) ในป้อมปราการพวกเขาเป็นพฤติกรรมที่บริสุทธิ์ อินเทอร์เฟซของ Java กับเมธอดดีฟอลต์ไม่มีสถานะ นี่หมายความว่าพวกเขาไม่ใช่ลักษณะ? (คำแนะนำ: นั่นเป็นคำถามหลอกลวง)

อีกครั้งใน Scala ลักษณะจะประกอบขึ้นจากการทำให้เป็นเส้นตรง ถ้าคลาสAขยายลักษณะXและYลำดับที่XและYผสมจะกำหนดว่าความขัดแย้งระหว่างXและอย่างไรYได้รับการแก้ไข ใน Java ไม่มีกลไกเชิงเส้นตรงนี้ (บางส่วนถูกปฏิเสธเนื่องจาก "ไม่เหมือน Java" เกินไป)

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

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

นี่คือกรณีการใช้งานบางส่วนที่อยู่ในเป้าหมายการออกแบบ:

  • วิวัฒนาการของอินเทอร์เฟซ ที่นี่เรากำลังเพิ่มวิธีการใหม่ให้กับอินเทอร์เฟซที่มีอยู่ซึ่งมีการใช้งานเริ่มต้นที่สมเหตุสมผลในแง่ของวิธีการที่มีอยู่บนอินเทอร์เฟซนั้น ตัวอย่างจะเป็นการเพิ่มforEachวิธีการCollectionโดยที่การใช้งานเริ่มต้นถูกเขียนในรูปแบบของiterator()วิธีการ

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

  • วิธีการอำนวยความสะดวก วิธีเหล่านี้เป็นวิธีการที่เคร่งครัดเพื่อความสะดวกโดยทั่วไปจะใช้อีกครั้งในแง่ของวิธีการที่ไม่ใช่ค่าเริ่มต้นในคลาส logger()วิธีการในตัวอย่างแรกของคุณเป็นภาพที่เหมาะสมนี้

  • combinators นี่คือวิธีการจัดองค์ประกอบที่สร้างอินสแตนซ์อินเทอร์เฟซใหม่ตามอินสแตนซ์ปัจจุบัน ตัวอย่างเช่นวิธีการPredicate.and()หรือComparator.thenComparing()ตัวอย่างของตัวผสม

หากคุณจัดเตรียมการใช้งานเริ่มต้นคุณควรระบุข้อกำหนดบางอย่างสำหรับค่าเริ่มต้น (ใน JDK เราใช้@implSpecแท็ก javadoc สำหรับสิ่งนี้) เพื่อช่วยผู้ใช้ในการทำความเข้าใจว่าพวกเขาต้องการแทนที่เมธอดหรือไม่ ค่าเริ่มต้นบางอย่างเช่นวิธีการอำนวยความสะดวกและตัวรวมพลังแทบจะไม่ถูกแทนที่ อื่น ๆ เช่นวิธีการทางเลือกมักจะถูกแทนที่ คุณต้องระบุข้อมูลจำเพาะที่เพียงพอ (ไม่ใช่แค่เอกสารประกอบ) เกี่ยวกับสิ่งที่สัญญาเริ่มต้นที่จะทำดังนั้นผู้ดำเนินการจึงสามารถตัดสินใจได้อย่างสมเหตุสมผลว่าพวกเขาจำเป็นต้องลบล้างสิ่งนั้นหรือไม่


9
ขอบคุณ Brian สำหรับคำตอบที่ครอบคลุมนี้ ตอนนี้ฉันสามารถใช้วิธีเริ่มต้นด้วยใจที่เบา ผู้อ่าน: ข้อมูลเพิ่มเติมจาก Brian Goetz เกี่ยวกับวิวัฒนาการของอินเทอร์เฟซและวิธีการเริ่มต้นสามารถพบได้ในNightHacking Worldwide Lambdasเช่น
youri

ขอบคุณ @ brian-goetz จากสิ่งที่คุณได้กล่าวมาฉันคิดว่าวิธีการเริ่มต้นของ Java นั้นใกล้เคียงกับแนวคิดของลักษณะดั้งเดิมตามที่กำหนดไว้ในกระดาษ Ducasse et al ( scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf ) "ลักษณะ" ของ Scala ดูเหมือนจะไม่เป็นลักษณะสำหรับฉันเลยเนื่องจากมีสถานะใช้การทำให้เป็นเส้นตรงในองค์ประกอบของพวกเขาและดูเหมือนว่าพวกเขาจะแก้ไขความขัดแย้งของวิธีการโดยปริยายด้วย - และสิ่งเหล่านี้เป็นสิ่งที่ลักษณะดั้งเดิมไม่มี มี. อันที่จริงฉันจะบอกว่าลักษณะของ Scala นั้นเหมือนกับการผสมผสานมากกว่าลักษณะ คุณคิดอย่างไร? PS: ฉันไม่เคยเขียนโค้ดใน Scala
adino

วิธีการใช้การติดตั้งเริ่มต้นว่างสำหรับวิธีการในอินเทอร์เฟซที่ทำหน้าที่เป็นผู้ฟัง การใช้งาน Listener อาจสนใจที่จะฟังวิธีการอินเทอร์เฟซเพียงไม่กี่วิธีเท่านั้นดังนั้นการกำหนดวิธีการเป็นค่าเริ่มต้นผู้ใช้จะต้องใช้วิธีการที่จำเป็นในการฟังเท่านั้น
Lahiru Chandima

1
@LahiruChandima Like ด้วยMouseListener? รูปแบบ API นี้เหมาะสมกับที่เก็บข้อมูล "วิธีการทางเลือก" ในระดับที่เหมาะสม อย่าลืมจัดทำเอกสารทางเลือกให้ชัดเจน!
Brian Goetz

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