วิธีการคงที่ได้รับมรดกใน Java?


142

ฉันอ่านคู่มือโปรแกรมเมอร์ของ Java ™ SCJP Certificationโดย Khalid Mughal

ในบทที่สืบทอดมันอธิบายว่า

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

นอกจากนี้ยังกล่าวถึงวิธีการคงที่ไม่ได้รับการสืบทอด แต่โค้ดด้านล่างนี้ใช้ได้อย่างสมบูรณ์แบบ:

class A
{
    public static void display()
    {
        System.out.println("Inside static method of superclass");
    }
}

class B extends A
{
    public void show()
    {
        // This works - accessing display() by its simple name -
        // meaning it is inherited according to the book.
        display();
    }
}

ฉันสามารถใช้display()ในชั้นเรียนได้โดยตรงBอย่างไร ยิ่งกว่านั้นB.display()ยังใช้งานได้

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


stackoverflow.com/questions/4716040/…มีข้อมูลที่น่าสนใจ
Mat

นั่นไม่ใช่สิ่งที่กล่าวในสำเนาฉบับที่ 1 ของฉัน โปรดระบุใบเสนอราคาจริง
มาร์ควิสแห่ง Lorne

เกี่ยวข้อง: stackoverflow.com/questions/25169175/…
jaco0646

คำตอบ:


179

เมธอดทั้งหมดที่สามารถเข้าถึงได้นั้นสืบทอดโดยคลาสย่อย

จากแบบฝึกหัด Sun Java :

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

ความแตกต่างเพียงอย่างเดียวกับเมธอด static (class) ที่สืบทอดมาและเมธอด non-static (เช่น) ที่สืบทอดมานั้นคือเมื่อคุณเขียนวิธีสแตติกใหม่ที่มีลายเซ็นเหมือนกันเมธอดสแตติกเก่าจะถูกซ่อนไว้เท่านั้น

จากหน้าเกี่ยวกับความแตกต่างระหว่างการเอาชนะและการซ่อน

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


สืบทอดมาเกี่ยวข้องกับว่าเราสามารถแทนที่สมาชิกในคลาสย่อย?
อัลกอริทึม

นั่นเป็นส่วนหนึ่งของมรดก แต่ไม่ใช่ทั้งหมด ฉันจะบอกว่าส่วนที่สำคัญอื่น ๆ ของการสืบทอดคือการใช้ซ้ำรหัสและความหลากหลาย
yincrash

นิยามใหม่ยังมีกฎบางอย่างเช่นการเอาชนะได้หรือไม่
Surak Thakran

2
@ อัลกอริทึมไม่แน่นอน ทุกสิ่งที่ subclass ของคุณเห็นเพิ่มเติมในลำดับชั้นคือสิ่งที่ชั้นเรียนของคุณสืบทอด แต่วิธีการคงที่ซึ่งสืบทอดมาไม่สามารถถูกแทนที่ถูกซ่อนอยู่เท่านั้น ("redeclared" ที่มีลายเซ็นเดียวกัน) ดังนั้นคุณอาจประกาศวิธีคงที่ของคุณเป็นขั้นตอนสุดท้ายก็ไม่สำคัญ วิธีการแบบคงที่ซึ่งจะถูกเรียกเป็นที่รู้จักกันในเวลารวบรวม ด้วยวิธีการที่ไม่ใช่ขั้นตอนสุดท้ายการแก้ปัญหาจะต้องรอการตัดบัญชีเป็นรันไทม์เนื่องจากอาจถูกแทนที่ได้
Martin Andersson

1
ย่อหน้าสุดท้ายของคุณถูกทำลายอย่างสมบูรณ์ !!! "รุ่นของวิธี overriden ที่ถูกเรียกเป็นหนึ่งในคลาสย่อย" ซึ่งไม่เป็นความจริง: สมมุติว่า: รุ่นของวิธี overriden ที่ถูกเรียกนั้นถูกกำหนดใน runtime โดย JVM ที่เกี่ยวข้องกับวัตถุที่ทำ โทร :)
mounaim

14

ถ้านั่นคือสิ่งที่หนังสือพูดจริง ๆ มันผิด [1]

Java Language ข้อกำหนด # 8.4.8ฯ :

8.4.8 การสืบทอดการเอาชนะและการซ่อน

คลาส C สืบทอดมาจากซูเปอร์คลาสโดยตรงของเมธอดคอนกรีตทั้งหมด m (ทั้งคงที่และอินสแตนซ์) ของซูเปอร์คลาสซึ่งทั้งหมดต่อไปนี้เป็นจริง:

  • m เป็นสมาชิกของ direct superclass ของ C

  • m เป็นสาธารณะป้องกันหรือประกาศด้วยการเข้าถึงแพ็คเกจในแพ็คเกจเดียวกับ C

  • ไม่มีวิธีการประกาศใน C มีลายเซ็นที่เป็นลายเซ็นย่อย (§8.4.2) ของลายเซ็นของ m

[1] ไม่ได้หมายความว่าในสำเนาฉบับที่ 1 ของฉันปี 2000


13

คุณสามารถพบความแตกต่างในรหัสต่อไปนี้ซึ่งเป็นการปรับเปลี่ยนรหัสของคุณเล็กน้อย

class A {
    public static void display() {
        System.out.println("Inside static method of superclass");
    }
}

class B extends A {
    public void show() {
        display();
    }

    public static void display() {
        System.out.println("Inside static method of this class");
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        // prints: Inside static method of this class
        b.display();

        A a = new B();
        // prints: Inside static method of superclass
        a.display();
    }
}

นี่คือสาเหตุที่วิธีการคงที่เป็นวิธีการเรียน

A.display () และ B.display () จะเรียกใช้เมธอดของคลาสที่เกี่ยวข้อง


1
การเรียกใช้เมธอดแบบสแตติกในอินสแตนซ์ที่คุณพยายามจะไม่ทำงานในภาษาจาวา
Lucas C. Feijo

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

2
@ LucasC.Feijo ที่จริงฉันทำงานแล้ว อย่างน้อยใน IDE ของฉัน (eclipse) ฉันเพิ่งได้รับคำเตือน มันอาจจะไม่ใช่สไตล์ที่ดี แต่ ... แต่นั่นเป็นเรื่องที่แตกต่าง
dingalapadum

2
@ LucasC.Feijo ไม่แนะนำให้ใช้วิธีการแบบคงที่ในอินสแตนซ์ แต่มันทำงานเหมือนกับการเรียกใช้เมธอดสแตติกบนชื่อคลาส
Ziyang Zhang

5

B.display () ทำงานได้เนื่องจากการประกาศแบบคงที่ทำให้วิธีการ / สมาชิกเป็นสมาชิกของชั้นเรียนและไม่ได้อินสแตนซ์ชั้นเรียนที่เฉพาะเจาะจงใด ๆ (aka วัตถุ) คุณสามารถอ่านเพิ่มเติมได้ที่นี่

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


3

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

ตัวอย่าง:

public class Writer {
    public static void write() {
        System.out.println("Writing");
    }
}

public class Author extends Writer {
    public static void write() {
        System.out.println("Writing book");
    }
}

public class Programmer extends Writer {

    public static void write() {
        System.out.println("Writing code");
    }

    public static void main(String[] args) {
        Writer w = new Programmer();
        w.write();

        Writer secondWriter = new Author();
        secondWriter.write();

        Writer thirdWriter = null;
        thirdWriter.write();

        Author firstAuthor = new Author();
        firstAuthor.write();
    }
}

คุณจะได้รับสิ่งต่อไปนี้:

Writing
Writing
Writing
Writing book

2

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


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

2

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


การขยายคลาสพาเรนต์คือความสัมพันธ์ 'is-a' หากการเข้าถึงเป็นแบบส่วนตัวคุณสามารถใช้งานได้จากภายในชั้นเรียน 'ป้องกัน' รวมถึงคลาสและคลาสที่ได้รับในแพ็คเกจปัจจุบัน มีข้อผิดพลาดมากเกินไปที่นี่
มาร์ควิสแห่ง Lorne

0

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


0

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

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


0

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

public class A {

    public static void display(){
        System.out.println("in static method of A");
    }
}

public class B extends A {

    void show(){
        display();
    }

     public static void display(){
        System.out.println("in static method of B");
    }

}
public class Test {

    public static void main(String[] args){
        B obj =new B();
        obj.show();

        A a_obj=new B();
        a_obj.display();


    }


}

ในกรณีแรก o / p คือ "ในวิธีการแบบคงที่ของ B" # การแทนที่ที่ประสบความสำเร็จในกรณีที่ 2, o / p คือ "ในวิธีการคงที่ของ A" # วิธีแบบคงที่ - จะไม่พิจารณาความหลากหลาย


-1

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


2
ฉันไม่รู้ว่าทำไมคนลงคะแนนเสมอและไม่ให้เหตุผลลงคะแนน
surajs1n

คำตอบนี้เกี่ยวกับการเอาชนะ คำถามเกี่ยวกับการสืบทอด
มาร์ควิสแห่ง Lorne

-1

หลายคนเปล่งคำตอบออกมาเป็นคำพูด นี่คือคำอธิบายเพิ่มเติมในรหัส:

public class A {
    public static void test() {
        System.out.println("A");
    }
    public static void test2() {
        System.out.println("Test");
    }
}

public class B extends A {
    public static void test() {
        System.out.println("B");
    }
}

// Called statically
A.test();
B.test();
System.out.println();

// Called statically, testing static inheritance
A.test2();
B.test2();
System.out.println();

// Called via instance object
A a = new A();
B b = new B();
a.test();
b.test();
System.out.println();

// Testing inheritance via instance call
a.test2();
b.test2();
System.out.println();

// Testing whether calling static method via instance object is dependent on compile or runtime type
((A) b).hi();
System.out.println();

// Testing whether null instance works
A nullObj = null;
nullObj.hi();

ผล:

A
B

Test
Test

A
B

Test
Test

A

A

ดังนั้นนี่คือข้อสรุป:

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

1
รหัสไม่ได้เป็นคำอธิบายเป็นการสาธิต คำตอบสำหรับคำถามนี้ควรอ้างอิงการอ้างอิงเชิงบรรทัดฐานไม่ใช่เพียงแสดงพฤติกรรมของการติดตั้งที่ไม่ระบุ
มาร์ควิสแห่ง Lorne

-2

สมาชิกแบบสแตติกเป็นสมาชิกสากล พวกเขาสามารถเข้าถึงได้จากทุกที่


4
สามารถเข้าถึงได้จากทุกที่ตามตัวอักษรนั่นคือผิด: static! = scope คุณอาจต้องการชี้แจง :-)
kleopatra

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

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

@kleopatra - ปิดหัวข้อ java swing? จริง ๆ แล้วคนใช้สิ่งเหล่านี้ในวันนี้?
MasterJoe

@Pavan ลองโทรคงที่ส่วนตัวจากนอกชั้นเรียน มันจะไม่ทำงาน
Rakesh Yadav

-2

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


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