1. ইন্টারফেস
ল্যাম্বডা ফাংশনগুলি কী তা বোঝার জন্য, আপনাকে প্রথমে ইন্টারফেসগুলি কী তা বুঝতে হবে। সুতরাং, আসুন মূল পয়েন্টগুলি স্মরণ করি।
একটি ইন্টারফেস হল একটি শ্রেণীর ধারণার একটি পরিবর্তন। একটি প্রবলভাবে ছাঁটাই করা ক্লাস, ধরা যাক। একটি ক্লাসের বিপরীতে, একটি ইন্টারফেসের নিজস্ব ভেরিয়েবল থাকতে পারে না (স্ট্যাটিকগুলি ছাড়া)। আপনি এমন বস্তু তৈরি করতে পারবেন না যার ধরন একটি ইন্টারফেস:
- আপনি ক্লাসের ভেরিয়েবল ঘোষণা করতে পারবেন না
- আপনি বস্তু তৈরি করতে পারবেন না
উদাহরণ:
interface Runnable
{
void run();
}
একটি ইন্টারফেস ব্যবহার করে
তাহলে কেন একটি ইন্টারফেস প্রয়োজন? ইন্টারফেস শুধুমাত্র উত্তরাধিকারের সাথে একসাথে ব্যবহার করা হয়। একই ইন্টারফেস বিভিন্ন শ্রেণীর দ্বারা উত্তরাধিকারসূত্রে প্রাপ্ত হতে পারে, বা যেমন বলা হয় — ক্লাসগুলি ইন্টারফেস বাস্তবায়ন করে ।
যদি একটি ক্লাস একটি ইন্টারফেস প্রয়োগ করে, তবে এটিকে অবশ্যই ঘোষিত পদ্ধতিগুলি প্রয়োগ করতে হবে কিন্তু ইন্টারফেস দ্বারা প্রয়োগ করা হয়নি। উদাহরণ:
interface Runnable
{
void run();
}
class Timer implements Runnable
{
void run()
{
System.out.println(LocalTime.now());
}
}
class Calendar implements Runnable
{
void run()
{
var date = LocalDate.now();
System.out.println("Today: " + date.getDayOfWeek());
}
}
ক্লাসটি ইন্টারফেসটি Timer
প্রয়োগ করে Runnable
, তাই এটি অবশ্যই ইন্টারফেসের মধ্যে থাকা সমস্ত পদ্ধতিগুলিকে নিজের ভিতরে ঘোষণা করতে হবে Runnable
এবং সেগুলি প্রয়োগ করতে হবে, অর্থাত্ একটি মেথড বডিতে কোড লিখতে হবে। একই Calendar
ক্লাসের জন্য যায়।
কিন্তু এখন Runnable
ভেরিয়েবল এমন বস্তুর রেফারেন্স সংরক্ষণ করতে পারে যা Runnable
ইন্টারফেস বাস্তবায়ন করে।
উদাহরণ:
কোড | বিঃদ্রঃ |
---|---|
|
run() ক্লাসের মেথডকে ক্লাসের মেথড বলা Timer হবে ক্লাসের মেথড বলা হবে run() Timer run() Calendar |
আপনি সর্বদা যেকোন প্রকারের একটি ভেরিয়েবলের জন্য একটি অবজেক্ট রেফারেন্স বরাদ্দ করতে পারেন, যতক্ষণ না সেই টাইপটি অবজেক্টের পূর্বপুরুষ শ্রেণীর একটি। Timer
এবং ক্লাসের জন্য Calendar
, এই ধরনের দুটি প্রকার রয়েছে: Object
এবং Runnable
.
আপনি যদি একটি ভেরিয়েবলের জন্য একটি অবজেক্ট রেফারেন্স বরাদ্দ করেন Object
, আপনি শুধুমাত্র ক্লাসে ঘোষিত পদ্ধতিগুলিকে কল করতে পারেন Object
। এবং যদি আপনি একটি Runnable
ভেরিয়েবলের জন্য একটি অবজেক্ট রেফারেন্স বরাদ্দ করেন, আপনি টাইপের পদ্ধতিগুলিকে কল করতে পারেন Runnable
।
উদাহরণ 2:
ArrayList<Runnable> list = new ArrayList<Runnable>();
list.add (new Timer());
list.add (new Calendar());
for (Runnable element: list)
element.run();
এই কোডটি কাজ করবে, কারণ Timer
এবং Calendar
অবজেক্টগুলিতে এমন পদ্ধতি রয়েছে যা পুরোপুরি ভাল কাজ করে। সুতরাং, তাদের কল করা কোন সমস্যা নয়। যদি আমরা উভয় ক্লাসে একটি রান() পদ্ধতি যোগ করতাম, তাহলে আমরা তাদের এত সহজ উপায়ে কল করতে সক্ষম হতাম না।
মূলত, Runnable
ইন্টারফেসটি শুধুমাত্র রান মেথড রাখার জায়গা হিসেবে ব্যবহার করা হয়।
2. বাছাই করা
এর আরো বাস্তব কিছু এগিয়ে চলুন. উদাহরণস্বরূপ, এর বাছাই স্ট্রিং তাকান.
বর্ণানুক্রমিকভাবে স্ট্রিংগুলির একটি সংগ্রহ বাছাই করার জন্য, জাভা নামক একটি দুর্দান্ত পদ্ধতি রয়েছেCollections.sort(collection);
এই স্ট্যাটিক পদ্ধতি পাস সংগ্রহ সাজান. এবং বাছাই করার প্রক্রিয়ায়, এটি উপাদানগুলির অদলবদল করা উচিত কিনা তা বোঝার জন্য এটির উপাদানগুলির জোড়াভিত্তিক তুলনা করে।
বাছাই করার সময়, এই তুলনাগুলি () পদ্ধতি ব্যবহার করে সঞ্চালিত হয় compareTo
, যা সমস্ত স্ট্যান্ডার্ড ক্লাসে রয়েছে: Integer
, String
, ...
Integer ক্লাসের compareTo() পদ্ধতি দুটি সংখ্যার মান তুলনা করে, যখন স্ট্রিং ক্লাসের compareTo() পদ্ধতি স্ট্রিংগুলির বর্ণানুক্রমিক ক্রম দেখে।
সুতরাং সংখ্যার একটি সংগ্রহ ক্রমানুসারে বাছাই করা হবে, যখন স্ট্রিংগুলির একটি সংগ্রহ বর্ণানুক্রমিকভাবে সাজানো হবে।
বিকল্প সাজানোর অ্যালগরিদম
কিন্তু আমরা কি বর্ণানুক্রমিকভাবে স্ট্রিং বাছাই করতে চান না, কিন্তু তাদের দৈর্ঘ্য দ্বারা? এবং যদি আমরা সংখ্যাকে অবরোহী ক্রমে সাজাতে চাই? এই ক্ষেত্রে আপনি কি করবেন?
এই ধরনের পরিস্থিতি পরিচালনা করার জন্য, Collections
ক্লাসের আরেকটি sort()
পদ্ধতি রয়েছে যার দুটি পরামিতি রয়েছে:
Collections.sort(collection, comparator);
যেখানে তুলনাকারী একটি বিশেষ বস্তু যা জানে কিভাবে একটি সাজানোর অপারেশন চলাকালীন একটি সংগ্রহে বস্তুর তুলনা করতে হয় । কম্প্যারেটর শব্দটি এসেছে ইংরেজি শব্দ comparator থেকে , যেটি এসেছে compare থেকে , যার অর্থ "তুলনা করা"।
তাই এই বিশেষ বস্তু কি?
Comparator
ইন্টারফেস
ওয়েল, এটা সব খুব সহজ. sort()
পদ্ধতির দ্বিতীয় প্যারামিটারের ধরন হলComparator<T>
যেখানে T হল একটি টাইপ প্যারামিটার যা সংগ্রহের উপাদানগুলির ধরন নির্দেশ করে এবং একটি ইন্টারফেস যার একটি একক পদ্ধতি Comparator
রয়েছেint compare(T obj1, T obj2);
অন্য কথায়, কম্প্যারেটর অবজেক্ট হল যে কোন ক্লাসের যেকোন অবজেক্ট যা কম্প্যারেটর ইন্টারফেস প্রয়োগ করে। তুলনাকারী ইন্টারফেস খুব সহজ দেখায়:
public interface Comparator<Type>
{
public int compare(Type obj1, Type obj2);
}
পদ্ধতিটি compare()
দুটি আর্গুমেন্টের তুলনা করে যা এতে পাস করা হয়।
যদি পদ্ধতিটি একটি ঋণাত্মক সংখ্যা প্রদান করে, তাহলে এর অর্থ হল obj1 < obj2
। যদি পদ্ধতিটি একটি ধনাত্মক সংখ্যা প্রদান করে, তার মানে obj1 > obj2
. যদি পদ্ধতিটি 0 ফেরত দেয়, তার মানে obj1 == obj2
.
এখানে একটি তুলনাকারী বস্তুর একটি উদাহরণ যা স্ট্রিংকে তাদের দৈর্ঘ্যের সাথে তুলনা করে:
public class StringLengthComparator implements Comparator<String>
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
}
স্ট্রিং দৈর্ঘ্য তুলনা করতে, কেবল একটি দৈর্ঘ্য অন্য থেকে বিয়োগ করুন।
একটি প্রোগ্রামের জন্য সম্পূর্ণ কোড যা দৈর্ঘ্য অনুসারে স্ট্রিংগুলি সাজায় তা দেখতে এইরকম হবে:
public class Solution
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Hello", "how's", "life?");
Collections.sort(list, new StringLengthComparator());
}
}
class StringLengthComparator implements Comparator<String>
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
}
3. সিনট্যাকটিক চিনি
আপনি কি মনে করেন, এই কোড আরো কম্প্যাক্টলি লেখা যাবে? মূলত, শুধুমাত্র একটি লাইন আছে যেটিতে দরকারী তথ্য রয়েছে — obj1.length() - obj2.length();
.
কিন্তু কোড একটি পদ্ধতির বাইরে থাকতে পারে না, তাই আমাদের একটি পদ্ধতি যোগ করতে হয়েছিল compare()
, এবং পদ্ধতিটি সংরক্ষণ করার জন্য আমাদের একটি নতুন ক্লাস যোগ করতে হয়েছিল — StringLengthComparator
। এবং আমাদের ভেরিয়েবলের প্রকারগুলিও নির্দিষ্ট করতে হবে... সবকিছু সঠিক বলে মনে হচ্ছে।
কিন্তু এই কোড ছোট করার উপায় আছে. আমরা আপনার জন্য কিছু সিনট্যাকটিক চিনি পেয়েছি। দুই স্কুপ!
বেনামী ভিতরের ক্লাস
আপনি সঠিক পদ্ধতির ভিতরে তুলনাকারী কোড লিখতে পারেন main()
, এবং কম্পাইলার বাকি কাজ করবে। উদাহরণ:
public class Solution
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Hello", "how's", "life?");
Comparator<String> comparator = new Comparator<String>()
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
};
Collections.sort(list, comparator);
}
}
আপনি একটি বস্তু তৈরি করতে পারেন যা Comparator
স্পষ্টভাবে একটি ক্লাস তৈরি না করে ইন্টারফেস প্রয়োগ করে! কম্পাইলার এটি স্বয়ংক্রিয়ভাবে তৈরি করবে এবং এটিকে কিছু অস্থায়ী নাম দেবে। আসুন তুলনা করা যাক:
Comparator<String> comparator = new Comparator<String>()
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
};
Comparator<String> comparator = new StringLengthComparator();
class StringLengthComparator implements Comparator<String>
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
}
দুটি ভিন্ন ক্ষেত্রে অভিন্ন কোড ব্লক নির্দেশ করতে একই রঙ ব্যবহার করা হয়। পার্থক্যগুলি অনুশীলনে বেশ ছোট।
যখন কম্পাইলার কোডের প্রথম ব্লকের মুখোমুখি হয়, তখন এটি কেবল কোডের একটি সংশ্লিষ্ট দ্বিতীয় ব্লক তৈরি করে এবং ক্লাসটিকে কিছু র্যান্ডম নাম দেয়।
4. জাভাতে ল্যাম্বডা এক্সপ্রেশন
ধরা যাক আপনি আপনার কোডে একটি বেনামী অভ্যন্তরীণ শ্রেণী ব্যবহার করার সিদ্ধান্ত নিয়েছেন। এই ক্ষেত্রে, আপনার কাছে এইরকম কোডের একটি ব্লক থাকবে:
Comparator<String> comparator = new Comparator<String>()
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
};
এখানে আমরা একটি বেনামী ক্লাস তৈরির সাথে একটি ভেরিয়েবলের ঘোষণাকে একত্রিত করি। কিন্তু এই কোড ছোট করার একটি উপায় আছে। উদাহরণস্বরূপ, এই মত:
Comparator<String> comparator = (String obj1, String obj2) ->
{
return obj1.length() – obj2.length();
};
সেমিকোলন প্রয়োজন কারণ এখানে আমাদের শুধুমাত্র একটি অন্তর্নিহিত শ্রেণী ঘোষণা নয়, একটি পরিবর্তনশীল সৃষ্টিও রয়েছে।
এই ধরনের স্বরলিপিকে ল্যাম্বডা এক্সপ্রেশন বলা হয় ।
যদি কম্পাইলারটি আপনার কোডে এইরকম স্বরলিপির সম্মুখীন হয়, তবে এটি কেবল কোডের ভার্বোস সংস্করণ তৈরি করে (একটি বেনামী ভিতরের শ্রেণী সহ)।
লক্ষ্য করুন যে ল্যাম্বডা এক্সপ্রেশন লেখার সময়, আমরা শুধুমাত্র ক্লাসের নামই নয় , পদ্ধতির নামও বাদ দিয়েছি।Comparator<String>
int compare()
কম্পাইলের পদ্ধতি নির্ধারণে কোন সমস্যা হবে না , কারণ একটি ল্যাম্বডা এক্সপ্রেশন শুধুমাত্র এমন ইন্টারফেসের জন্য লেখা যেতে পারে যার একটি একক পদ্ধতি রয়েছে । যাইহোক, এই নিয়মের কাছাকাছি যাওয়ার একটি উপায় রয়েছে, তবে আপনি যখন আরও গভীরতার সাথে OOP অধ্যয়ন শুরু করবেন তখন আপনি এটি সম্পর্কে শিখবেন (আমরা ডিফল্ট পদ্ধতি সম্পর্কে কথা বলছি)।
আসুন কোডটির ভার্বোস সংস্করণটি আবার দেখি, তবে আমরা ল্যাম্বডা এক্সপ্রেশন লেখার সময় যে অংশটি বাদ দেওয়া যেতে পারে তা ধূসর করে দেব:
Comparator<String> comparator = new Comparator<String>()
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
};
মনে হচ্ছে গুরুত্বপূর্ণ কিছুই বাদ দেওয়া হয়নি। প্রকৃতপক্ষে, যদি Comparator
ইন্টারফেসের শুধুমাত্র একটি compare()
পদ্ধতি থাকে, তাহলে কম্পাইলারটি অবশিষ্ট কোড থেকে ধূসর-আউট কোডটি সম্পূর্ণরূপে পুনরুদ্ধার করতে পারে।
শ্রেণীবিভাজন
যাইহোক, এখন আমরা এইভাবে সাজানোর কোড লিখতে পারি:
Comparator<String> comparator = (String obj1, String obj2) ->
{
return obj1.length() – obj2.length();
};
Collections.sort(list, comparator);
বা এমনকি এই মত:
Collections.sort(list, (String obj1, String obj2) ->
{
return obj1.length() – obj2.length();
}
);
আমরা অবিলম্বে comparator
ভেরিয়েবলের সাথে বরাদ্দ করা মান দিয়ে ভেরিয়েবলটি প্রতিস্থাপন করেছি comparator
।
অনুমান টাইপ করুন
কিন্তু এখানেই শেষ নয়. এই উদাহরণগুলিতে কোডটি আরও কম্প্যাক্টলি লেখা যেতে পারে। প্রথমত, কম্পাইলার নিজেই নির্ধারণ করতে পারে যে obj1
এবং obj2
ভেরিয়েবলগুলি Strings
। এবং দ্বিতীয়, কোঁকড়া ধনুর্বন্ধনী এবং রিটার্ন স্টেটমেন্টও বাদ দেওয়া যেতে পারে যদি আপনার শুধুমাত্র মেথড কোডে একটি একক কমান্ড থাকে।
সংক্ষিপ্ত সংস্করণ এই মত হবে:
Comparator<String> comparator = (obj1, obj2) ->
obj1.length() – obj2.length();
Collections.sort(list, comparator);
এবং যদি পরিবর্তনশীল ব্যবহার করার পরিবর্তে comparator
, আমরা অবিলম্বে এর মান ব্যবহার করি, তাহলে আমরা নিম্নলিখিত সংস্করণটি পাই:
Collections.sort(list, (obj1, obj2) -> obj1.length() — obj2.length() );
আচ্ছা, আপনি যে কি মনে করেন? কোন অপ্রয়োজনীয় তথ্য ছাড়া কোডের মাত্র একটি লাইন — শুধুমাত্র ভেরিয়েবল এবং কোড। এটাকে ছোট করার কোন উপায় নেই! নাকি আছে?
5. এটি কিভাবে কাজ করে
আসলে, কোড আরও কম্প্যাক্টভাবে লেখা যেতে পারে। কিন্তু পরে যে আরো.
আপনি একটি ল্যাম্বডা এক্সপ্রেশন লিখতে পারেন যেখানে আপনি একটি একক পদ্ধতির সাথে একটি ইন্টারফেস টাইপ ব্যবহার করবেন ।
উদাহরণস্বরূপ, কোডে , আপনি একটি ল্যাম্বডা এক্সপ্রেশন লিখতে পারেন কারণ পদ্ধতির স্বাক্ষরটি এরকম:Collections.sort(list, (obj1, obj2) -> obj1.length() - obj2.length());
sort()
sort(Collection<T> colls, Comparator<T> comp)
যখন আমরা ArrayList<String>
সর্ট পদ্ধতিতে প্রথম আর্গুমেন্ট হিসাবে সংগ্রহটি পাস করি, তখন কম্পাইলার দ্বিতীয় আর্গুমেন্টের ধরনটি নির্ধারণ করতে সক্ষম হয়েছিল । এবং এটি থেকে, এটি উপসংহারে এসেছে যে এই ইন্টারফেসের একটি একক পদ্ধতি রয়েছে। বাকি সব একটি প্রযুক্তিগত.Comparator<String>
int compare(String obj1, String obj2)
GO TO FULL VERSION