"আমি আপনাকে « অ্যাক্সেস মডিফায়ার » ​​সম্পর্কে বলতে যাচ্ছি । আমি তাদের সম্পর্কে আগে একবার বলেছিলাম, কিন্তু পুনরাবৃত্তি শেখার একটি স্তম্ভ।"

আপনি আপনার ক্লাসের পদ্ধতি এবং ভেরিয়েবলগুলিতে অন্যান্য ক্লাসের অ্যাক্সেস (দৃশ্যমানতা) নিয়ন্ত্রণ করতে পারেন। একটি অ্যাক্সেস মডিফায়ার "কে এই পদ্ধতি/ভেরিয়েবল অ্যাক্সেস করতে পারে?" প্রশ্নের উত্তর দেয়। আপনি প্রতিটি পদ্ধতি বা ভেরিয়েবলের জন্য শুধুমাত্র একটি পরিবর্তনকারী নির্দিষ্ট করতে পারেন।

1) " সর্বজনীন " সংশোধক।

পাবলিক মডিফায়ার দিয়ে চিহ্নিত একটি পরিবর্তনশীল, পদ্ধতি বা শ্রেণী প্রোগ্রামের যেকোনো স্থান থেকে অ্যাক্সেস করা যেতে পারে। এটি উন্মুক্ততার সর্বোচ্চ ডিগ্রি: কোনও বিধিনিষেধ নেই।

2) " ব্যক্তিগত " সংশোধক।

প্রাইভেট মডিফায়ার দিয়ে চিহ্নিত একটি ভেরিয়েবল, পদ্ধতি বা ক্লাস শুধুমাত্র সেই ক্লাসে অ্যাক্সেস করা যেতে পারে যেখানে এটি ঘোষণা করা হয়েছে। চিহ্নিত পদ্ধতি বা পরিবর্তনশীল অন্য সব ক্লাস থেকে লুকানো হয়. এটি গোপনীয়তার সর্বোচ্চ ডিগ্রি: শুধুমাত্র আপনার ক্লাস দ্বারা অ্যাক্সেসযোগ্য। এই ধরনের পদ্ধতি উত্তরাধিকারসূত্রে পাওয়া যায় না এবং ওভাররাইড করা যায় না। উপরন্তু, তারা একটি বংশধর শ্রেণীতে অ্যাক্সেস করা যাবে না.

3)  " ডিফল্ট মডিফায়ার"।

যদি একটি পরিবর্তনশীল বা পদ্ধতি কোন পরিবর্তনকারীর সাথে চিহ্নিত না থাকে, তাহলে এটিকে "ডিফল্ট" সংশোধক দ্বারা চিহ্নিত করা বলে মনে করা হয়। এই মডিফায়ার সহ ভেরিয়েবল এবং পদ্ধতিগুলি প্যাকেজের সমস্ত ক্লাসের কাছে দৃশ্যমান যেখানে সেগুলি ঘোষণা করা হয়েছে এবং শুধুমাত্র সেই ক্লাসগুলিতে। এই সংশোধকটিকে " প্যাকেজ " বা " প্যাকেজ ব্যক্তিগত " অ্যাক্সেসও বলা হয় , যেটি এই ইঙ্গিত দেয় যে ভেরিয়েবল এবং পদ্ধতিতে অ্যাক্সেস পুরো প্যাকেজের জন্য উন্মুক্ত যেখানে ক্লাস রয়েছে।

4) " সুরক্ষিত " সংশোধক।

অ্যাক্সেসের এই স্তরটি প্যাকেজের চেয়ে কিছুটা বিস্তৃত । সুরক্ষিত সংশোধক দ্বারা চিহ্নিত একটি পরিবর্তনশীল, পদ্ধতি বা শ্রেণী তার প্যাকেজ (যেমন "প্যাকেজ") থেকে এবং সমস্ত উত্তরাধিকারী শ্রেণী থেকে অ্যাক্সেস করা যেতে পারে।

এই টেবিলটি সব ব্যাখ্যা করে:

দৃশ্যমানতার প্রকার কীওয়ার্ড অ্যাক্সেস
তোমার শ্রেণী আপনার প্যাকেজ বংশধর সব ক্লাস
ব্যক্তিগত ব্যক্তিগত হ্যাঁ না না না
প্যাকেজ (কোন সংশোধনকারী নেই) হ্যাঁ হ্যাঁ না না
সুরক্ষিত সুরক্ষিত হ্যাঁ হ্যাঁ হ্যাঁ না
পাবলিক পাবলিক হ্যাঁ হ্যাঁ হ্যাঁ হ্যাঁ

সহজে এই টেবিল মনে রাখার একটি উপায় আছে. কল্পনা করুন যে আপনি একটি উইল লিখছেন। আপনি আপনার সমস্ত জিনিসকে চারটি বিভাগে ভাগ করছেন। কে আপনার জিনিস ব্যবহার পায়?

যার প্রবেশাধিকার আছে পরিবর্তনকারী উদাহরণ
শুধু  আমি ব্যক্তিগত ব্যক্তিগত জার্নাল
পরিবার (কোন সংশোধনকারী নেই) পারিবারিক ছবি
পরিবার এবং উত্তরাধিকারী সুরক্ষিত পারিবারিক সম্পত্তি
সবাই পাবলিক স্মৃতিকথা

"এটি অনেকটা কল্পনা করার মতো যে একই প্যাকেজে ক্লাসগুলি একটি পরিবারের অংশ।"

"আমি আপনাকে ওভাররাইডিং পদ্ধতি সম্পর্কে কিছু আকর্ষণীয় সূক্ষ্মতাও বলতে চাই।"

1) একটি বিমূর্ত পদ্ধতির অন্তর্নিহিত বাস্তবায়ন।

ধরা যাক আপনার নিম্নলিখিত কোড আছে:

কোড
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

এবং আপনি একটি টাইগার ক্লাস তৈরি করার সিদ্ধান্ত নিয়েছেন যা এই ক্লাসের উত্তরাধিকারী, এবং নতুন ক্লাসে একটি ইন্টারফেস যোগ করুন

কোড
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 আপনাকে যে সমস্ত অনুপস্থিত পদ্ধতিগুলিকে বাস্তবায়ন করতে বলে আপনি যদি তা বাস্তবায়ন করেন, তাহলে পরবর্তীতে আপনি একটি বাগ খুঁজতে দীর্ঘ সময় ব্যয় করতে পারেন।

দেখা যাচ্ছে যে টাইগার ক্লাসে Cat থেকে উত্তরাধিকারসূত্রে প্রাপ্ত একটি getName পদ্ধতি রয়েছে, যা HasName ইন্টারফেসের জন্য getName পদ্ধতির বাস্তবায়ন হিসাবে নেওয়া হবে।

"আমি এতে ভয়ানক কিছু দেখছি না।"

"এটি খুব খারাপ নয়, এটি ভুলের জন্য একটি সম্ভাব্য জায়গা।"

কিন্তু এটি আরও খারাপ হতে পারে:

কোড
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();
}
এখানে আমরা সেই পদ্ধতিটিকে বলি যার দৃশ্যমানতা বাড়ানো হয়েছে।

যদি এটি সম্ভব না হয়, আমরা সবসময় টাইগারে একটি পদ্ধতি ঘোষণা করতে পারতাম:
পাবলিক স্ট্রিং getPublicName()
{
super.getName(); // সুরক্ষিত পদ্ধতিতে কল করুন
}

অন্য কথায়, আমরা কোনো নিরাপত্তা লঙ্ঘনের বিষয়ে কথা বলছি না।

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
যদি একটি বেস ক্লাসে একটি পদ্ধতি কল করার জন্য প্রয়োজনীয় সমস্ত শর্ত ( বিড়াল ) সন্তুষ্ট হয়, তবে তারা অবশ্যই বংশধর টাইপ ( বাঘ ) পদ্ধতিতে কল করার জন্য সন্তুষ্ট। কারণ পদ্ধতি কলের সীমাবদ্ধতা দুর্বল ছিল, শক্তিশালী ছিল না।

"আমি নিশ্চিত নই যে আমি পুরোপুরি বুঝতে পেরেছি, তবে আমি মনে রাখব যে এটি সম্ভব।"

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();
}
এখানে আমরা সেই পদ্ধতিটিকে বলি যার রিটার্ন টাইপ সংকীর্ণ করা হয়েছে।

যদি এটি সম্ভব না হয়, আমরা সবসময় টাইগারে একটি পদ্ধতি ঘোষণা করতে পারতাম:
পাবলিক টাইগার 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();
}
এবং এখানে সবকিছু ঠিকঠাক কাজ করে, যদিও আমরা ভেরিয়েবলের ধরনকে বেস ক্লাসে (বিড়াল) প্রশস্ত করেছি।

ওভাররাইডিংয়ের কারণে, সঠিক setMyParent পদ্ধতি বলা হয়।

এবং getMyParent পদ্ধতিতে কল করার সময় উদ্বিগ্ন হওয়ার কিছু নেই , কারণ রিটার্ন ভ্যালু, যদিও Tiger ক্লাসের, তবুও কোন সমস্যা ছাড়াই বেস ক্লাসের (Cat) myParent ভেরিয়েবলে বরাদ্দ করা যেতে পারে ।

টাইগার অবজেক্ট টাইগার ভেরিয়েবল এবং ক্যাট ভেরিয়েবল উভয়েই নিরাপদে সংরক্ষণ করা যেতে পারে।

"হ্যাঁ। বুঝেছি। পদ্ধতিগুলিকে ওভাররাইড করার সময়, আপনাকে সচেতন হতে হবে যে এই সমস্ত কীভাবে কাজ করে যদি আমরা আমাদের অবজেক্টগুলিকে কোডে পাস করি যা শুধুমাত্র বেস ক্লাস পরিচালনা করতে পারে এবং আমাদের ক্লাস সম্পর্কে কিছুই জানে না। "

"ঠিক আছে! তাহলে বড় প্রশ্ন হল কেন আমরা একটি পদ্ধতি ওভাররাইড করার সময় রিটার্ন মানের ধরণকে সংকুচিত করতে পারি না?"

"এটা স্পষ্ট যে এই ক্ষেত্রে বেস ক্লাসের কোড কাজ করা বন্ধ করবে:"

জাভা কোড সমস্যার ব্যাখ্যা
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 পদ্ধতিটি একটি অবজেক্টের যেকোন ইন্সট্যান্স ফিরিয়ে দিতে পারে, কারণ এটি আসলে একটি টাইগার অবজেক্টে বলা হয়।

এবং আমরা অ্যাসাইনমেন্ট আগে একটি চেক আছে না. সুতরাং, এটি সম্পূর্ণভাবে সম্ভব যে Cat-টাইপ myParent ভেরিয়েবল একটি স্ট্রিং রেফারেন্স সংরক্ষণ করবে।

"বিস্ময়কর উদাহরণ, আমিগো!"

জাভাতে, একটি পদ্ধতি কল করার আগে, বস্তুটিতে এমন একটি পদ্ধতি আছে কিনা তা পরীক্ষা করা হয় না। সমস্ত চেক রানটাইমে ঘটবে. এবং একটি অনুপস্থিত পদ্ধতিতে একটি [অনুমানিক] কল সম্ভবত প্রোগ্রামটি অস্তিত্বহীন বাইটকোড চালানোর চেষ্টা করতে পারে। এটি শেষ পর্যন্ত একটি মারাত্মক ত্রুটির দিকে পরিচালিত করবে এবং অপারেটিং সিস্টেম জোরপূর্বক প্রোগ্রামটি বন্ধ করে দেবে।

"ওহ। এখন আমি জানি।"