Java: การเรียกเมธอดที่ได้รับการป้องกันของคลาส super จากคลาสย่อย - ไม่สามารถมองเห็นได้?

ฉันกำลังเรียกวิธีการป้องกันของคลาสซุปเปอร์จากคลาสย่อย เหตุใดวิธีนี้จึง "มองไม่เห็น"

ฉันได้อ่านโพสต์บางโพสต์เช่นอันนี้ที่ดูเหมือนว่า ที่จะโต้แย้งดังต่อไปนี้:

ซูเปอร์คลาส:

package com.first;

public class Base
{
    protected void sayHello()
    {
        System.out.println("hi!");
    }
}

คลาสย่อย:

package com.second;

import com.first.Base;

public class BaseChild extends Base
{
    Base base = new Base();

    @Override
    protected void sayHello()
    {
        super.sayHello(); //OK :)
        base.sayHello(); //Hmmm... "The method sayHello() from the type Base is not visible" ?!?
    }   
}

person rapt    schedule 24.03.2016    source แหล่งที่มา
comment
stackoverflow.com/a/19949354/868975   -  person Bax    schedule 24.03.2016
comment
เช่นเดียวกับอันนี้บางที: stackoverflow.com/questions/36093187/   -  person aioobe    schedule 24.03.2016


คำตอบ (3)


base เป็นตัวแปรที่ไม่ได้พิเศษ แต่อย่างใด: ไม่ได้เป็นส่วนหนึ่งของลำดับชั้นของคลาสและการเข้าถึงที่ได้รับการป้องกันไม่สามารถใช้งานได้ แม้ว่า sayHello จะสามารถเข้าถึงสมาชิกที่ได้รับการป้องกันของ Base ได้ แต่สามารถเข้าถึงได้ผ่านการสืบทอดเท่านั้น (เนื่องจากไม่ได้อยู่ในแพ็คเกจเดียวกัน: คีย์เวิร์ด protected อนุญาตให้เข้าถึงผ่านการสืบทอดและแพ็คเกจ โปรดดูตารางใน บทช่วยสอนของ Oracle นี้)

อนุญาตให้เข้าถึงผ่าน this และ super เนื่องจากเป็นส่วนหนึ่งของลำดับชั้นการสืบทอด

person Paul Hicks    schedule 24.03.2016
comment
ฉันคิดว่าคุณเป็นคนเดียวที่นี่ที่สังเกตเห็นว่า base เป็นเพียงตัวแปร - person Scary Wombat; 24.03.2016
comment
ฉันคิดว่าฉันต้องการคำศัพท์ที่ดีกว่านี้.. ฉันรู้ว่าฉันกำลังพยายามจะพูดอะไร แต่ฉันคิดว่ายังมีคำศัพท์ที่ได้รับการยอมรับดีกว่าซึ่งฉันจำไม่ได้ในขณะนี้.. - person Paul Hicks; 24.03.2016
comment
สิ่งนี้ยังคงน่าประหลาดใจ เนื่องจากคุณสามารถเรียกใช้เมธอดส่วนตัวบนอินสแตนซ์ใดๆ ของคลาสที่ประกาศในปัจจุบัน (this และอินสแตนซ์อื่นๆ ที่ไม่ใช่ this) รหัสต่อไปนี้ใช้งานได้: public class A { private void b() { new A().b(); }} - person Stefan Dollase; 24.03.2016
comment
@StefanDollase นั่นอยู่ข้างใน A แน่นอนว่าวิธีการส่วนตัวของ A นั้นใช้ได้กับ A - person ChiefTwoPencils; 24.03.2016
comment
@ChiefTwoPencils ใช่ แต่ยังคงเป็นอาร์กิวเมนต์เดียวกัน: คำตอบบอกว่ามันใช้งานไม่ได้เพราะมันเป็นลำดับชั้นการสืบทอดเดียวกัน แต่ไม่ใช่อินสแตนซ์เดียวกัน ฉันแสดงให้เห็นว่าในกรณีของตัวแก้ไข private ก็เพียงพอที่จะอยู่ในลำดับชั้นการสืบทอดเดียวกัน (ประกอบด้วยเฉพาะประเภทเท่านั้น) สำหรับ private ไม่สำคัญว่าจะเป็นอินสแตนซ์เดียวกันหรือไม่ ฉันแค่ชี้ให้เห็นว่าฉันรู้สึกประหลาดใจกับพฤติกรรมที่แตกต่าง - person Stefan Dollase; 24.03.2016
comment
ข้อแตกต่างคือเป็นคลาสเดียวกัน ไม่มีคลาสย่อยที่เกี่ยวข้อง ดูคำพูดของ dasblinkenlight สำหรับรายละเอียดที่เฉพาะเจาะจงมากขึ้น - person Paul Hicks; 24.03.2016
comment
@StefanDollase ฉันอ่านมันแตกต่างออกไป ฟังดูถูกต้องและไม่เหมือนกับตัวอย่างของคุณ ข้อแตกต่างที่สำคัญคือ OP พยายามใช้ อินสแตนซ์ ของพาเรนต์ราวกับว่าเป็น this ถ้าเป็นคลาสอื่นก็คงจะน่าแปลกใจมาก - person ChiefTwoPencils; 24.03.2016
comment
เพื่อให้ชัดเจน: ฉันไม่แปลกใจกับพฤติกรรมของ private แต่พฤติกรรมของ protected ฉันคาดหวังว่าตัวดัดแปลง protected จะมีพฤติกรรมคล้ายกับ private เมื่อเทียบกับสถานการณ์ที่อธิบายไว้ข้างต้น อย่างไรก็ตาม ดูเหมือนฉันจะเป็นคนเดียวที่แปลกใจ ไม่เป็นไร :-P - person Stefan Dollase; 24.03.2016
comment
@ChiefTwoPencils แต่คุณจะเข้าถึงของส่วนตัวในชั้นเรียนอื่นได้อย่างไร ฉันเห็นด้วยกับ Stefan Dollase ว่ามันค่อนข้างไม่สอดคล้องกัน - person rapt; 24.03.2016
comment
@rapt การออกแบบไม่สอดคล้องกัน คุณ ไม่ เข้าถึงสิ่งของส่วนตัว (โดยตรงหรือไม่มีเวทย์มนตร์) ในคลาสอื่นไม่ว่าจะสืบทอดมาหรือไม่ก็ตาม มันจะไม่เป็นส่วนตัวแล้ว หากวิธีการทำงานขัดกับการออกแบบของคุณ การออกแบบอาจต้องอาศัยการคิดเพิ่มเติม - person ChiefTwoPencils; 24.03.2016
comment
@ChiefTwoPencils ฉันไม่ได้พูดถึงการเข้าถึงของส่วนตัวจากประเภทอื่น ฉันคิดว่าคุณยังพลาดประเด็นของฉัน: สำหรับ private คุณต้องอยู่ในคลาสการประกาศเดียวกัน แต่ คุณสามารถใช้ private สิ่งจากอินสแตนซ์อื่น ที่ไม่ใช่ this สำหรับ protected คุณจะต้องอยู่ในลำดับชั้นของคลาสเดียวกันกับคลาสที่ประกาศ และ คุณสามารถใช้ได้ เท่านั้น protected เนื้อหาจาก this - person Stefan Dollase; 24.03.2016
comment
@StefanDollase ขออภัยฉันเข้าใจสิ่งที่คุณพูด แต่ไม่เห็นว่ามีอะไรน่าประหลาดใจเกี่ยวกับเรื่องนี้ แม้ว่าคุณจะมีอินสแตนซ์อื่นของชั้นเรียน แต่คุณก็ยังอยู่ในชั้นเรียนเดียวกัน แม้ว่าสิ่งนั้นอาจดูไม่สอดคล้องกัน และบางทีตามคำจำกัดความแล้ว มันก็สมเหตุสมผลดี (สำหรับฉัน) นอกเหนือจากการอยู่ในชั้นเรียนที่รับรองว่าไม่มีการละเมิดความเป็นส่วนตัวแล้ว บางสิ่งเช่น คำจำกัดความแบบเรียกซ้ำ อาจเป็นไปไม่ได้หรือยากโดยไม่จำเป็น ลองนึกถึง BinaryTreeNode ที่ไม่สามารถเข้าถึงข้อมูลส่วนตัวที่ผู้อ้างอิงเก็บไว้ นั่นคงจะน่าตกใจสำหรับฉัน ทุกความคิดเห็น :) - person ChiefTwoPencils; 24.03.2016
comment
@ChiefTwoPencils อย่างที่ฉันบอกไปแล้วฉันไม่แปลกใจกับพฤติกรรมของความเป็นส่วนตัว แต่เป็นพฤติกรรมของการป้องกัน ดังนั้นคำถามไม่ใช่ว่าทำไมจึงอนุญาตให้มีความเป็นส่วนตัว แต่ทำไมจึงไม่ได้รับอนุญาตให้มีการป้องกัน มาถ่ายโอนตัวอย่างอัลกอริทึมของคุณซึ่งใช้วิธีการส่วนตัวบนอินสแตนซ์อื่นที่ไม่ใช่ this ไปยังเวอร์ชันที่ได้รับการป้องกัน: เหตุใดฉันจึงไม่สามารถใช้อัลกอริทึมซึ่งใช้วิธีการที่ได้รับการป้องกันจากลำดับชั้นการสืบทอดปัจจุบัน แต่ในอินสแตนซ์อื่นที่ไม่ใช่ this เหตุใดฉันจึงไม่สามารถเข้าถึงวิธีการที่ได้รับการป้องกันของคลาสพาเรนต์ของโหนดลูกใน BinaryTreeNode ได้ - person Stefan Dollase; 24.03.2016
comment
@ChiefTwoPencils ฉันจะหยุดสิ่งนี้ที่นี่เนื่องจากนี่เป็นหัวข้อสำหรับคำถามอื่น ไชโย! - person Stefan Dollase; 24.03.2016
comment
@ Stefan สำหรับบันทึก ฉันใช้โทรศัพท์และพลาดความคิดเห็นที่ชี้แจงของคุณ ขออภัย ไว้คราวหน้า... - person ChiefTwoPencils; 24.03.2016

นี่คือพฤติกรรมที่ถูกต้อง ที่จริงแล้ว ข้อกำหนดภาษา Java ส่วน 6.6.2-1 มีตัวอย่างที่คล้ายคลึงกับของคุณมาก โดยมีความคิดเห็นที่ไม่ควรคอมไพล์

ข้อมูลเฉพาะของการเข้าถึงสมาชิกที่ได้รับการคุ้มครองมีรายละเอียดในส่วน 6.6.2.1:

6.6.2.1. การเข้าถึงสมาชิกที่ได้รับการคุ้มครอง

ให้ C เป็นคลาสที่มีการประกาศสมาชิกที่ได้รับการคุ้มครอง อนุญาตให้เข้าถึงได้ภายในเนื้อหาของคลาสย่อย S จาก C เท่านั้น

นอกจากนี้ หาก Id หมายถึงฟิลด์อินสแตนซ์หรือวิธีการอินสแตนซ์ ดังนั้น:

หากการเข้าถึงเป็นชื่อที่ผ่านการรับรอง Q.Id โดยที่ Q คือ ExpressionName ดังนั้น การเข้าถึงจะได้รับอนุญาตก็ต่อเมื่อชนิดของนิพจน์ Q คือ S หรือคลาสย่อยเป็น S

หากการเข้าถึงเป็นไปตามนิพจน์การเข้าถึงฟิลด์ E.Id โดยที่ E เป็นนิพจน์หลัก หรือโดยนิพจน์การเรียกใช้เมธอด E.Id(. . .) โดยที่ E เป็นนิพจน์หลัก การเข้าถึงจะได้รับอนุญาตหากและเฉพาะในกรณีที่ประเภทของ E คือ S หรือ คลาสย่อยของ S

เป็นย่อหน้าสุดท้ายที่อธิบายว่าทำไมการเข้าถึงจึงควรถูกปฏิเสธ ในตัวอย่างของคุณ C คือ Base, S คือ BaseChild และ E ซึ่งเป็นประเภทของตัวแปร base ก็คือ Base เช่นกัน เนื่องจาก Base ไม่ใช่ทั้ง BaseChild หรือคลาสย่อยของ BaseChild การเข้าถึงจึงถูกปฏิเสธ

person Sergey Kalinichenko    schedule 24.03.2016
comment
แม้ว่าฉันจะเข้าใจว่าการทำเช่นนี้จะป้องกันการเข้าถึงผ่าน base ฉันไม่เห็นว่ามันจะอนุญาตการเข้าถึงผ่าน super ได้อย่างไร super ไม่ใช่ประเภทเดียวกันกับ base ใช่หรือไม่ บางทีฉันอาจพลาดบางสิ่งบางอย่างใน JLS? ได้รับอนุญาตในส่วนอื่นของ JLS หรือไม่ ฉันไม่พบส่วนที่อนุญาต - person Stefan Dollase; 24.03.2016
comment
@StefanDollase นั่นคือสิ่งที่ super ไม่ใช่ฟิลด์ มันเป็นคำหลักที่ช่วยให้คุณเข้าถึงวิธีการของคลาสพื้นฐานที่ถูกแทนที่ในชั้นเรียนของคุณ อย่างไรก็ตาม การอ้างอิงจริงจะดำเนินการผ่าน this ไม่ใช่ผ่านตัวแปรอื่น - person Sergey Kalinichenko; 24.03.2016

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

person Dust Francis    schedule 24.03.2016
comment
เพื่ออธิบายการโหวตที่ไม่เห็นด้วย: แป้นพิมพ์เป็นอุปกรณ์ในการป้อน ตัวอักษรลงในคอมพิวเตอร์ protected มีไว้สำหรับการมองเห็นบนแพ็คเกจเดียวกัน - ในแพ็คเกจเดียวกัน แต่ฉันพูดนอกเรื่อง protected ส่วนใหญ่จะใช้เพื่ออนุญาตการเข้าถึงคลาสย่อย หากคลาสลูกไม่ใช่แพ็คเกจเดียวกัน - ในคลาสเดียวกัน... วิธีการป้องกันของพาเรนต์จะไม่ปรากฏแก่คลาสลูก ไม่ การเข้าถึงจากคลาสย่อยคือจุดรวมของ protected คุณอาจหมายถึงการมองเห็นเริ่มต้น ซึ่งการเข้าถึง protected รวมอยู่ด้วย - person Johannes Kuhn; 15.07.2018