"ฉันจะบอกคุณเกี่ยวกับ « ตัวดัดแปลงการเข้าถึง » ฉันเคยบอกไปแล้วครั้งหนึ่ง แต่การทำซ้ำๆ เป็นเสาหลักของการเรียนรู้"
คุณสามารถควบคุมการเข้าถึง (การมองเห็น) ที่คลาสอื่นมีต่อเมธอดและตัวแปรของคลาสของคุณ ตัวแก้ไขการเข้าถึงตอบคำถาม «ใครสามารถเข้าถึงเมธอด/ตัวแปรนี้ได้บ้าง» คุณสามารถระบุตัวแก้ไขได้เพียงตัวเดียวสำหรับแต่ละเมธอดหรือตัวแปร
1) ตัวแก้ไข « สาธารณะ »
ตัวแปร เมธอด หรือคลาสที่มี ตัวปรับแต่ง สาธารณะสามารถเข้าถึงได้จากทุกที่ในโปรแกรม นี่คือระดับสูงสุดของการเปิดกว้าง: ไม่มีข้อ จำกัด
2) ตัวแก้ไข « ส่วนตัว »
ตัวแปร เมธอด หรือคลาสที่ทำเครื่องหมายด้วย ตัวดัดแปลง ส่วนตัวสามารถเข้าถึงได้เฉพาะในคลาสที่มีการประกาศเท่านั้น เมธอดหรือตัวแปรที่ทำเครื่องหมายถูกซ่อนจากคลาสอื่นๆ ทั้งหมด นี่เป็นระดับความเป็นส่วนตัวสูงสุด: ชั้นเรียนของคุณเท่านั้นที่เข้าถึงได้ วิธีการดังกล่าวไม่ได้รับการสืบทอดและไม่สามารถแทนที่ได้ นอกจากนี้ยังไม่สามารถเข้าถึงได้ในคลาสที่สืบทอดมา
3) « ตัวดัดแปลงเริ่ม ต้น ».
หากตัวแปรหรือเมธอดไม่ถูกทำเครื่องหมายด้วยตัวแก้ไขใดๆ จะถือว่าถูกทำเครื่องหมายด้วยตัวดัดแปลง "ดีฟอลต์" ตัวแปรและเมธอดที่มีตัวแก้ไขนี้สามารถมองเห็นได้ในทุกคลาสในแพ็คเกจที่มีการประกาศ และเฉพาะคลาสเหล่านั้นเท่านั้น โมดิฟายเออร์นี้เรียกอีกอย่างว่าการเข้าถึง " แพ็คเกจ " หรือ " แพ็คเกจส่วนตัว " โดยบอกเป็นนัยว่าการเข้าถึงตัวแปรและเมธอดนั้นเปิดให้ทั้งแพ็คเกจที่มีคลาสอยู่
4) ตัวแก้ไข « ป้องกัน »
การเข้าถึงระดับนี้กว้างกว่าpackageเล็กน้อย ตัวแปร เมธอด หรือคลาสที่ทำเครื่องหมายด้วย ตัวแก้ไข ที่ได้รับการป้องกันสามารถเข้าถึงได้จากแพ็คเกจของมัน (เช่น "แพ็คเกจ") และจากคลาสที่สืบทอดมาทั้งหมด
ตารางนี้อธิบายทั้งหมด:
ประเภทของการมองเห็น | คำสำคัญ | เข้าถึง | |||
---|---|---|---|---|---|
ชั้นเรียนของคุณ | แพ็คเกจของคุณ | ลูกหลาน | ทุกชั้นเรียน | ||
ส่วนตัว | ส่วนตัว | ใช่ | เลขที่ | เลขที่ | เลขที่ |
บรรจุุภัณฑ์ | (ไม่มีตัวดัดแปลง) | ใช่ | ใช่ | เลขที่ | เลขที่ |
มีการป้องกัน | มีการป้องกัน | ใช่ | ใช่ | ใช่ | เลขที่ |
สาธารณะ | สาธารณะ | ใช่ | ใช่ | ใช่ | ใช่ |
มีวิธีจำตารางนี้ง่ายๆ ลองนึกภาพว่าคุณกำลังเขียนพินัยกรรม คุณกำลังแบ่งสิ่งของทั้งหมดของคุณออกเป็นสี่ประเภท ใครจะใช้สิ่งของของคุณ?
ใครเข้าได้ | ตัวดัดแปลง | ตัวอย่าง |
---|---|---|
แค่ ฉัน | ส่วนตัว | บันทึกส่วนตัว |
ตระกูล | (ไม่มีตัวดัดแปลง) | รูปถ่ายครอบครัว |
ครอบครัวและทายาท | มีการป้องกัน | ทรัพย์สินของครอบครัว |
ทุกคน | สาธารณะ | บันทึกความทรงจำ |
"มันเหมือนกับจินตนาการว่าชั้นเรียนในแพ็คเกจเดียวกันเป็นส่วนหนึ่งของครอบครัวเดียวกัน"
"ฉันอยากจะบอกคุณถึงความแตกต่างที่น่าสนใจบางประการเกี่ยวกับวิธีการเอาชนะ"
1) การดำเนินการโดยนัยของวิธีการนามธรรม
สมมติว่าคุณมีรหัสต่อไปนี้:
class Cat
{
public String getName()
{
return "Oscar";
}
}
และคุณตัดสินใจสร้างคลาส Tiger ที่สืบทอดคลาสนี้ และเพิ่มอินเทอร์เฟซให้กับคลาสใหม่
class Cat
{
public String getName()
{
return "Oscar";
}
}
interface HasName
{
String getName();
int getWeight();
}
class Tiger extends Cat implements HasName
{
public int getWeight()
{
return 115;
}
}
หากคุณเพิ่งใช้วิธีการที่ขาดหายไปทั้งหมดที่ IntelliJ IDEA บอกให้คุณนำไปใช้ คุณอาจต้องใช้เวลานานในการค้นหาจุดบกพร่องในภายหลัง
ปรากฎว่าคลาส Tiger มีเมธอด getName ที่สืบทอดมาจาก Cat ซึ่งจะใช้เป็นเมธอด getName สำหรับอินเทอร์เฟซ HasName
"ไม่เห็นมีอะไรน่ากลัวเลย"
“มันไม่เลวร้ายเกินไป มันมีโอกาสที่ความผิดพลาดจะเล็ดลอดเข้ามา”
แต่อาจแย่กว่านั้น:
interface HasWeight
{
int getValue();
}
interface HasSize
{
int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
public int getValue()
{
return 115;
}
}
ปรากฎว่าคุณไม่สามารถสืบทอดจากหลายอินเทอร์เฟซได้เสมอไป คุณสามารถสืบทอดได้ แต่คุณไม่สามารถนำไปใช้ได้อย่างถูกต้อง ดูตัวอย่าง อินเทอร์เฟซทั้งสองต้องการให้คุณใช้เมธอด getValue() แต่ไม่ชัดเจนว่าควรส่งคืนอะไร: น้ำหนักหรือขนาด สิ่งนี้ค่อนข้างไม่เป็นที่พอใจที่จะต้องจัดการ
"ฉันเห็นด้วย คุณต้องการนำเมธอดไปใช้ แต่คุณทำไม่ได้ คุณได้สืบทอดเมธอดที่มีชื่อเดียวกันมาจากคลาสพื้นฐานแล้ว มันใช้งานไม่ได้"
"แต่มีข่าวดี"
2) ขยายการมองเห็น เมื่อคุณสืบทอดประเภท คุณสามารถขยายการมองเห็นของวิธีการได้ นี่คือลักษณะ:
รหัสจาวา | คำอธิบาย |
---|---|
|
|
|
เราได้ขยายการมอง เห็นของเมธอดจากprotected เป็นpublic |
รหัส | ทำไมถึงเป็น «ถูกกฎหมาย» |
---|---|
|
ทุกอย่างยอดเยี่ยม ที่นี่เราไม่รู้ด้วยซ้ำว่าการมองเห็นได้ถูกขยายออกไปในคลาสที่สืบทอดมา |
|
ที่นี่เราเรียกวิธีการที่มีการขยายการมองเห็น
หากไม่สามารถทำได้ เราสามารถประกาศเมธอดใน Tiger ได้เสมอ: กล่าวอีกนัยหนึ่ง เราไม่ได้พูดถึงการละเมิดความปลอดภัยใดๆ |
|
หากเงื่อนไขทั้งหมดที่จำเป็นในการเรียกใช้เมธอดในคลาสพื้นฐาน ( Cat ) เป็นไปตามเงื่อนไข เงื่อนไขเหล่านั้นจะพึงพอใจอย่างแน่นอนสำหรับการเรียกใช้เมธอดในประเภทลูกหลาน ( Tiger ) เนื่องจากข้อ จำกัด ในการเรียกใช้เมธอดอ่อนแอไม่แข็งแรง |
"ฉันไม่แน่ใจว่าฉันเข้าใจทั้งหมด แต่ฉันจะจำไว้ว่าสิ่งนี้เป็นไปได้"
3) จำกัด ประเภทผลตอบแทนให้แคบลง
ในวิธีการแทนที่ เราสามารถเปลี่ยนประเภทการส่งคืนเป็นประเภทการอ้างอิงที่แคบลงได้
รหัสจาวา | คำอธิบาย |
---|---|
|
|
|
เราลบล้างเมธอดgetMyParent และตอนนี้เมธอดจะส่งคืนTiger ออบเจกต์ |
รหัส | ทำไมถึงเป็น «ถูกกฎหมาย» |
---|---|
|
ทุกอย่างยอดเยี่ยม ที่นี่เราไม่รู้ด้วยซ้ำว่าประเภทการส่งคืนของเมธอด getMyParent นั้นกว้างขึ้นในคลาสที่สืบทอดมา
«รหัสเก่า» ทำงานและทำงานอย่างไร |
|
ที่นี่เราเรียกเมธอดที่ประเภทผลตอบแทนถูกจำกัดให้แคบลง
หากไม่สามารถทำได้ เราสามารถประกาศวิธีการใน Tiger ได้เสมอ: กล่าวอีกนัยหนึ่งคือไม่มีการละเมิดความปลอดภัยและ/หรือการละเมิดการหล่อประเภท |
|
และทุกอย่างทำงานได้ดีที่นี่ แม้ว่าเราจะขยายประเภทของตัวแปรเป็นคลาสพื้นฐาน (Cat)
เนื่องจากการแทนที่ เมธอด setMyParent ที่ถูกต้องจึงถูกเรียก และไม่มีอะไรต้องกังวลเมื่อเรียกใช้เมธอด getMyParentเนื่องจากค่าที่ส่งคืนแม้จะเป็นคลาส Tiger ก็ยังสามารถกำหนดให้กับตัวแปร myParentของคลาสพื้นฐาน (Cat) ได้ โดยไม่มีปัญหาใดๆ วัตถุเสือสามารถจัดเก็บได้อย่างปลอดภัยทั้งในตัวแปร Tiger และตัวแปร Cat |
"ใช่ เข้าใจแล้ว เมื่อแทนที่เมธอด คุณต้องรู้ว่าทั้งหมดนี้ทำงานอย่างไร หากเราส่งอ็อบเจกต์ของเราไปยังโค้ดที่สามารถจัดการได้เฉพาะคลาสพื้นฐานและไม่รู้อะไรเลยเกี่ยวกับคลาสของเรา "
"แน่นอน! คำถามใหญ่ก็คือทำไมเราไม่สามารถจำกัดประเภทของค่าส่งคืนให้แคบลงได้เมื่อแทนที่เมธอด"
"เห็นได้ชัดว่าในกรณีนี้โค้ดในคลาสพื้นฐานจะหยุดทำงาน:"
รหัสจาวา | คำอธิบายของปัญหา |
---|---|
|
|
|
เราโอเวอร์โหลดเมธอด getMyParent และทำให้ประเภทของค่าส่งคืนแคบลง
ทุกอย่างเรียบร้อยดีที่นี่ |
|
จากนั้นรหัสนี้จะหยุดทำงาน
เมธอด getMyParent สามารถส่งคืนอินสแตนซ์ใดๆ ของอ็อบเจกต์ได้ เพราะมันถูกเรียกใช้บนออบเจกต์ Tiger และเราไม่มีการตรวจสอบก่อนการมอบหมายงาน ดังนั้นจึงเป็นไปได้โดยสิ้นเชิงที่ตัวแปร myParent ประเภท Cat จะจัดเก็บการอ้างอิงสตริง |
"ตัวอย่างที่ยอดเยี่ยม Amigo!"
ใน Java ก่อนที่จะเรียกใช้เมธอด จะไม่มีการตรวจสอบว่าอ็อบเจกต์มีเมธอดดังกล่าวหรือไม่ การตรวจสอบทั้งหมดเกิดขึ้นในรันไทม์ และการเรียก [สมมุติฐาน] ไปยังเมธอดที่ขาดหายไปมักจะทำให้โปรแกรมพยายามเรียกใช้ bytecode ที่ไม่มีอยู่จริง สิ่งนี้จะนำไปสู่ข้อผิดพลาดร้ายแรงในที่สุด และระบบปฏิบัติการจะบังคับปิดโปรแกรม
“โอ้ ตอนนี้ฉันรู้แล้ว”
GO TO FULL VERSION