ทำไมไม่เป็นสาขานามธรรม?


102

เหตุใดคลาส Java จึงไม่มีฟิลด์นามธรรมเช่นเดียวกับที่สามารถมีวิธีนามธรรมได้

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

เหตุใดฉันจึงไม่สามารถสร้างบทคัดย่อภาคสนามขึ้นมาได้ Java ได้รับการออกแบบมาเพื่ออนุญาตสิ่งนี้หรือไม่?


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

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

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

1
@ liltitus27 ฉันคิดว่าประเด็นของฉันเมื่อ 3.5 ปีที่แล้วคือการมีฟิลด์นามธรรมจะไม่เปลี่ยนแปลงมากนักนอกจากทำลายแนวคิดทั้งหมดในการแยกผู้ใช้อินเทอร์เฟซออกจากการใช้งาน
Peter Lawrey

สิ่งนี้จะเป็นประโยชน์เพราะสามารถอนุญาตคำอธิบายประกอบฟิลด์ที่กำหนดเองในคลาสย่อย
geg

คำตอบ:


105

คุณสามารถทำสิ่งที่คุณอธิบายได้โดยมีฟิลด์สุดท้ายในคลาสนามธรรมของคุณที่เริ่มต้นในตัวสร้าง (โค้ดที่ยังไม่ทดสอบ):

abstract class Base {

    final String errMsg;

    Base(String msg) {
        errMsg = msg;
    }

    abstract String doSomething();
}

class Sub extends Base {

    Sub() {
        super("Sub message");
    }

    String doSomething() {

        return errMsg + " from something";
    }
}

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


ยังไม่มีตัวแก้ไขให้ลองใช้ที่นี่ แต่นี่คือสิ่งที่ฉันจะโพสต์เช่นกัน ..
ทิม

6
"คอมไพเลอร์จะเตือน". จริงๆแล้วตัวสร้างลูกจะพยายามใช้ตัวสร้างโนอาร์กที่ไม่มีอยู่จริงและนั่นเป็นข้อผิดพลาดในการคอมไพล์ (ไม่ใช่คำเตือน)
Stephen C

และการเพิ่มความคิดเห็นของ @Stephen C "เช่นเมื่อไม่มีการใช้วิธีนามธรรม" ก็เป็นข้อผิดพลาดเช่นกันไม่ใช่คำเตือน
Laurence Gonsalves

4
คำตอบที่ดี - สิ่งนี้ได้ผลสำหรับฉัน ฉันรู้ว่ามันได้รับในขณะที่ตั้งแต่มันถูกโพสต์ แต่คิดว่าจะต้องประกาศBase abstract
Steve Chambers

2
ฉันยังไม่ชอบวิธีนี้ (แม้ว่าจะเป็นวิธีเดียวที่จะไป) เพราะมันเพิ่มอาร์กิวเมนต์พิเศษให้กับตัวสร้าง ในการเขียนโปรแกรมเครือข่ายฉันชอบสร้างประเภทข้อความโดยที่ข้อความใหม่แต่ละข้อความจะใช้ประเภทนามธรรม แต่ต้องมีอักขระที่กำหนดเฉพาะเช่น 'A' สำหรับ AcceptMessage และ 'L' สำหรับ LoginMessage สิ่งเหล่านี้ช่วยให้มั่นใจได้ว่าโปรโตคอลข้อความที่กำหนดเองจะสามารถใส่อักขระที่แตกต่างนี้ไว้ในสายได้ สิ่งเหล่านี้มีนัยต่อชั้นเรียนดังนั้นจึงควรทำหน้าที่เป็นเขตข้อมูลได้ดีที่สุดไม่ใช่สิ่งที่ส่งผ่านไปยังตัวสร้าง
Adam Hughes

8

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

public abstract class Abstract {
    protected String errorMsg = "";

    public String getErrMsg() {
        return this.errorMsg;
    }
}

public class Foo extends Abstract {
    public Foo() {
       this.errorMsg = "Foo";
    }

}

public class Bar extends Abstract {
    public Bar() {
       this.errorMsg = "Bar";
    }
}

ประเด็นของคุณคือคุณต้องการบังคับใช้ / ลบล้าง / อะไรก็ตามerrorMsgในคลาสย่อย? ฉันคิดว่าคุณแค่อยากมีเมธอดในคลาสพื้นฐานและไม่รู้ว่าจะจัดการกับฟิลด์อย่างไร


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

1
ดีด้วยวิธีนี้ไม่ได้ทุกประเภทรองมีการแทนที่ค่า ฉันยังสามารถกำหนดได้protected String errorMsg;ว่าจะบังคับใช้วิธีใดให้ฉันตั้งค่าในคลาสย่อย มันคล้ายกับคลาสนามธรรมที่ใช้วิธีการทั้งหมดเป็นตัวยึดตำแหน่งเพื่อให้นักพัฒนาไม่ต้องใช้ทุกวิธีแม้ว่าจะไม่ต้องการก็ตาม
Felix Kling

7
บอกprotected String errorMsg;ไม่ได้บังคับให้คุณตั้งค่าใน subclasses
Laurence Gonsalves

1
หากมีค่าเป็นโมฆะหากคุณไม่ตั้งค่าให้นับเป็น "การบังคับใช้" คุณจะเรียกการไม่บังคับใช้ว่าอย่างไร
Laurence Gonsalves

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

3

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


@Laurence - ไม่ชัดเจนสำหรับฉันว่าสามารถรวมฟิลด์นามธรรมได้ สิ่งนั้นน่าจะมีผลกระทบอย่างลึกซึ้งและเป็นพื้นฐานต่อระบบประเภทและความสามารถในการใช้ภาษา (ในทำนองเดียวกันกับการสืบทอดหลาย ๆ ปัญหาทำให้เกิดปัญหาลึก ๆ ... ที่สามารถกัดโปรแกรมเมอร์ C ++ ที่ไม่ระมัดระวังได้)
Stephen C

0

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

ฉันมักจะหวังว่าจะสามารถประกาศวิธีการดังต่อไปนี้ใน Java:

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

โดยพื้นฐานแล้วฉันต้องการยืนยันว่าการใช้งานคลาสแม่ของฉันอย่างเป็นรูปธรรมมีวิธีการโรงงานแบบคงที่พร้อมลายเซ็นเฉพาะ สิ่งนี้จะช่วยให้ฉันได้รับการอ้างอิงถึงคลาสที่เป็นรูปธรรมClass.forName()และมั่นใจได้ว่าฉันสามารถสร้างคลาสตามที่ฉันเลือกได้


1
เออ ... นี่คงหมายความว่าคุณใช้การสะท้อนไปไกลมาก !!
Stephen C

ฟังดูเป็นนิสัยการเขียนโปรแกรมที่แปลกสำหรับฉัน Class.forName (.. ) มีกลิ่นเหมือนข้อบกพร่องในการออกแบบ
วิสกี้เซียร่า

ทุกวันนี้เรามักจะใช้ Spring DI เพื่อทำสิ่งนี้ให้สำเร็จ แต่ก่อนที่เฟรมเวิร์กจะเป็นที่รู้จักและเป็นที่นิยมเราจะระบุคลาสการใช้งานในคุณสมบัติหรือไฟล์ XML จากนั้นใช้ Class.forName () เพื่อโหลด
Drew Wills

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

0

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

public abstract class Base {
  public final int field;
  public Base() {
    if (this instanceof SubClassOne) {
      field = 1;
    } else if (this instanceof SubClassTwo) {
      field = 2;
    } else {
      // assertion, thrown exception, set to -1, whatever you want to do 
      // to trigger an error
      field = -1;
    }
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.