การใช้วิธีการ "เริ่มต้น" ที่กำหนดไว้ในอินเทอร์เฟซคืออะไร?


91

ในอินเทอร์เฟซการรวบรวมฉันพบเมธอดชื่อหนึ่งremoveIf()ที่มีการนำไปใช้งาน

default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);  
    boolean removed = false;  
    final Iterator<E> each = iterator();   
    while (each.hasNext()) {  
        if (filter.test(each.next())) {  
            each.remove();  
            removed = true;  
        }  
    }  
    return removed;  
}  

ฉันต้องการทราบว่ามีวิธีใดในการกำหนด method body ในอินเทอร์เฟซหรือไม่? คีย์เวิร์ด
คืออะไรdefaultและทำงานอย่างไร?


3
ดูโพสต์นี้เกี่ยวกับzeroturnaround.com/rebellabs/java-8-explained-default-methods/…
emeraldjava

โพสต์ที่เกี่ยวข้องstackoverflow.com/questions/31578427/…
ราวี

คำตอบ:


162

จากhttps://dzone.com/articles/interface-default-methods-java

Java 8 แนะนำคุณลักษณะใหม่“ Default Method” หรือ (Defender method) ซึ่งช่วยให้นักพัฒนาสามารถเพิ่มวิธีการใหม่ ๆ ให้กับอินเทอร์เฟซได้โดยไม่ทำลายการใช้งานอินเทอร์เฟซที่มีอยู่เดิม ให้ความยืดหยุ่นในการอนุญาตให้ใช้อินเทอร์เฟซกำหนดการใช้งานซึ่งจะใช้เป็นค่าเริ่มต้นในสถานการณ์ที่คลาสคอนกรีตไม่สามารถจัดเตรียมการใช้งานสำหรับวิธีการนั้นได้

public interface A {
    default void foo(){
       System.out.println("Calling A.foo()");
    }
}

public class ClassAB implements A {
}

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

จะเกิดอะไรขึ้นถ้าคลาสใช้สองอินเทอร์เฟซและทั้งสองอินเทอร์เฟซเหล่านั้นกำหนดวิธีการเริ่มต้นด้วยลายเซ็นเดียวกัน

ตัวอย่างเพื่อแสดงสถานการณ์นี้:

public interface A {  
    default void foo(){  
        System.out.println("Calling A.foo()");  
    }  
}

public interface B {
    default void foo(){
        System.out.println("Calling B.foo()");
    }
}


public class ClassAB implements A, B {

}  

รหัสนี้ไม่สามารถรวบรวมด้วยผลลัพธ์ต่อไปนี้:

java: class Clazz inherits unrelated defaults for foo() from types A and B

ในการแก้ไขใน Clazz เราต้องแก้ไขด้วยตนเองโดยการลบล้างวิธีการที่ขัดแย้งกัน:

public class Clazz implements A, B {
    public void foo(){}
}

แต่จะเกิดอะไรขึ้นถ้าเราต้องการเรียกการใช้งาน method foo () เริ่มต้นจากอินเทอร์เฟซ A แทนการใช้งานของเราเอง

สามารถอ้างถึง A # foo () ได้ดังนี้:

public class Clazz implements A, B {
    public void foo(){
       A.super.foo();
    }
}

18
ขอบคุณประสบการณ์ที่ดีจริงๆ คุณตอบคำถามของฉันทั้งหมดก่อนที่ฉันจะมีโอกาสถามพวกเขา
Jeff Hutchins

ทำไมไม่ใช้นามธรรมแทนล่ะ
Astolfo Hoscher

1
@AstolfoHoscher คุณสามารถขยายคลาสได้เพียงคลาสเดียว แต่คุณสามารถใช้อินเทอร์เฟซหลาย ๆ
Charles Wood

49

วิธีการเหล่านั้นเรียกว่าวิธีการเริ่มต้น Default methodหรือDefender methodเป็นหนึ่งในคุณสมบัติที่เพิ่มเข้ามาใหม่ใน Java 8

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

ดังนั้นหากคุณมีอินเทอร์เฟซด้วยวิธีการเริ่มต้น:

public interface Hello {
    default void sayHello() {
        System.out.println("Hello");
    }
}

คลาสต่อไปนี้ใช้ได้อย่างสมบูรณ์แบบ:

public class HelloImpl implements Hello {

}

หากคุณสร้างอินสแตนซ์ของHelloImpl:

Hello hello = new HelloImpl();
hello.sayHello();  // This will invoke the default method in interface

ลิงค์ที่มีประโยชน์:


มันก็โอเคถ้าคลาสใช้อินเทอร์เฟซและไม่ใช้เมธอดนั้น? เท่าที่เกี่ยวข้องกับ Java7 ซึ่งฉันใช้สิ่งนี้ไม่ได้รับอนุญาต
Aniket Thakur

2
@AniketThakur. ไม่อนุญาตก่อน Java 8 คุณลักษณะนี้เพิ่มใน Java 8 เท่านั้น คุณสามารถหลีกเลี่ยงการใช้วิธีการเริ่มต้นในคลาสการนำไปใช้ของคุณได้
Rohit Jain

1
@PawanMishra. ดูความคิดเห็นก่อนหน้าของฉัน ไม่คุณไม่จำเป็นต้องจัดเตรียมวิธีการใช้งานอินเทอร์เฟซเริ่มต้นในการใช้คลาส
Rohit Jain

1
@PawanMishra คุณสามารถแทนที่มันได้ ไม่มีข้อ จำกัด เช่นคุณต้องใช้การใช้งานเริ่มต้นเท่านั้น
Aniket Thakur

4
ก้าวต่อไปที่จะหลีกเลี่ยงการงงงวยกับการสืบทอดหลายรายการในที่สุด!
Xtreme Biker

17

ฉันได้ทำการค้นคว้าและพบสิ่งต่อไปนี้ หวังว่านี่จะช่วยได้

ปัญหาที่มีอยู่

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

โซลูชันที่นำมาใช้ใน Java 8

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

ประเด็นสำคัญที่ควรทราบ

  1. ผู้ใช้สามารถเลือกที่จะไม่ใช้วิธีการเริ่มต้นในการใช้คลาส
  2. ผู้ใช้งานยังคงสามารถลบล้างเมธอดเริ่มต้นได้เช่นเดียวกับเมธอดคลาสที่ไม่ใช่คลาสสุดท้ายทั่วไปสามารถถูกแทนที่ในคลาสย่อยได้
  3. คลาสนามธรรมสามารถ (อีกครั้ง) ประกาศเมธอดเริ่มต้นเป็นนามธรรมบังคับให้คลาสย่อยนำวิธีการนี้ไปใช้ใหม่ (บางครั้งเรียกว่า 're-abstract')
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.