เหตุใดคลาสนามธรรมที่ใช้อินเทอร์เฟซจึงพลาดการประกาศ / การใช้งานวิธีใดวิธีหนึ่งของอินเทอร์เฟซ


123

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

ตัวอย่างเช่นให้อินเทอร์เฟซ:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

คลาสนามธรรมต่อไปนี้ได้รับการรวบรวมอย่างสนุกสนานโดยไม่มีคำเตือนหรือข้อผิดพลาด:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

คุณอธิบายได้ไหมว่าทำไม?


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

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

คุณสามารถดู anwer สำหรับคำถามที่คล้ายกันstackoverflow.com/questions/8026580/…
Do Nhu Vy

คำตอบ:


156

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

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


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

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

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

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

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

33

ดีอย่างสมบูรณ์แบบ
คุณไม่สามารถสร้างอินสแตนซ์คลาสนามธรรมได้ แต่คลาสนามธรรมสามารถใช้ในการจัดวางการใช้งานทั่วไปสำหรับ m1 () และ m3 ()
ดังนั้นหากการใช้งาน m2 () แตกต่างกันสำหรับการใช้งานแต่ละครั้ง แต่ m1 และ m3 ไม่ใช่ คุณสามารถสร้างการใช้งาน IAnything ที่เป็นรูปธรรมที่แตกต่างกันโดยใช้เพียงการใช้งาน m2 ที่แตกต่างกันและได้มาจาก AbstractThing - โดยปฏิบัติตามหลักการ DRY การตรวจสอบว่าอินเทอร์เฟซถูกนำไปใช้อย่างสมบูรณ์สำหรับคลาสนามธรรมนั้นไร้ประโยชน์หรือไม่ ..

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


7

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

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

ดังนั้นคุณต้องคิดว่าจะเกิดอะไรขึ้นเมื่ออินเทอร์เฟซขยายอินเทอร์เฟซอื่น ตัวอย่างเช่น ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

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

หวังว่าจะช่วย ... และอัลเลาะห์ 'alam!


นั่นเป็นมุมมองที่น่าสนใจ มันทำให้ฉันคิดว่า "คลาสนามธรรม" เป็น "อินเทอร์เฟซที่เป็นรูปธรรม" จริงๆนั่นคือการเชื่อมต่อกับวิธีการที่เป็นรูปธรรมมากกว่าคลาสด้วยวิธีนามธรรมบางอย่าง
Giulio Piancastelli

... บิตของทั้งสองอย่างจริงๆ แต่สิ่งหนึ่งที่แน่นอนคือพวกเขาไม่สามารถทำได้ทันที
ขอบคุณ

4

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


4

รับอินเทอร์เฟซ:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

นี่คือสิ่งที่ Java เห็นจริง:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

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

เมื่อคุณกฎว่าทุกวิธีการจะต้องดำเนินการในการที่ได้มานำไปใช้เท่านั้นที่จะเป็นรูปธรรมการดำเนินงาน (เช่นซึ่งไม่ได้เป็นของตัวเอง)implementinterfaceinterfaceclassclassabstract

หากคุณวางแผนที่จะสร้างabstract classออกจากมันจริง ๆ แล้วไม่มีกฎใดที่บอกว่าคุณimplementใช้interfaceวิธีการทั้งหมด(โปรดทราบว่าในกรณีเช่นนี้จำเป็นต้องประกาศสิ่งที่ได้มาclassเป็นabstract)


ใช้javap IAnything.classเพื่อสร้างข้อมูลโค้ดที่สอง
sharhp

3

เมื่อคลาสนามธรรมใช้อินเทอร์เฟซ

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

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

ในกรณีนี้คลาส X จะต้องเป็นนามธรรมเนื่องจากไม่ได้ใช้ Y อย่างสมบูรณ์ แต่คลาส XX ใช้ Y

อ้างอิง: http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html


1

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

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