ฉันจะอธิบายความแตกต่างระหว่างอินเทอร์เฟซกับคลาสนามธรรมได้อย่างไร


469

ในตอนหนึ่งของการสัมภาษณ์ของฉันฉันได้รับการถามเพื่ออธิบายความแตกต่างระหว่างนั้นอินเตอร์เฟซและระดับนามธรรม

นี่คือคำตอบของฉัน:

วิธีการของอินเทอร์เฟซ Java เป็นนามธรรมโดยปริยายและไม่สามารถใช้งานได้ คลาสนามธรรม Java สามารถมีวิธีการอินสแตนซ์ที่ดำเนินพฤติกรรมเริ่มต้น

ตัวแปรที่ประกาศในอินเตอร์เฟส Java นั้นเป็นค่าเริ่มต้นสุดท้าย คลาสนามธรรมอาจมีตัวแปรที่ไม่สิ้นสุด

สมาชิกของส่วนต่อประสาน Java เป็นสาธารณะโดยค่าเริ่มต้น คลาสนามธรรมของ Java สามารถมีสมาชิกคลาสได้ตามปกติเช่นส่วนตัวมีการป้องกัน ฯลฯ

อินเทอร์เฟซ Java ควรใช้งานโดยใช้คำหลัก“ ดำเนินการ”; คลาสนามธรรมของ Java ควรถูกขยายโดยใช้คีย์เวิร์ด“ extends”

อินเตอร์เฟสสามารถขยายอินเตอร์เฟส Java อื่นได้เท่านั้นคลาสนามธรรมสามารถขยายคลาส Java อื่นและใช้อินเตอร์เฟส Java หลายอินเตอร์เฟส

คลาส Java สามารถใช้หลายอินเตอร์เฟส แต่สามารถขยายคลาสนามธรรมเพียงคลาสเดียวเท่านั้น

อย่างไรก็ตามผู้สัมภาษณ์ไม่พอใจและบอกฉันว่าคำอธิบายนี้เป็นตัวแทนของ " ความรู้เกี่ยวกับหนอนหนังสือ "

เขาถามฉันสำหรับการตอบสนองการปฏิบัติมากขึ้นอธิบายเมื่อฉันจะเลือกระดับนามธรรมมากกว่าอินเตอร์เฟซที่ใช้ตัวอย่างการปฏิบัติ

ฉันไปผิดที่ไหน


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

19
คุณตอบด้วยรายการความแตกต่างด้านเทคนิค (ค่อนข้างถูกต้อง) ผู้สัมภาษณ์มักมองหาคำตอบที่เป็นแนวคิดมากกว่า (เช่นบนพื้นฐานที่ว่าใครจะเลือกระหว่างการใช้อินเทอร์เฟซกับคลาสนามธรรม)
Ted Hopp

15
คุณลืมที่จะบอกว่าคลาสนามธรรมมีคอนสตรัคเตอร์ถึงแม้ว่าคุณจะไม่สามารถยกตัวอย่างคลาสนามธรรมได้ ถูกใช้โดยคลาสย่อย อินเทอร์เฟซระบุ "อะไร" แต่ไม่ใช่ "อย่างไร" เนื่องจากกำหนดสัญญา (รายการวิธีการ) ในขณะที่ abst ชั้นยังสามารถระบุ "วิธี" (ใช้ปรุง) การใช้ int คุณสามารถจำลองหลายมรดก (ชั้นสามารถใช้หลาย int แต่ขยายชั้นเดียวเท่านั้น) การใช้ int คุณสามารถมีประเภทฐานสำหรับ dif ครอบครัว: Flyer f = เครื่องบินใหม่ (); Flyer f2 = นกใหม่ (); นกและเครื่องบินไม่สอดคล้องกับตระกูลเดียวกัน แต่ทั้งคู่สามารถบินได้ (เป็นใบปลิว)
Francisco Goldenstein

7
ในฐานะของส่วนต่อประสาน java8 สามารถมีวิธีการ .. ดังนั้นเกินกว่าแนวคิด OO เหล่านี้เรียกว่า "ความแตกต่าง" สามารถเปลี่ยนแปลงได้ทุกวัน
ผู้ถือแหวน

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

คำตอบ:


513

ฉันจะให้ตัวอย่างก่อน:

public interface LoginAuth{
   public String encryptPassword(String pass);
   public void checkDBforUser();
}

สมมติว่าคุณมี 3 ฐานข้อมูลในแอปพลิเคชันของคุณ จากนั้นแต่ละการดำเนินการสำหรับฐานข้อมูลนั้นจำเป็นต้องกำหนดวิธีการ 2 ข้างต้น:

public class DBMySQL implements LoginAuth{
          // Needs to implement both methods
}
public class DBOracle implements LoginAuth{
          // Needs to implement both methods
}
public class DBAbc implements LoginAuth{
          // Needs to implement both methods
}

แต่ถ้าencryptPassword()ไม่ใช่ฐานข้อมูลขึ้นอยู่กับว่ามันจะเหมือนกันสำหรับแต่ละคลาสหรือไม่? จากนั้นจะไม่เป็นวิธีที่ดี

ให้พิจารณาแนวทางนี้แทน:

public abstract class LoginAuth{
   public String encryptPassword(String pass){
            // Implement the same default behavior here 
            // that is shared by all subclasses.
   }

   // Each subclass needs to provide their own implementation of this only:
   public abstract void checkDBforUser();
}

ตอนนี้ในแต่ละคลาสลูกเราต้องใช้วิธีเดียวเท่านั้น - วิธีที่ขึ้นอยู่กับฐานข้อมูล


97
ฉันไม่แน่ใจว่านี่จะอธิบายความแตกต่างจริง ๆ ... แน่นอนว่ามันเป็นเทคนิคที่ดี ฉันคิดว่ามันก็คุ้มค่าที่ชี้ให้เห็นว่าในที่สุด Java 8 ได้ยอมรับว่า C ++ นั้นถูกต้องและสามารถทำการสืบทอดหลาย ๆ อย่างและสามารถใช้งานได้ดังนั้นอินเทอร์เฟซจึงไม่เพียง แต่กำหนดลายเซ็นของฟังก์ชั่นเท่านั้น ดังนั้นการใช้อินเตอร์เฟสจะดีกว่า
thecoshman

1
@thecoshman จะมีความแตกต่างอะไรถ้าฉันเข้าหาปัญหาดังที่คำตอบ (คลาสนามธรรมที่มีวิธีการหนึ่งที่นำมาใช้และนามธรรมอื่น ๆ ) หรือกำหนดอินเทอร์เฟซที่มีการใช้วิธีการเริ่มต้น? โดยทั่วไปสิ่งที่ฉันพยายามจะพูดคือคุณเขียนว่า 'ใช้ส่วนต่อประสานจะดีกว่า' และคำถามของฉันคือ - ทำไม
Neutrino

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

4
@Neutrino แม้จะมี Java ที่อนุญาตให้คุณใช้หลาย ๆ อินเทอร์เฟซแต่ละข้อเสนอการใช้งานเริ่มต้นสำหรับฟังก์ชั่นคุณยังสามารถขยายชั้นเดียว ดังนั้นการใช้อินเทอร์เฟซสามารถให้ความยืดหยุ่นมากขึ้นสำหรับผู้ที่ต้องการใช้พร้อมกับอินเตอร์เฟสอื่น ๆ
thecoshman

3
@ HiradNikoo ขออภัยที่มีความคิดเห็นล่าช้า แต่ฉันเพิ่งสะดุดในหัวข้อนี้ คุณสามารถคำนึงถึงการสืบทอดคลาสเป็นความสัมพันธ์ IS-A ในขณะที่อินเทอร์เฟซมีความหมายว่า "มีฟังก์ชั่นบางอย่าง"
Alexander Jank

206

ไม่มีอะไรสมบูรณ์แบบในโลกนี้ พวกเขาอาจคาดหวังแนวทางการปฏิบัติที่มากกว่านี้

แต่หลังจากคำอธิบายของคุณคุณสามารถเพิ่มบรรทัดเหล่านี้ด้วยวิธีที่แตกต่างกันเล็กน้อย

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

  2. อินเทอร์เฟซให้แนวคิดว่าจะต้องทำอะไร แต่จะทำไม่ได้ ดังนั้นการนำไปใช้อย่างสมบูรณ์ขึ้นอยู่กับผู้พัฒนาโดยทำตามกฎที่กำหนด

  3. คลาสที่เป็นนามธรรมอาจมีการประกาศที่เป็นนามธรรมการใช้งานที่เป็นรูปธรรมหรือทั้งสองอย่าง

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

  5. ยิ่งไปกว่านั้นวิธีการใดที่มีลายเซ็นเดียวกันอาจเปลี่ยนพฤติกรรมในบริบทที่แตกต่างกันไว้เป็นการประกาศอินเทอร์เฟซเป็นกฎเพื่อนำไปใช้ตามบริบทที่แตกต่างกัน

แก้ไข: Java 8 อำนวยความสะดวกในการกำหนดวิธีการเริ่มต้นและคงที่ในอินเตอร์เฟซ

public interface SomeInterfaceOne {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceOne defaultMethod::"+inputString);
    }
}

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

หากเรามีอินเทอร์เฟซอื่นด้วยวิธีการต่อไปนี้:

public interface SomeInterfaceTwo {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceTwo defaultMethod::"+inputString);
    }

}

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

SomeInterfaceOne and SomeInterfaceTwo

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


11
+1 นี่เป็นคำตอบที่ดีจริง ๆ เพื่อหลีกเลี่ยงความสับสน แต่ฉันไม่เห็นลิงก์ใด ๆ และไม่รู้เลยว่าทำไมคุณอ้างถึงบรรทัดที่มีค่าเหล่านั้น ทำให้เป็นจุดถ้าเป็นไปได้ :)
Suresh Atta

อ่านความคิดเห็นของฉันด้านบนเกี่ยวกับการจำลองการสืบทอดหลายรายการโดยใช้อินเตอร์เฟสและการใช้อินเตอร์เฟสสำหรับการมีประเภทฐานสำหรับคลาสของตระกูลที่แตกต่างกัน ฉันคิดว่าผู้สัมภาษณ์ต้องการได้ยินคำตอบแบบนั้นจากการสัมภาษณ์
Francisco Goldenstein

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

4. การใช้งานที่เป็นรูปธรรมเป็นกฎที่มีการใช้งานเริ่มต้น
Luten

@Luten: ตามความรู้ของฉันหากคุณสามารถหลีกเลี่ยง / เพิกเฉยกฎโดยไม่มีปัญหาใด ๆ นั่นจะต้องเป็นแนวทางไม่ใช่กฎ โปรดแก้ไขฉันหากฉันผิด
Shailesh Saxena

168

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

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

ระดับนามธรรมเป็นพื้นฐานสำหรับการย่อยที่แตกต่างกันว่าพฤติกรรมของหุ้นซึ่งไม่จำเป็นต้องได้รับการสร้างขึ้นซ้ำ ๆ คลาสย่อยต้องเสร็จสิ้นการทำงานและมีตัวเลือกเพื่อแทนที่พฤติกรรมที่กำหนดไว้ล่วงหน้า (ตราบใดที่ไม่ได้กำหนดไว้เป็นfinalหรือprivate)

คุณจะพบตัวอย่างที่ดีในjava.utilแพ็คเกจซึ่งรวมถึงอินเทอร์เฟซเช่นListและคลาสนามธรรมAbstractListซึ่งใช้อินเทอร์เฟซแล้ว เอกสารอย่างเป็นทางการอธิบายAbstractListดังต่อไปนี้:

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


16
นี่ควรเป็นคำตอบ ไม่ใช่รายการของรายละเอียด แต่เป็นแนวคิดพื้นฐานที่สร้างความแตกต่างระหว่างอินเตอร์เฟสและคลาสนามธรรมไม่เพียง แต่ใน Java แต่โดยทั่วไป
edc65

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

85

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

ตัวอย่างนี้สามารถเข้าใจแนวคิดได้ดีขึ้น:

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

public interface Payment
{
    void makePayment();//by default it is a abstract method
}
public class PayPal implements Payment
{
    public void makePayment()
    {
        //some logic for PayPal payment
        //e.g. Paypal uses username and password for payment
    }
}
public class CreditCard implements Payment
{
    public void makePayment()
    {
        //some logic for CreditCard payment
        //e.g. CreditCard uses card number, date of expiry etc...
    }
}

ในตัวอย่างข้างต้น CreditCard และ PayPal เป็นสองชั้นเรียน / กลยุทธ์การใช้งาน อินเทอร์เฟซยังช่วยให้เรามีแนวคิดของการสืบทอดหลาย ๆ อย่างใน Java ซึ่งไม่สามารถทำได้โดยคลาสนามธรรม

เราเลือกระดับนามธรรมเมื่อมีคุณสมบัติบางอย่างที่เรารู้ว่าสิ่งที่จะทำและคุณสมบัติอื่น ๆ ที่เรารู้วิธีการดำเนินการ

ลองพิจารณาตัวอย่างต่อไปนี้:

public abstract class Burger
{
    public void packing()
    {
        //some logic for packing a burger
    }
    public abstract void price(); //price is different for different categories of burgers
}
public class VegBerger extends Burger
{
    public void price()
    {
        //set price for a veg burger.
    }
}
public class NonVegBerger extends Burger
{
    public void price()
    {
        //set price for a non-veg burger.
    }
}

หากเราเพิ่มเมธอด (เป็นรูปธรรม / นามธรรม) ในอนาคตให้กับคลาสนามธรรมที่กำหนดคลาสการใช้งานจะไม่จำเป็นต้องเปลี่ยนโค้ด อย่างไรก็ตามหากเราเพิ่มวิธีการในอินเทอร์เฟซในอนาคตเราต้องเพิ่มการใช้งานให้กับคลาสทั้งหมดที่ใช้อินเทอร์เฟซนั้นมิฉะนั้นจะเกิดข้อผิดพลาดในการรวบรวมเวลา

มีความแตกต่างอื่น ๆ แต่สิ่งเหล่านี้เป็นสิ่งสำคัญซึ่งอาจเป็นสิ่งที่ผู้สัมภาษณ์คาดหวัง หวังว่านี่จะเป็นประโยชน์


1
ดีคำตอบนี้ทำให้รู้สึกมากและมันค่อนข้างชัดเจนกับตัวอย่างเช่นเมื่อเราที่ดินขึ้นการเลือกระหว่างและinterface abstract class
MAC

"จะทำอย่างไร แต่ไม่รู้ว่าจะทำอย่างไร" ในขณะที่เรากำหนดวิธีการโดยไม่มีการใช้งานใด ๆ ในนั้น "void makePayment ();" ในขณะที่กำหนดการใช้งานของวิธีการในชั้นเรียนที่จะใช้อินเตอร์เฟซ
Abdel-Raouf

45

ความแตกต่างระหว่างคลาส Abstact และอินเตอร์เฟส

  1. คลาสนามธรรมกับอินเตอร์เฟสใน Java 8
  2. ความแตกต่างทางแนวคิด:

วิธีการเริ่มต้นของอินเตอร์เฟสใน Java 8

  1. วิธีการเริ่มต้นคืออะไร?
  2. ข้อผิดพลาดในการรวบรวมวิธี ForEach แก้ไขได้โดยใช้วิธีการเริ่มต้น
  3. วิธีการเริ่มต้นและปัญหาความกำกวมหลายประการ
  4. จุดสำคัญเกี่ยวกับวิธีการเริ่มต้นของอินเตอร์เฟส Java:

วิธีการคง Java Interface

  1. Java Interface Static Method ตัวอย่างโค้ดวิธี static เทียบกับวิธีการเริ่มต้น
  2. จุดสำคัญเกี่ยวกับวิธีการคงที่ของอินเตอร์เฟส Java:

Java ฟังก์ชั่นอินเทอร์เฟซ



คลาสนามธรรมกับอินเตอร์เฟสใน Java 8

การเปลี่ยนแปลงส่วนต่อประสานของ Java 8 รวมถึงวิธีการคงที่และวิธีการเริ่มต้นในส่วนต่อประสาน ก่อนหน้า Java 8 เราสามารถมีวิธีการประกาศในอินเตอร์เฟสเท่านั้น แต่จาก Java 8 เราสามารถมีวิธีการเริ่มต้นและวิธีการคงที่ในส่วนต่อประสาน

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

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

ความแตกต่างทางแนวคิด:

คลาสนามธรรมใช้ได้สำหรับการใช้งานโครงร่าง (เช่นบางส่วน) ของอินเตอร์เฟส แต่ไม่ควรมีอยู่หากไม่มีอินเตอร์เฟสที่ตรงกัน

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

วิธีการเริ่มต้นของอินเตอร์เฟสใน Java 8

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

ลองพิจารณาตัวอย่างเล็ก ๆ เพื่อทำความเข้าใจว่ามันทำงานอย่างไร:

public interface OldInterface {
    public void existingMethod();
 
    default public void newDefaultMethod() {
        System.out.println("New default method"
               + " is added in interface");
    }
}

คลาสต่อไปนี้จะคอมไพล์สำเร็จใน Java JDK 8

public class OldInterfaceImpl implements OldInterface {
    public void existingMethod() {
     // existing implementation is here…
    }
}

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

OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod(); 

วิธีการเริ่มต้น:

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

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

วิธีการเริ่มต้นเปิดใช้งานเพื่อเพิ่มฟังก์ชันการทำงานใหม่ให้กับอินเทอร์เฟซที่มีอยู่

เมื่อเราขยายส่วนต่อประสานที่มีวิธีการเริ่มต้นเราสามารถดำเนินการดังต่อไปนี้

  1. ไม่ได้แทนที่วิธีการเริ่มต้นและจะสืบทอดวิธีการเริ่มต้น
  2. แทนที่วิธีการเริ่มต้นคล้ายกับวิธีอื่น ๆ ที่เราแทนที่ในคลาสย่อย
  3. Redeclare วิธีการเริ่มต้นเป็นนามธรรมซึ่งบังคับให้คลาสย่อยแทนที่มัน

ข้อผิดพลาดในการรวบรวมวิธี ForEach แก้ไขได้โดยใช้วิธีการเริ่มต้น

สำหรับ Java 8, คอลเลกชัน JDK ได้รับการขยายและวิธีการ forEach ถูกเพิ่มเข้าไปในคอลเลกชันทั้งหมด (ซึ่งทำงานร่วมกับ lambdas) ด้วยวิธีการทั่วไปรหัสมีลักษณะดังนี้

public interface Iterable<T> {
    public void forEach(Consumer<? super T> consumer);
}

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

Iterable Interface ที่มีวิธีการเริ่มต้นอยู่ด้านล่าง

public interface Iterable<T> {
    public default void forEach(Consumer
                   <? super T> consumer) {
        for (T t : this) {
            consumer.accept(t);
        }
    }
}

มีการใช้กลไกเดียวกันนี้เพื่อเพิ่มกระแสข้อมูลในอินเทอร์เฟซ JDK โดยไม่ทำลายการใช้คลาส


วิธีการเริ่มต้นและปัญหาความกำกวมหลายประการ

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

ลองพิจารณาตัวอย่างด้านล่าง

public interface InterfaceA {  
       default void defaultMethod(){  
           System.out.println("Interface A default method");  
    }  
}
 
public interface InterfaceB {
   default void defaultMethod(){
       System.out.println("Interface B default method");
   }
}
 
public class Impl implements InterfaceA, InterfaceB  {
}

รหัสข้างต้นจะไม่สามารถรวบรวมกับข้อผิดพลาดดังต่อไปนี้

java: class Impl สืบทอดค่าเริ่มต้นที่ไม่เกี่ยวข้องสำหรับ defaultMethod () จากประเภท InterfaceA และ InterfaceB

ในการแก้ไขคลาสนี้เราจำเป็นต้องจัดเตรียมการใช้วิธีการเริ่มต้น:

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
    }
}

นอกจากนี้หากเราต้องการเรียกใช้การติดตั้งเริ่มต้นจากอินเทอร์เฟซสุดยอดใด ๆ แทนที่จะใช้งานของเราเองเราสามารถทำได้ดังนี้

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
        // existing code here..
        InterfaceA.super.defaultMethod();
    }
}

เราสามารถเลือกการใช้งานเริ่มต้นหรือทั้งสองอย่างเป็นส่วนหนึ่งของวิธีการใหม่ของเรา

จุดสำคัญเกี่ยวกับวิธีการเริ่มต้นของอินเตอร์เฟส Java:

  1. วิธีการตั้งค่าเริ่มต้นของส่วนต่อประสาน Java จะช่วยเราในการขยายส่วนต่อประสานโดยไม่ต้องกลัวการทำลายชั้นเรียนการใช้งาน
  2. วิธีการเริ่มต้นของส่วนต่อประสาน Java ได้ลดความแตกต่างระหว่างส่วนต่อประสานและคลาสนามธรรม
  3. วิธีการตั้งค่าเริ่มต้นของอินเตอร์เฟส Java 8 จะช่วยเราในการหลีกเลี่ยงคลาสยูทิลิตี้เช่นเมธอดคลาส Collections ทั้งหมดสามารถจัดเตรียมไว้ในอินเตอร์เฟสได้
  4. วิธีการเริ่มต้นของอินเตอร์เฟส Java จะช่วยเราในการลบคลาสการใช้งานพื้นฐานเราสามารถจัดให้มีการใช้งานเริ่มต้นและคลาสการใช้งานสามารถเลือกที่จะแทนที่
  5. หนึ่งในเหตุผลสำคัญสำหรับการแนะนำวิธีการเริ่มต้นในส่วนต่อประสานคือการปรับปรุง Collections API ใน Java 8 เพื่อรองรับการแสดงออกแลมบ์ดา
  6. ถ้าคลาสใด ๆ ในลำดับชั้นมีเมธอดที่มีลายเซ็นเหมือนกันดังนั้นเมธอดดีฟอลต์จะไม่เกี่ยวข้อง วิธีการเริ่มต้นไม่สามารถแทนที่วิธีการจาก java.lang.Object เหตุผลง่ายมากก็เพราะ Object เป็นคลาสพื้นฐานสำหรับคลาส java ทั้งหมด ดังนั้นแม้ว่าเราจะมีวิธีการคลาสวัตถุที่กำหนดเป็นวิธีการเริ่มต้นในการเชื่อมต่อก็จะไร้ประโยชน์เพราะวิธีการวัตถุคลาสจะถูกนำมาใช้เสมอ นั่นเป็นเหตุผลที่จะหลีกเลี่ยงความสับสนเราไม่สามารถมีวิธีการเริ่มต้นที่เอาชนะวิธีการคลาสวัตถุ
  7. วิธีการเริ่มต้นส่วนต่อประสาน Java ยังถูกอ้างถึงเป็นวิธีการ Defender หรือวิธีการขยายเสมือน

ลิงค์ทรัพยากร:

  1. ส่วนต่อประสานกับวิธีการเริ่มต้นกับคลาสนามธรรมใน Java 8
  2. คลาสนามธรรมกับอินเทอร์เฟซในยุค JDK 8
  3. วิวัฒนาการส่วนต่อประสานผ่านวิธีการขยายเสมือน

วิธีการคง Java Interface

Java Interface Static Method ตัวอย่างโค้ดวิธี static เทียบกับวิธีการเริ่มต้น

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

public interface MyData {

    default void print(String str) {
        if (!isNull(str))
            System.out.println("MyData Print::" + str);
    }

    static boolean isNull(String str) {
        System.out.println("Interface Null Check");

        return str == null ? true : "".equals(str) ? true : false;
    }
}

ตอนนี้เรามาดูคลาสการใช้งานที่มีเมธอด isNull () พร้อมกับการติดตั้งที่ไม่ดี

public class MyDataImpl implements MyData {

    public boolean isNull(String str) {
        System.out.println("Impl Null Check");

        return str == null ? true : false;
    }

    public static void main(String args[]){
        MyDataImpl obj = new MyDataImpl();
        obj.print("");
        obj.isNull("abc");
    }
}

โปรดทราบว่า isNull (String str) เป็นวิธีการเรียนที่เรียบง่ายมันไม่ได้แทนที่วิธีการอินเตอร์เฟซ ตัวอย่างเช่นถ้าเราจะเพิ่มคำอธิบายประกอบ @Override ให้กับวิธี isNull () ก็จะส่งผลให้เกิดข้อผิดพลาดคอมไพเลอร์

ตอนนี้เมื่อเราจะเรียกใช้แอปพลิเคชันเราจะได้ผลลัพธ์ต่อไปนี้

อินเตอร์เฟส Null Check

Impl Null Check

หากเราสร้างวิธีการเชื่อมต่อจากคงที่ถึงค่าเริ่มต้นเราจะได้รับผลลัพธ์ต่อไปนี้

Impl Null Check

MyData พิมพ์ ::

Impl Null Check

วิธีการแบบคงที่ส่วนต่อประสาน Java จะปรากฏให้เห็นวิธีการติดต่อเท่านั้นถ้าเราลบวิธี isNull () จากชั้น MyDataImpl เราจะไม่สามารถใช้มันสำหรับวัตถุ MyDataImpl อย่างไรก็ตามเช่นเดียวกับวิธีการคงที่อื่น ๆ เราสามารถใช้วิธีคงที่ส่วนต่อประสานโดยใช้ชื่อชั้นเรียน ตัวอย่างเช่นคำสั่งที่ถูกต้องจะเป็น:

boolean result = MyData.isNull("abc");

จุดสำคัญเกี่ยวกับวิธีการคงที่ของอินเตอร์เฟส Java:

  1. วิธีการคงที่ของส่วนต่อประสาน Java เป็นส่วนหนึ่งของส่วนติดต่อเราไม่สามารถใช้มันสำหรับวัตถุคลาสการใช้งาน
  2. Java คงที่วิธีการอินเตอร์เฟซที่ดีสำหรับการให้วิธีการยูทิลิตี้เช่นการตรวจสอบโมฆะเรียงลำดับการเก็บรวบรวม ฯลฯ
  3. วิธีการคงที่ส่วนต่อประสาน Java ช่วยให้เราในการให้ความปลอดภัยโดยไม่อนุญาตให้ชั้นเรียนการใช้งานเพื่อแทนที่พวกเขา
  4. เราไม่สามารถกำหนดวิธีการคงที่ส่วนต่อประสานสำหรับวิธีการของคลาสวัตถุเราจะได้รับข้อผิดพลาดของคอมไพเลอร์เนื่องจาก“ วิธีการแบบคงที่นี้ไม่สามารถซ่อนวิธีอินสแตนซ์จากวัตถุ” นี่เป็นเพราะมันไม่ได้รับอนุญาตใน java เนื่องจาก Object เป็นคลาสพื้นฐานสำหรับทุกชั้นเรียนและเราไม่สามารถมีวิธีคงที่ระดับหนึ่งและวิธีการอินสแตนซ์อื่นที่มีลายเซ็นเดียวกัน
  5. เราสามารถใช้วิธีการคงที่ของอินเตอร์เฟส Java เพื่อลบคลาสยูทิลิตี้เช่นคอลเลกชันและย้ายวิธีการคงที่ทั้งหมดไปยังอินเทอร์เฟซที่เกี่ยวข้องซึ่งจะง่ายต่อการค้นหาและใช้งาน

Java ฟังก์ชั่นอินเทอร์เฟซ

ก่อนที่จะสรุปโพสต์ฉันต้องการที่จะให้คำแนะนำสั้น ๆ เกี่ยวกับฟังก์ชั่นการใช้งาน อินเทอร์เฟซที่มีวิธีการทางนามธรรมที่เรียกว่า Functional Interface

คำอธิบายประกอบใหม่@FunctionalInterfaceได้รับการแนะนำให้ทำเครื่องหมายส่วนต่อประสานเป็น Functional Interface @FunctionalInterfaceคำอธิบายประกอบเป็นสิ่งอำนวยความสะดวกเพื่อหลีกเลี่ยงการเพิ่มโดยไม่ได้ตั้งใจของวิธีการที่เป็นนามธรรมในส่วนต่อประสานการทำงาน มันเป็นทางเลือก แต่เป็นแนวปฏิบัติที่ดีที่จะใช้

ส่วนต่อประสานการใช้งานนั้นเป็นคุณสมบัติที่รอคอยมานานและเป็นที่ต้องการอย่างมากของ Java 8 เพราะมันทำให้เราสามารถใช้แลมบ์ดานิพจน์เพื่อยกตัวอย่างพวกมัน แพ็กเกจใหม่ java.util.function ที่มีอินเตอร์เฟสการทำงานจำนวนมากถูกเพิ่มเพื่อจัดเตรียมชนิดเป้าหมายสำหรับนิพจน์แลมบ์ดาและการอ้างอิงเมธอด เราจะตรวจสอบอินเตอร์เฟสการใช้งานและการแสดงออกแลมบ์ดาในโพสต์ในอนาคต

ที่ตั้งทรัพยากร:

  1. การเปลี่ยนแปลงส่วนต่อประสานของ Java 8 - วิธีการคงที่, วิธีการเริ่มต้น

8
ฉันกำลังค้นหาคำตอบที่ปรับปรุงแล้วเหล่านี้ทุกประการ ขอบคุณสำหรับการตอบสนองที่รวดเร็ว
Ravindra babu

41

ใบแจ้งยอดทั้งหมดของคุณถูกต้องยกเว้นใบแจ้งยอดแรกของคุณ (หลังจากการปล่อย Java 8):

วิธีการของอินเทอร์เฟซ Java เป็นนามธรรมโดยปริยายและไม่สามารถใช้งานได้

จากหน้าเอกสาร:

อินเทอร์เฟซเป็นชนิดการอ้างอิงคล้ายกับคลาสที่สามารถประกอบด้วยค่าคงที่เท่านั้น ลายเซ็นของวิธีการวิธีการเริ่มต้นวิธีการแบบคงที่และชนิดซ้อนกัน

เมธอดเนื้อหามีอยู่สำหรับวิธีการเริ่มต้นและวิธีการคงที่เท่านั้น

วิธีการเริ่มต้น:

อินเทอร์เฟซสามารถมีวิธีการเริ่มต้นแต่แตกต่างจากวิธีนามธรรมในคลาสนามธรรม

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

เมื่อคุณขยายอินเทอร์เฟซที่ประกอบด้วยวิธีการเริ่มต้นคุณสามารถทำสิ่งต่อไปนี้:

  1. ไม่พูดถึงวิธีการเริ่มต้นเลยซึ่งจะช่วยให้ส่วนต่อขยายที่ได้รับการสืบทอดเป็นวิธีการเริ่มต้น
  2. redeclare abstractวิธีการเริ่มต้นซึ่งจะทำให้มัน
  3. กำหนดวิธีการเริ่มต้นใหม่ซึ่งจะแทนที่มัน

วิธีการคงที่:

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

สิ่งนี้ทำให้ง่ายขึ้นสำหรับคุณในการจัดระเบียบวิธีใช้ตัวช่วยในไลบรารีของคุณ

โค้ดตัวอย่างจากหน้าเอกสารเกี่ยวกับinterfaceการมีstaticและdefaultวิธีการ

import java.time.*;

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();

    static ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

ใช้แนวทางด้านล่างเพื่อเลือกว่าจะใช้อินเทอร์เฟซหรือคลาสนามธรรม

อินเตอร์เฟซ:

  1. ในการกำหนดสัญญา (ไร้สัญชาติโดยเฉพาะ - ฉันหมายถึงไม่มีตัวแปร)
  2. เพื่อเชื่อมโยงคลาสที่ไม่เกี่ยวข้องด้วยมีความสามารถ
  3. เพื่อประกาศตัวแปรคงที่สาธารณะ ( รัฐไม่เปลี่ยนรูป )

ระดับนามธรรม:

  1. แบ่งปันรหัสระหว่างคลาสที่เกี่ยวข้องอย่างใกล้ชิดหลายชั้น มันกำหนดเป็นความสัมพันธ์

  2. แชร์สถานะทั่วไประหว่างคลาสที่เกี่ยวข้อง (สถานะสามารถแก้ไขได้ในคลาสคอนกรีต)

กระทู้ที่เกี่ยวข้อง:

อินเตอร์เฟส vs คลาสนามธรรม (OO ทั่วไป)

นำมาใช้ vs ขยาย: เมื่อใดควรใช้ ความแตกต่างคืออะไร?

เมื่อทำตามตัวอย่างเหล่านี้คุณสามารถเข้าใจได้

คลาสที่ไม่เกี่ยวข้องสามารถมีความสามารถผ่านส่วนต่อประสาน แต่คลาสที่เกี่ยวข้องจะเปลี่ยนพฤติกรรมผ่านการขยายคลาสพื้นฐาน


คุณหมายถึงอะไรว่า "สัญญาไร้สัญชาติ" เป็นรายการที่ 1 เกี่ยวกับอินเทอร์เฟซ
Maksim Dmitriev

1
ไม่มีสถานะที่ไม่แน่นอน เนื่องจากส่วนต่อประสานสามารถมีค่าคงที่ข้อมูลจึงสามารถกลายพันธุ์ได้ซึ่งแตกต่างจากคลาสนามธรรม
Ravindra babu

1
แก้ไขข้อความข้างต้น ในอินเทอร์เฟซข้อมูลไม่สามารถเปลี่ยนแปลงได้ซึ่งแตกต่างจากคลาสนามธรรม
Ravindra babu

2
นี่คือคำตอบที่ดีที่สุด ไม่เพียง แต่จะกล่าวถึง Java8 เท่านั้น แต่ยังอธิบายในสถานการณ์เฉพาะที่คุณจะใช้อย่างใดอย่างหนึ่ง
Shuklaswag

แนวคิดของstatelessอินเทอร์เฟซเป็นที่นิยมมาก อินเทอร์เฟซต้องไม่มีสถานะใด ๆ (อินเทอร์เฟซอาจมีค่าคงที่ แต่สุดท้ายจะคงที่ / ไม่เปลี่ยนแปลงดังนั้น)
Kaihua

22

คำอธิบายของคุณดูดี แต่อาจดูเหมือนว่าคุณกำลังอ่านทุกอย่างจากตำราหรือไม่? : - /

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

ส่วนตัวฉันขอแนะนำลิงค์นี้: http://mindprod.com/jgloss/interfacevsabstract.html#TABLE

สำหรับรายการความแตกต่างที่ละเอียดถี่ถ้วน ..

หวังว่ามันจะช่วยให้คุณและผู้อ่านอื่น ๆ ในการสัมภาษณ์ในอนาคตของพวกเขา


1
การแชร์ลิงก์นั้นยอดเยี่ยมจริงๆ
Premraj

คุณสามารถให้การใช้งานเริ่มต้นในอินเทอร์เฟซ java โดยใช้คีย์เวิร์ดเริ่มต้น
Ogen

21

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

อินเทอร์เฟซและคลาสนามธรรมแม้ว่าจะคล้ายกันจากมุมมองทางเทคนิค แต่มีความหมายและวัตถุประสงค์ที่แตกต่างกันโดยสิ้นเชิง

สรุป

  1. อินเตอร์เฟซที่กำหนดในสัญญาว่าการดำเนินการบางอย่างที่จะตอบสนองสำหรับคุณ

  2. คลาสนามธรรมให้พฤติกรรมเริ่มต้นที่การใช้งานของคุณสามารถนำมาใช้ใหม่

ทั้งสองประเด็นข้างต้นคือสิ่งที่ฉันกำลังมองหาเมื่อสัมภาษณ์และเป็นการสรุปที่มีขนาดกะทัดรัดเพียงพอ อ่านต่อเพื่อดูรายละเอียดเพิ่มเติม

สรุปทางเลือก

  1. อินเทอร์เฟซสำหรับการกำหนด API สาธารณะ
  2. คลาสนามธรรมสำหรับใช้ภายในและเพื่อกำหนด SPIs

ตามตัวอย่าง

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

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

คลาสนามธรรมอยู่ตรงกลางระหว่างอินเตอร์เฟสและคลาสที่เป็นรูปธรรม มันควรจะช่วยให้การใช้งานร่วมกันรหัสทั่วไปหรือน่าเบื่อ ตัวอย่างเช่นAbstractCollectionจัดเตรียมการนำไปใช้งานพื้นฐานสำหรับisEmptyขนาดเป็น 0 containsตามที่วนซ้ำและเปรียบเทียบaddAllเป็นซ้ำaddและอื่น ๆ วิธีนี้ช่วยให้การใช้งานมุ่งเน้นไปที่ส่วนสำคัญที่แยกความแตกต่างระหว่างพวกเขา: วิธีการจัดเก็บและดึงข้อมูลจริง

อีกมุมมองหนึ่ง: APIs กับ SPIs

อินเทอร์เฟซเป็นเกตเวย์ที่มีการทำงานร่วมกันต่ำระหว่างส่วนต่าง ๆ ของรหัส พวกเขาอนุญาตให้ไลบรารีมีอยู่และมีวิวัฒนาการโดยไม่ทำลายผู้ใช้ไลบรารีทุกคนเมื่อมีบางสิ่งเปลี่ยนแปลงภายใน มันเรียกว่าApplication Programming Interfaceไม่ใช่ Application Programming Classes ในขนาดที่เล็กลงพวกเขายังอนุญาตให้นักพัฒนาหลายคนทำงานร่วมกันได้สำเร็จในโครงการขนาดใหญ่โดยแยกโมดูลที่แตกต่างกันผ่านทางเอกสารที่มีอินเตอร์เฟสที่ดี

คลาสนามธรรมเป็นตัวช่วยที่เชื่อมโยงกันสูงที่จะใช้เมื่อนำอินเตอร์เฟสมาใช้โดยสมมติว่ามีรายละเอียดของการนำไปปฏิบัติบางระดับ อีกทางเลือกหนึ่งคลาสนามธรรมใช้สำหรับกำหนด SPIs, Service Provider Interfaces

ความแตกต่างระหว่าง API และ SPI นั้นบอบบาง แต่สำคัญ: สำหรับ API โฟกัสอยู่ที่ใครใช้และสำหรับ SPI นั้นโฟกัสอยู่ที่ว่าใครจะใช้มัน

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

หมายเหตุเกี่ยวกับ Java 8 และวิธีการเริ่มต้น

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

"หนังสือความรู้"

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

นี่คือการเปรียบเทียบ: คำถามควรเป็น:

อะไรจะดีไปกว่าการเช่าสำหรับคืนพรหม, รถยนต์หรือห้องพักในโรงแรม?

คำตอบทางเทคนิคดูเหมือนว่า:

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

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


คุณหมายถึง "coupling low" หรือเปล่า
user2418306

@ user2418306 ไม่การทำงานร่วมกันเป็นคำทั่วไปมากกว่าซึ่งรวมถึงการแต่งงานแม้ว่าพวกเขาจะมีความหมายเหมือนกัน
Sergiu Dumitriu

9

คิดอย่างไรเกี่ยวกับวิธีต่อไปนี้:

  • ความสัมพันธ์ระหว่างคลาสและคลาสนามธรรมเป็นประเภท "is-a"
  • ความสัมพันธ์ระหว่างคลาสและส่วนต่อประสานเป็นประเภท "has-a"

ดังนั้นเมื่อคุณมีสัตว์เลี้ยงลูกด้วยนมในระดับนามธรรมมนุษย์ย่อยและอินเทอร์เฟซการขับรถแล้วคุณสามารถพูดได้

  • มนุษย์แต่ละคนเป็นสัตว์เลี้ยงลูกด้วยนม
  • มนุษย์แต่ละคนมีพฤติกรรมการขับรถ (พฤติกรรม)

ข้อเสนอแนะของฉันคือวลีความรู้ของหนังสือบ่งบอกว่าเขาต้องการได้ยินความแตกต่างทางความหมายระหว่างทั้งสอง (เหมือนคนอื่น ๆ ที่นี่แล้วแนะนำ)


9

อินเทอร์เฟซคือ "สัญญา" ที่คลาสที่ใช้สัญญาสัญญาเพื่อใช้วิธีการ ตัวอย่างที่ฉันต้องเขียนส่วนต่อประสานแทนที่จะเป็นคลาสคือตอนที่ฉันอัพเกรดเกมจาก 2D เป็น 3D ฉันต้องสร้างส่วนต่อประสานเพื่อแชร์คลาสระหว่างเกม 2D และ 3D

package adventure;
import java.awt.*;
public interface Playable {
    public void playSound(String s);
    public Image loadPicture(String s);    
}

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

public class Adventure extends JFrame implements Playable

public class Dungeon3D extends SimpleApplication implements Playable

public class Main extends SimpleApplication implements AnimEventListener, ActionListener, Playable

โดยทั่วไปแล้วในโลกแห่งเกมโลกสามารถเป็นคลาสนามธรรมที่ใช้วิธีการในเกม:

public abstract class World...

    public Playable owner;

    public Playable getOwner() {
        return owner;
    }

    public void setOwner(Playable owner) {
        this.owner = owner;
    }

6

คลาสนามธรรมไม่ได้เป็นนามธรรมที่บริสุทธิ์ bcz การเก็บรวบรวมของคอนกรีต (วิธีการดำเนินการ) เช่นเดียวกับวิธีการที่ไม่ได้ดำเนินการ แต่การเชื่อมต่อนั้นเป็นนามธรรมที่บริสุทธิ์ bcz มีเพียงวิธีการที่ไม่ได้ดำเนินการไม่ใช่วิธีที่เป็นรูปธรรม

ทำไมต้องเรียนบทคัดย่อ?

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

ทำไมการเชื่อมต่อ?

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

5

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

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

(*) เพื่อขยายคำอุปมานี้สมมติว่าสมาชิกทุกคนในเผ่าพันธุ์มีชีวิตอยู่ในวัยเดียวกัน นี่หมายความว่าบรรพบุรุษทั้งหมดของบรรพบุรุษที่ตายแล้วจะต้องตาย - และเช่นเดียวกันลูกหลานทั้งหมดของบรรพบุรุษที่มีชีวิตจะต้องมีชีวิตอยู่


4

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

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

ขอให้โชคดีกับการสัมภาษณ์ในอนาคต

นอกจากนี้คำตอบของฉันสำหรับคำถามนี้เป็นมากกว่าเทคนิคการสัมภาษณ์มากกว่าวัสดุด้านเทคนิคที่คุณให้ไว้ อาจลองอ่านเกี่ยวกับเรื่องนี้ดู https://workplace.stackexchange.com/อาจเป็นสถานที่ที่ยอดเยี่ยมสำหรับสิ่งเหล่านี้


1
คุณสามารถบอกฉันว่าคุณตอบอย่างไร อาจจะช่วยฉันได้
code_fish

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

4

คุณสามารถเลือกการเชื่อมต่อใน Java เพื่อหลีกเลี่ยงปัญหาเพชรในการรับมรดกหลาย

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

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


3

อินเทอร์เฟซเป็นนามธรรมล้วนๆ เราไม่มีรหัสการนำไปใช้ในอินเทอร์เฟซ

คลาสนามธรรมมีทั้งวิธีและการใช้งาน

คลิกที่นี่เพื่อดูบทช่วยสอนเกี่ยวกับส่วนต่อประสานและคลาสนามธรรม


3

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


3

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

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

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

ฉันคิดว่าสิ่งนี้อาจโน้มน้าวผู้สัมภาษณ์ ... การสัมภาษณ์ที่มีความสุขล่วงหน้า


2

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

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

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

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

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


2

ความแตกต่างพื้นฐานระหว่างอินเทอร์เฟซและคลาสนามธรรมคืออินเทอร์เฟซรองรับหลายมรดก แต่คลาสนามธรรมไม่

ในคลาส abstract คุณสามารถจัดเตรียมเมธอด abstract ทั้งหมดเช่นอินเตอร์เฟส

ทำไมต้องใช้คลาสนามธรรม?

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

ทำไมต้องมีส่วนต่อประสาน

สมมติว่าฉันมีงานที่ฉันไม่มีประสบการณ์ในพื้นที่นั้น ตัวอย่างถ้าคุณต้องการสร้างสิ่งปลูกสร้างหรือเขื่อนคุณจะทำอะไรในสถานการณ์นั้น

  1. คุณจะระบุความต้องการของคุณคืออะไรและทำสัญญากับข้อกำหนดนั้น
  2. จากนั้นเรียกผู้ประมูลเพื่อสร้างโครงการของคุณ
  3. ใครเคยสร้างโครงการที่ควรตอบสนองความต้องการของคุณ แต่ตรรกะการก่อสร้างนั้นแตกต่างจากผู้ขายรายหนึ่งกับผู้ขายรายอื่น

ที่นี่ฉันไม่ได้กังวลเกี่ยวกับตรรกะที่พวกเขาสร้าง วัตถุสุดท้ายเป็นไปตามข้อกำหนดของฉันหรือไม่นั่นเป็นเพียงประเด็นสำคัญของฉัน

ที่นี่ความต้องการของคุณที่เรียกว่าส่วนต่อประสานและตัวสร้างจะเรียกว่า implementor


2

ฉันจะตอบด้วยวิธีนี้:

  • มรดกผ่านลำดับชั้นหมายถึงมรดกรัฐ ;
  • ขณะที่มรดกผ่านระบบอินเตอร์ย่อมาจากมรดกพฤติกรรม ;

คลาสนามธรรมสามารถใช้เป็นบางสิ่งระหว่างทั้งสองกรณี (มันแนะนำบางสถานะ แต่ยังบังคับให้คุณกำหนดพฤติกรรม) คลาสแบบเต็มนามธรรมเป็นอินเทอร์เฟซ (นี่คือการพัฒนาเพิ่มเติมของคลาสที่ประกอบด้วยวิธีเสมือนใน C ++ เท่านั้น เท่าที่ฉันรู้ซินแท็คซ์)

แน่นอนเริ่มจาก Java 8 สิ่งต่าง ๆ มีการเปลี่ยนแปลงเล็กน้อย แต่ความคิดยังคงเหมือนเดิม

ฉันเดาว่านี่น่าจะเพียงพอสำหรับการสัมภาษณ์ Java ทั่วไปถ้าคุณไม่ได้สัมภาษณ์ทีมคอมไพเลอร์


1

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


1

ในคลาสนามธรรมคุณสามารถเขียนวิธีใช้เริ่มต้นได้! แต่ในอินเทอร์เฟซคุณไม่สามารถ โดยพื้นฐานแล้วในส่วนต่อประสานจะมีวิธีการเสมือนจริงที่ต้องดำเนินการโดยคลาสที่ใช้ส่วนต่อประสาน


1

ใช่คำตอบของคุณถูกต้องทางเทคนิค แต่ที่ที่คุณผิดพลาดไม่ได้แสดงพวกเขาคุณเข้าใจว่า Upside และ Downsides ของการเลือกอย่างใดอย่างหนึ่ง ยิ่งไปกว่านั้นพวกเขายังมีความกังวล / กังวลเกี่ยวกับความเข้ากันได้ของโค๊ดเบสกับการอัพเกรดในอนาคต การตอบสนองประเภทนี้อาจช่วยได้ (นอกเหนือจากที่คุณพูด):

"การเลือกคลาสนามธรรมเหนือคลาสอินเตอร์เฟสจะขึ้นอยู่กับสิ่งที่เราคาดการณ์ในอนาคตของโค้ด

คลาสนามธรรมช่วยให้สามารถใช้งานร่วมกันได้ดีกว่าเพราะคุณสามารถเพิ่มพฤติกรรมไปยังคลาสนามธรรมต่อไปในอนาคตโดยไม่ทำให้โค้ดที่มีอยู่ของคุณพัง -> เป็นไปไม่ได้ในอินเทอร์เฟซคลาส

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

ดังนั้นในที่สุดกฎทั่วไปที่ดีของหัวแม่มือคือ: ชอบใช้คลาสอินเทอร์เฟซเมื่อไม่มีการใช้งานที่มีอยู่ / เริ่มต้นใน codebase ของคุณ และใช้คลาสนามธรรมเพื่อรักษาความเข้ากันได้ถ้าคุณรู้ว่าคุณจะอัปเดตคลาสของคุณในอนาคต "

ขอให้โชคดีในการสัมภาษณ์ครั้งต่อไปของคุณ!


1

ฉันจะพยายามตอบโดยใช้สถานการณ์จริงเพื่อแสดงความแตกต่างระหว่างทั้งสอง

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

ตัวอย่างเช่นสมมติว่าฉันมีคลาส Task ที่ดำเนินการบางอย่างตอนนี้เพื่อรันภารกิจในเธรดแยกกันฉันไม่ต้องการขยายเธรดคลาสค่อนข้างเป็นทางเลือกที่ดีกว่าคือการทำให้ Task ใช้อินเตอร์เฟส Runnable (เช่นใช้เมธอด run () ) จากนั้นผ่านวัตถุของคลาสงานนี้ไปยังอินสแตนซ์เธรดและเรียกใช้เมธอด start ()

ตอนนี้คุณสามารถถามได้ไหมว่า Runnable เป็นคลาสนามธรรมหรือไม่?

ในทางเทคนิคนั้นเป็นไปได้ แต่การออกแบบที่ชาญฉลาดนั้นน่าจะเป็นเหตุผลทางเลือกที่แย่

  • Runnable ไม่มีสถานะที่เกี่ยวข้องกับมันและไม่มี 'การเสนอ' การใช้งานเริ่มต้นสำหรับวิธีการ run ()
  • ภารกิจจะต้องขยายออกไปดังนั้นจึงไม่สามารถขยายคลาสอื่นได้
  • งานไม่มีสิ่งใดที่จะเสนอให้เป็นความเชี่ยวชาญของคลาส Runnable ทั้งหมดที่จำเป็นคือการแทนที่เมธอด run ()

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

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

คำเตือน:ตัวอย่างโง่ ๆ ดังต่อไปนี้พยายามอย่าตัดสิน :-P

interface Forgiver {
    void forgive();
}

abstract class GodLike implements Forgiver {
    abstract void forget();
    final void forgive() {
        forget();
    }
}

ตอนนี้คุณได้รับเลือกให้เป็น GodLike แต่คุณอาจเลือกที่จะให้อภัยเท่านั้น (เช่นไม่ใช่ GodLike) และทำ:

class HumanLike implements Forgiver {
    void forgive() {
       // forgive but remember    
    }
}

หรือคุณอาจเลือกที่จะเป็น GodLike และทำ:

class AngelLike extends GodLike {
    void forget() {
       // forget to forgive     
    }
}

PS ที่มีอินเตอร์เฟส java 8 สามารถมีวิธีการแบบคงที่เช่นกัน (การใช้งานแบบ overridable) ซึ่งจะทำให้ b / w ที่ต่างกันและคลาสนามธรรมนั้นแคบลง


1

เกือบทุกอย่างดูเหมือนว่าจะครอบคลุมที่นี่แล้ว .. เพิ่มเพียงจุดเดียวในการใช้งานจริงของabstractชั้นเรียน:

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


1

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

หลังจากเสร็จสิ้นคำตอบของคุณคุณควรข้ามไปที่ตัวอย่าง:

นามธรรม:

ตัวอย่างเช่นเรามีฟังก์ชั่นเงินเดือนที่มีค่าพารามิเตอร์บางอย่างร่วมกันกับพนักงานทุกคน จากนั้นเราสามารถมีคลาสนามธรรมที่เรียกว่า CTC ด้วยวิธีการที่กำหนดบางส่วนและมันจะได้รับการขยายโดยพนักงานทุกประเภทและได้รับการไถ่ถอนตามความต้องการพิเศษของพวกเขา สำหรับฟังก์ชั่นทั่วไป

public abstract class CTC {

    public int salary(int hra, int da, int extra)
    {
        int total;
        total = hra+da+extra;
        //incentive for specific performing employee
        //total = hra+da+extra+incentive;
        return total;
    }
}

class Manger extends CTC
{
}


class CEO extends CTC
{
}

class Developer extends CTC
{   
}

อินเตอร์เฟซ

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

public interface EmployeType {

    public String typeOfEmployee();
}

class ContarctOne implements EmployeType
{

    @Override
    public String typeOfEmployee() {
        return "contract";
    }

}

class PermanentOne implements EmployeType
{

    @Override
    public String typeOfEmployee() {
        return "permanent";
    }

}

คุณสามารถมีกิจกรรมบังคับที่มีคลาสนามธรรมเช่นกันโดยกำหนด methgos เป็นนามธรรมขณะนี้คลาส tha ขยายคลาสนามธรรม remin บทคัดย่อหนึ่งจนกว่ามันจะแทนที่ฟังก์ชั่นที่เป็นนามธรรม


1

จากสิ่งที่ฉันเข้าใจและวิธีที่ฉันเข้าหา

อินเทอร์เฟซเป็นเหมือนข้อกำหนด / สัญญาคลาสใด ๆ ที่ใช้คลาสอินเตอร์เฟสต้องใช้วิธีการทั้งหมดที่กำหนดไว้ในคลาสนามธรรม (ยกเว้นวิธีการเริ่มต้น (แนะนำใน Java 8))

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

หมายเหตุ: คุณไม่สามารถมีฟังก์ชั่นของร่างกายในวิธีการเชื่อมต่อเว้นแต่ว่าวิธีการจะคงที่หรือเริ่มต้น


0

ฉันเชื่อว่าสิ่งที่ผู้สัมภาษณ์พยายามทำคืออาจจะเป็นความแตกต่างระหว่างอินเตอร์เฟสและการใช้งาน

อินเทอร์เฟซ - ไม่ใช่อินเทอร์เฟซ Java แต่ "อินเทอร์เฟซ" ในแง่ทั่วไป - สำหรับโมดูลรหัสคือโดยทั่วไปสัญญาที่ทำด้วยรหัสไคลเอนต์ที่ใช้อินเทอร์เฟซ

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

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

ในทางตรงกันข้าม Java abstract class ให้การใช้งานบางส่วนของคลาสแทนที่จะมีวัตถุประสงค์หลักในการระบุอินเตอร์เฟส ควรใช้เมื่อมีการใช้รหัสร่วมกันหลายคลาส แต่เมื่อคาดว่า subclasses จะให้ส่วนหนึ่งของการนำไปใช้ สิ่งนี้อนุญาตให้รหัสที่ใช้ร่วมกันปรากฏในที่เดียวเท่านั้น - คลาสนามธรรม - ในขณะที่ทำให้ชัดเจนว่าบางส่วนของการใช้งานไม่ได้อยู่ในชั้นนามธรรมและคาดว่าจะได้รับจากคลาสย่อย


0

คำตอบของคุณถูกต้อง แต่ผู้สัมภาษณ์ต้องการให้คุณแยกความแตกต่างตามมุมมองด้านวิศวกรรมซอฟต์แวร์ไม่ใช่ตามรายละเอียดของ Java

คำง่าย ๆ :

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

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