"ฉันจะบอกคุณเกี่ยวกับ « ตัวดัดแปลงการเข้าถึง » ฉันเคยบอกไปแล้วครั้งหนึ่ง แต่การทำซ้ำๆ เป็นเสาหลักของการเรียนรู้"

คุณสามารถควบคุมการเข้าถึง (การมองเห็น) ที่คลาสอื่นมีต่อเมธอดและตัวแปรของคลาสของคุณ ตัวแก้ไขการเข้าถึงตอบคำถาม «ใครสามารถเข้าถึงเมธอด/ตัวแปรนี้ได้บ้าง» คุณสามารถระบุตัวแก้ไขได้เพียงตัวเดียวสำหรับแต่ละเมธอดหรือตัวแปร

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) ขยายการมองเห็น เมื่อคุณสืบทอดประเภท คุณสามารถขยายการมองเห็นของวิธีการได้ นี่คือลักษณะ:

รหัสจาวา คำอธิบาย
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
เราได้ขยายการมอง เห็นของเมธอดจากprotectedเป็นpublic
รหัส ทำไมถึงเป็น «ถูกกฎหมาย»
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
ทุกอย่างยอดเยี่ยม ที่นี่เราไม่รู้ด้วยซ้ำว่าการมองเห็นได้ถูกขยายออกไปในคลาสที่สืบทอดมา
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
ที่นี่เราเรียกวิธีการที่มีการขยายการมองเห็น

หากไม่สามารถทำได้ เราสามารถประกาศเมธอดใน Tiger ได้เสมอ:
public String getPublicName()
{
super.getName(); // เรียกวิธีการป้องกัน
}

กล่าวอีกนัยหนึ่ง เราไม่ได้พูดถึงการละเมิดความปลอดภัยใดๆ

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
หากเงื่อนไขทั้งหมดที่จำเป็นในการเรียกใช้เมธอดในคลาสพื้นฐาน ( Cat ) เป็นไปตามเงื่อนไข เงื่อนไขเหล่านั้นจะพึงพอใจอย่างแน่นอนสำหรับการเรียกใช้เมธอดในประเภทลูกหลาน ( Tiger ) เนื่องจากข้อ จำกัด ในการเรียกใช้เมธอดอ่อนแอไม่แข็งแรง

"ฉันไม่แน่ใจว่าฉันเข้าใจทั้งหมด แต่ฉันจะจำไว้ว่าสิ่งนี้เป็นไปได้"

3) จำกัด ประเภทผลตอบแทนให้แคบลง

ในวิธีการแทนที่ เราสามารถเปลี่ยนประเภทการส่งคืนเป็นประเภทการอ้างอิงที่แคบลงได้

รหัสจาวา คำอธิบาย
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Tiger getMyParent()
 {
  return (Tiger) this.parent;
 }
}
เราลบล้างเมธอดgetMyParentและตอนนี้เมธอดจะส่งคืนTigerออบเจกต์
รหัส ทำไมถึงเป็น «ถูกกฎหมาย»
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
ทุกอย่างยอดเยี่ยม ที่นี่เราไม่รู้ด้วยซ้ำว่าประเภทการส่งคืนของเมธอด getMyParent นั้นกว้างขึ้นในคลาสที่สืบทอดมา

«รหัสเก่า» ทำงานและทำงานอย่างไร

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
ที่นี่เราเรียกเมธอดที่ประเภทผลตอบแทนถูกจำกัดให้แคบลง

หากไม่สามารถทำได้ เราสามารถประกาศวิธีการใน Tiger ได้เสมอ:
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

กล่าวอีกนัยหนึ่งคือไม่มีการละเมิดความปลอดภัยและ/หรือการละเมิดการหล่อประเภท

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
และทุกอย่างทำงานได้ดีที่นี่ แม้ว่าเราจะขยายประเภทของตัวแปรเป็นคลาสพื้นฐาน (Cat)

เนื่องจากการแทนที่ เมธอด setMyParent ที่ถูกต้องจึงถูกเรียก

และไม่มีอะไรต้องกังวลเมื่อเรียกใช้เมธอด getMyParentเนื่องจากค่าที่ส่งคืนแม้จะเป็นคลาส Tiger ก็ยังสามารถกำหนดให้กับตัวแปร myParentของคลาสพื้นฐาน (Cat) ได้ โดยไม่มีปัญหาใดๆ

วัตถุเสือสามารถจัดเก็บได้อย่างปลอดภัยทั้งในตัวแปร Tiger และตัวแปร Cat

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

"แน่นอน! คำถามใหญ่ก็คือทำไมเราไม่สามารถจำกัดประเภทของค่าส่งคืนให้แคบลงได้เมื่อแทนที่เมธอด"

"เห็นได้ชัดว่าในกรณีนี้โค้ดในคลาสพื้นฐานจะหยุดทำงาน:"

รหัสจาวา คำอธิบายของปัญหา
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Object getMyParent()
 {
  if (this.parent != null)
   return this.parent;
  else
   return "I'm an orphan";
 }
}
เราโอเวอร์โหลดเมธอด getMyParent และทำให้ประเภทของค่าส่งคืนแคบลง

ทุกอย่างเรียบร้อยดีที่นี่

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
จากนั้นรหัสนี้จะหยุดทำงาน

เมธอด getMyParent สามารถส่งคืนอินสแตนซ์ใดๆ ของอ็อบเจกต์ได้ เพราะมันถูกเรียกใช้บนออบเจกต์ Tiger

และเราไม่มีการตรวจสอบก่อนการมอบหมายงาน ดังนั้นจึงเป็นไปได้โดยสิ้นเชิงที่ตัวแปร myParent ประเภท Cat จะจัดเก็บการอ้างอิงสตริง

"ตัวอย่างที่ยอดเยี่ยม Amigo!"

ใน Java ก่อนที่จะเรียกใช้เมธอด จะไม่มีการตรวจสอบว่าอ็อบเจกต์มีเมธอดดังกล่าวหรือไม่ การตรวจสอบทั้งหมดเกิดขึ้นในรันไทม์ และการเรียก [สมมุติฐาน] ไปยังเมธอดที่ขาดหายไปมักจะทำให้โปรแกรมพยายามเรียกใช้ bytecode ที่ไม่มีอยู่จริง สิ่งนี้จะนำไปสู่ข้อผิดพลาดร้ายแรงในที่สุด และระบบปฏิบัติการจะบังคับปิดโปรแกรม

“โอ้ ตอนนี้ฉันรู้แล้ว”