অভিবাদন, তরুণ পদোয়ান। এই নিবন্ধে, আমি আপনাকে বল সম্পর্কে বলব, একটি শক্তি যা জাভা প্রোগ্রামাররা শুধুমাত্র আপাতদৃষ্টিতে অসম্ভব পরিস্থিতিতে ব্যবহার করে। জাভার অন্ধকার দিক হল প্রতিফলন API। জাভাতে, জাভা প্রতিফলন API ব্যবহার করে প্রতিফলন প্রয়োগ করা হয়।
আমার প্যাকেজ অনুক্রমে, MyClass এর পুরো নাম হবে "reflection.MyClass"। একটি ক্লাসের নাম শেখার একটি সহজ উপায়ও রয়েছে (একটি স্ট্রিং হিসাবে ক্লাসের নাম ফেরত দিন):
জাভা প্রতিফলন কি?
ইন্টারনেটে একটি সংক্ষিপ্ত, সঠিক এবং জনপ্রিয় সংজ্ঞা রয়েছে। প্রতিফলন ( দেরী লাতিন রিফ্লেক্সিও থেকে - ফিরে যেতে ) একটি প্রোগ্রাম যখন এটি চলছে তখন তার সম্পর্কে ডেটা অন্বেষণ করার একটি পদ্ধতি। প্রতিফলন আপনাকে ক্ষেত্র, পদ্ধতি এবং ক্লাস কনস্ট্রাক্টর সম্পর্কে তথ্য অন্বেষণ করতে দেয়। প্রতিফলন আপনাকে এমন ধরণের সাথে কাজ করতে দেয় যা কম্পাইলের সময় উপস্থিত ছিল না, কিন্তু যা রান টাইমে উপলব্ধ ছিল। প্রতিফলন এবং ত্রুটি তথ্য প্রদানের জন্য একটি যৌক্তিকভাবে সামঞ্জস্যপূর্ণ মডেল সঠিক গতিশীল কোড তৈরি করা সম্ভব করে তোলে। অন্য কথায়, জাভাতে প্রতিফলন কীভাবে কাজ করে তা বোঝা আপনার জন্য বেশ কয়েকটি আশ্চর্যজনক সুযোগ উন্মুক্ত করবে। আপনি আক্ষরিকভাবে ক্লাস এবং তাদের উপাদানগুলিকে ধাক্কা দিতে পারেন। এখানে প্রতিফলন অনুমতি দেয় একটি মৌলিক তালিকা:- একটি বস্তুর ক্লাস শিখুন/নির্ধারণ করুন;
- একটি ক্লাসের মডিফায়ার, ক্ষেত্র, পদ্ধতি, ধ্রুবক, কনস্ট্রাক্টর এবং সুপারক্লাস সম্পর্কে তথ্য পান;
- বাস্তবায়িত ইন্টারফেসের অন্তর্গত কোন পদ্ধতিগুলি খুঁজে বের করুন;
- একটি ক্লাসের একটি উদাহরণ তৈরি করুন যার ক্লাসের নাম রান টাইম পর্যন্ত অজানা;
- নাম অনুসারে একটি বস্তুর ক্ষেত্রগুলির মানগুলি পান এবং সেট করুন;
- নাম দ্বারা একটি বস্তুর পদ্ধতি কল.
MyClass
:
public class MyClass {
private int number;
private String name = "default";
// public MyClass(int number, String name) {
// this.number = number;
// this.name = name;
// }
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public void setName(String name) {
this.name = name;
}
private void printData(){
System.out.println(number + name);
}
}
আপনি দেখতে পাচ্ছেন, এটি একটি খুব মৌলিক ক্লাস। প্যারামিটার সহ কন্সট্রাকটর ইচ্ছাকৃতভাবে মন্তব্য করা হয়। আমরা যে পরে ফিরে আসব. আপনি যদি ক্লাসের বিষয়বস্তু মনোযোগ সহকারে দেখেন, আপনি সম্ভবত নাম ক্ষেত্রের জন্য একজন গেটারের অনুপস্থিতি লক্ষ্য করেছেন। নামের ক্ষেত্রটি নিজেই ব্যক্তিগত অ্যাক্সেস মডিফায়ার দিয়ে চিহ্নিত করা হয়েছে : আমরা ক্লাসের বাইরে এটি অ্যাক্সেস করতে পারি না যার অর্থ আমরা এর মান পুনরুদ্ধার করতে পারি না । " তাহলে সমস্যা কি ?" তুমি বলো. "একটি গেটার যোগ করুন বা অ্যাক্সেস মডিফায়ার পরিবর্তন করুন"। এবং আপনি সঠিক হবে, যদি নাMyClass
একটি সংকলিত AAR লাইব্রেরিতে বা অন্য কোনো ব্যক্তিগত মডিউলে ছিল যার কোনো পরিবর্তন করার ক্ষমতা নেই। অনুশীলনে, এটি সব সময় ঘটে। এবং কিছু অসতর্ক প্রোগ্রামার কেবল একটি গেটার লিখতে ভুলে গেছে । এই প্রতিফলন মনে করার খুব সময়! আসুন ক্লাসের ব্যক্তিগত নামের ক্ষেত্রে যাওয়ার চেষ্টা করি MyClass
:
public static void main(String[] args) {
MyClass myClass = new MyClass();
int number = myClass.getNumber();
String name = null; // No getter =(
System.out.println(number + name); // Output: 0null
try {
Field field = myClass.getClass().getDeclaredField("name");
field.setAccessible(true);
name = (String) field.get(myClass);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(number + name); // Output: 0default
}
এর বিশ্লেষণ করা যাক কি ঘটেছে. জাভাতে, নামক একটি চমৎকার ক্লাস আছে Class
। এটি একটি এক্সিকিউটেবল জাভা অ্যাপ্লিকেশনে ক্লাস এবং ইন্টারফেস প্রতিনিধিত্ব করে। Class
আমরা এবং এর মধ্যে সম্পর্ক কভার করব না ClassLoader
, কারণ এটি এই নিবন্ধের বিষয় নয়। এর পরে, এই ক্লাসের ক্ষেত্রগুলি পুনরুদ্ধার করতে, আপনাকে getFields()
পদ্ধতিটি কল করতে হবে। এই পদ্ধতিটি এই শ্রেণীর সমস্ত অ্যাক্সেসযোগ্য ক্ষেত্র ফিরিয়ে দেবে। এটি আমাদের জন্য কাজ করে না, কারণ আমাদের ক্ষেত্র ব্যক্তিগত , তাই আমরা getDeclaredFields()
পদ্ধতিটি ব্যবহার করি। এই পদ্ধতিটি ক্লাস ক্ষেত্রগুলির একটি অ্যারেও প্রদান করে, তবে এখন এটি ব্যক্তিগত এবং সুরক্ষিত ক্ষেত্রগুলি অন্তর্ভুক্ত করে। এই ক্ষেত্রে, আমরা যে ক্ষেত্রের প্রতি আগ্রহী তার নাম জানি, তাই আমরা getDeclaredField(String)
পদ্ধতিটি কোথায় ব্যবহার করতে পারিString
পছন্দসই ক্ষেত্রের নাম। বিঃদ্রঃ: getFields()
এবং getDeclaredFields()
একটি অভিভাবক শ্রেণীর ক্ষেত্র ফিরে না! দারুণ। আমরা আমাদের নামের উল্লেখ করে একটি Field
বস্তু পেয়েছি । যেহেতু ক্ষেত্রটি সর্বজনীন ছিল না , তাই আমাদের অবশ্যই এটির সাথে কাজ করার অনুমতি দিতে হবে৷ পদ্ধতিটি আমাদের আরও এগিয়ে যেতে দেয়। এখন নাম ক্ষেত্র আমাদের সম্পূর্ণ নিয়ন্ত্রণে! আপনি অবজেক্টের পদ্ধতিতে কল করে এর মান পুনরুদ্ধার করতে পারেন , যেখানে আমাদের ক্লাসের একটি উদাহরণ রয়েছে। আমরা টাইপকে রূপান্তর করি এবং আমাদের নামের ভেরিয়েবলে মান নির্ধারণ করি । যদি আমরা নামের ক্ষেত্রে একটি নতুন মান সেট করার জন্য একটি সেটার খুঁজে না পাই , আপনি সেট পদ্ধতি ব্যবহার করতে পারেন: setAccessible(true)
Field
get(Object)
Object
MyClass
String
field.set(myClass, (String) "new value");
অভিনন্দন! আপনি সবেমাত্র প্রতিফলনের মূল বিষয়গুলি আয়ত্ত করেছেন এবং একটি ব্যক্তিগত ক্ষেত্রে অ্যাক্সেস করেছেন! ব্লকের দিকে মনোযোগ দিন try/catch
এবং ব্যতিক্রমের ধরনগুলি পরিচালনা করুন। IDE আপনাকে বলবে যে তাদের উপস্থিতি নিজে থেকেই প্রয়োজন, তবে আপনি তাদের নাম দিয়ে স্পষ্টভাবে বলতে পারবেন কেন তারা এখানে আছে। সরানো! আপনি হয়তো লক্ষ্য করেছেন, আমাদের MyClass
ক্লাসে ইতিমধ্যেই ক্লাস ডেটা সম্পর্কে তথ্য প্রদর্শনের জন্য একটি পদ্ধতি রয়েছে:
private void printData(){
System.out.println(number + name);
}
কিন্তু এই প্রোগ্রামার এখানেও তার আঙুলের ছাপ রেখে গেছেন। পদ্ধতিটির একটি ব্যক্তিগত অ্যাক্সেস মডিফায়ার রয়েছে এবং প্রতিবার ডেটা প্রদর্শন করার জন্য আমাদের নিজস্ব কোড লিখতে হবে। কী এলোমেলো. আমাদের প্রতিফলন কোথায় গেল? নিম্নলিখিত ফাংশন লিখুন:
public static void printData(Object myClass){
try {
Method method = myClass.getClass().getDeclaredMethod("printData");
method.setAccessible(true);
method.invoke(myClass);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
এখানে পদ্ধতিটি একটি ক্ষেত্র পুনরুদ্ধারের জন্য ব্যবহৃত পদ্ধতির মতোই। আমরা নাম অনুসারে পছন্দসই পদ্ধতিতে প্রবেশ করি এবং এটিতে অ্যাক্সেস প্রদান করি। এবং বস্তুর উপর Method
আমরা পদ্ধতি কল invoke(Object, Args)
, যেখানে Object
এছাড়াও ক্লাস একটি উদাহরণ MyClass
. Args
পদ্ধতির আর্গুমেন্ট, যদিও আমাদের কোনো নেই। এখন আমরা printData
তথ্য প্রদর্শন করতে ফাংশন ব্যবহার করি:
public static void main(String[] args) {
MyClass myClass = new MyClass();
int number = myClass.getNumber();
String name = null; //?
printData(myClass); // Output: 0default
try {
Field field = myClass.getClass().getDeclaredField("name");
field.setAccessible(true);
field.set(myClass, (String) "new value");
name = (String) field.get(myClass);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
printData(myClass);// Output: 0new value
}
হুররে! এখন আমরা ক্লাসের ব্যক্তিগত পদ্ধতিতে অ্যাক্সেস পেয়েছি। কিন্তু যদি পদ্ধতিতে আর্গুমেন্ট থাকে এবং কেন কনস্ট্রাক্টর মন্তব্য করা হয়? সবকিছু তার নিজস্ব নির্ধারিত সময়ে। এটি শুরুতে সংজ্ঞা থেকে স্পষ্ট যে প্রতিফলন আপনাকে রান টাইমে একটি ক্লাসের উদাহরণ তৈরি করতে দেয় (যখন প্রোগ্রামটি চলছে)! আমরা ক্লাসের পুরো নাম ব্যবহার করে একটি অবজেক্ট তৈরি করতে পারি। ক্লাসের পুরো নাম হল ক্লাসের নাম, এর প্যাকেজের পথ সহ ।
MyClass.class.getName()
ক্লাসের একটি উদাহরণ তৈরি করতে জাভা প্রতিফলন ব্যবহার করা যাক:
public static void main(String[] args) {
MyClass myClass = null;
try {
Class clazz = Class.forName(MyClass.class.getName());
myClass = (MyClass) clazz.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(myClass); // Output: created object reflection.MyClass@60e53b93
}
যখন একটি জাভা অ্যাপ্লিকেশন শুরু হয়, সমস্ত ক্লাস JVM এ লোড হয় না। যদি আপনার কোডটি ক্লাসের উল্লেখ না করে MyClass
, তাহলে ClassLoader
, যা JVM-এ ক্লাস লোড করার জন্য দায়ী, ক্লাসটি কখনই লোড করবে না। এর মানে আপনাকে এটি লোড করতে বাধ্য করতে হবে এবং একটি পরিবর্তনশীল ClassLoader
আকারে একটি শ্রেণির বিবরণ পেতে হবে । Class
এই জন্য আমাদের forName(String)
পদ্ধতি আছে, যেখানে String
ক্লাসের নাম যার বর্ণনা আমাদের প্রয়োজন। অবজেক্ট পাওয়ার পর Сlass
, মেথড কল করলে সেই বর্ণনা ব্যবহার করে তৈরি করা newInstance()
একটি অবজেক্ট ফিরে আসবে । Object
যা বাকি আছে আমাদের এই বস্তু সরবরাহ করা হয়MyClass
ক্লাস শান্ত! এটা কঠিন ছিল, কিন্তু বোধগম্য, আমি আশা করি. এখন আমরা আক্ষরিক অর্থে এক লাইনে একটি ক্লাসের একটি উদাহরণ তৈরি করতে পারি! দুর্ভাগ্যবশত, বর্ণিত পদ্ধতি শুধুমাত্র ডিফল্ট কনস্ট্রাক্টরের সাথে কাজ করবে (পরামিটার ছাড়া)। আপনি প্যারামিটার সহ পদ্ধতি এবং কনস্ট্রাক্টরকে কীভাবে কল করবেন? এটা আমাদের কনস্ট্রাক্টর uncomment করার সময়. প্রত্যাশিত হিসাবে, newInstance()
ডিফল্ট কনস্ট্রাক্টর খুঁজে পাচ্ছি না, এবং আর কাজ করে না। আসুন ক্লাস ইনস্ট্যান্টিয়েশনটি আবার লিখি:
public static void main(String[] args) {
MyClass myClass = null;
try {
Class clazz = Class.forName(MyClass.class.getName());
Class[] params = {int.class, String.class};
myClass = (MyClass) clazz.getConstructor(params).newInstance(1, "default2");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(myClass);// Output: created object reflection.MyClass@60e53b93
}
ক্লাস কনস্ট্রাক্টর প্রাপ্ত করার জন্য পদ্ধতিটিকে getConstructors()
ক্লাস ডেফিনিশনে কল করা উচিত এবং তারপর getParameterTypes()
একটি কনস্ট্রাক্টরের পরামিতি পেতে কল করা উচিত:
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
Class[] paramTypes = constructor.getParameterTypes();
for (Class paramType : paramTypes) {
System.out.print(paramType.getName() + " ");
}
System.out.println();
}
যে আমাদের সব কনস্ট্রাক্টর এবং তাদের পরামিতি পায়. আমার উদাহরণে, আমি নির্দিষ্ট, পূর্বে পরিচিত পরামিতি সহ একটি নির্দিষ্ট কনস্ট্রাক্টরকে উল্লেখ করি। এবং এই কনস্ট্রাক্টরকে কল করার জন্য, আমরা newInstance
পদ্ধতিটি ব্যবহার করি, যা আমরা এই পরামিতিগুলির মানগুলি পাস করি। invoke
কল পদ্ধতি ব্যবহার করার সময় এটি একই হবে । এটি প্রশ্ন তোলে: প্রতিফলনের মাধ্যমে কনস্ট্রাক্টরদের কল করা কখন কাজে আসে? ইতিমধ্যেই শুরুতে উল্লিখিত হিসাবে, আধুনিক জাভা প্রযুক্তিগুলি জাভা প্রতিফলন API ছাড়া পেতে পারে না। উদাহরণ স্বরূপ, ডিপেনডেন্সি ইনজেকশন (DI), যা জনপ্রিয় ডারার গঠনের জন্য পদ্ধতি এবং কনস্ট্রাক্টরের প্রতিফলনের সাথে টীকাকে একত্রিত করে।অ্যান্ড্রয়েড বিকাশের জন্য লাইব্রেরি। এই নিবন্ধটি পড়ার পরে, আপনি আত্মবিশ্বাসের সাথে জাভা প্রতিফলন API-এর পদ্ধতিতে নিজেকে শিক্ষিত বিবেচনা করতে পারেন। তারা প্রতিফলনকে জাভার অন্ধকার দিক বলে না। এটি সম্পূর্ণরূপে OOP দৃষ্টান্ত ভঙ্গ করে। জাভাতে, এনক্যাপসুলেশন কিছু প্রোগ্রামের উপাদানগুলিতে অন্যদের অ্যাক্সেস লুকিয়ে রাখে এবং সীমাবদ্ধ করে। যখন আমরা প্রাইভেট মডিফায়ার ব্যবহার করি, তখন আমরা সেই ফিল্ডটি শুধুমাত্র সেই ক্লাসের মধ্যে থেকে অ্যাক্সেস করতে চাই যেখানে এটি বিদ্যমান। এবং আমরা এই নীতির উপর ভিত্তি করে প্রোগ্রামের পরবর্তী আর্কিটেকচার তৈরি করি। এই প্রবন্ধে, আমরা দেখেছি যে আপনি কীভাবে প্রতিফলন ব্যবহার করে যেকোন জায়গায় আপনার পথ জোর করতে পারেন। সৃজনশীল নকশা প্যাটার্ন Singletonএটি একটি স্থাপত্য সমাধান হিসাবে এর একটি ভাল উদাহরণ। মূল ধারণাটি হল যে এই প্যাটার্নটি বাস্তবায়নকারী একটি শ্রেণী সম্পূর্ণ প্রোগ্রামটি কার্যকর করার সময় শুধুমাত্র একটি উদাহরণ থাকবে। ডিফল্ট কনস্ট্রাক্টরে ব্যক্তিগত অ্যাক্সেস মডিফায়ার যোগ করে এটি সম্পন্ন করা হয়। এবং এটি খুব খারাপ হবে যদি একজন প্রোগ্রামার প্রতিফলন ব্যবহার করে এই ধরনের ক্লাসের আরও উদাহরণ তৈরি করে। যাইহোক, আমি সম্প্রতি একজন সহকর্মীকে একটি খুব আকর্ষণীয় প্রশ্ন জিজ্ঞাসা করতে শুনেছি: সিঙ্গেলটন প্যাটার্ন প্রয়োগ করে এমন একটি ক্লাস কি উত্তরাধিকারসূত্রে পাওয়া যেতে পারে? এটা কি হতে পারে যে, এই ক্ষেত্রে, এমনকি প্রতিফলন শক্তিহীন হবে? নীচের মন্তব্যে নিবন্ধ এবং আপনার উত্তর সম্পর্কে আপনার প্রতিক্রিয়া ছেড়ে দিন, এবং সেখানে আপনার নিজের প্রশ্ন জিজ্ঞাসা করুন!
আরো পড়া: |
---|
GO TO FULL VERSION