এই বিষয়টি অ্যান্ড্রয়েডের জন্য ডেভেলপ করার সময় কোটলিন ভাষার কিছু সবচেয়ে কার্যকর দিকগুলির উপর আলোকপাত করে।
টুকরোগুলো নিয়ে কাজ করুন
নিম্নলিখিত বিভাগগুলিতে কোটলিনের সেরা কিছু বৈশিষ্ট্য তুলে ধরার জন্য Fragment উদাহরণ ব্যবহার করা হয়েছে।
উত্তরাধিকার
আপনি Kotlin-এ class কীওয়ার্ড ব্যবহার করে একটি ক্লাস ঘোষণা করতে পারেন। নিম্নলিখিত উদাহরণে, LoginFragment হল Fragment এর একটি সাবক্লাস। আপনি সাবক্লাস এবং এর প্যারেন্টের মধ্যে : অপারেটর ব্যবহার করে উত্তরাধিকার নির্দেশ করতে পারেন:
class LoginFragment : Fragment()
এই ক্লাস ডিক্লারে, LoginFragment তার সুপারক্লাস, Fragment এর কনস্ট্রাক্টরকে কল করার জন্য দায়ী।
LoginFragment এর মধ্যে, আপনি আপনার Fragment এর অবস্থার পরিবর্তনের প্রতিক্রিয়া জানাতে বেশ কয়েকটি জীবনচক্র কলব্যাক ওভাররাইড করতে পারেন। একটি ফাংশন ওভাররাইড করতে, নিম্নলিখিত উদাহরণে দেখানো override কীওয়ার্ড ব্যবহার করুন:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}
প্যারেন্ট ক্লাসে একটি ফাংশন রেফারেন্স করতে, super কীওয়ার্ড ব্যবহার করুন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
বাতিলযোগ্যতা এবং আরম্ভকরণ
পূর্ববর্তী উদাহরণগুলিতে, ওভাররাইড করা পদ্ধতিগুলির কিছু প্যারামিটারের সাথে প্রশ্ন চিহ্ন ? টাইপ যুক্ত করা হয়েছে। এটি নির্দেশ করে যে এই প্যারামিটারগুলির জন্য পাস করা আর্গুমেন্টগুলি null হতে পারে। তাদের nullability নিরাপদে পরিচালনা করতে ভুলবেন না।
কোটলিনে, অবজেক্ট ডিক্লেয়ার করার সময় আপনাকে অবশ্যই একটি অবজেক্টের প্রোপার্টি ইনিশিয়ালাইজ করতে হবে। এর অর্থ হল যখন আপনি একটি ক্লাসের ইনস্ট্যান্স পাবেন, তখন আপনি তাৎক্ষণিকভাবে এর অ্যাক্সেসযোগ্য প্রোপার্টিগুলির যেকোনো একটি উল্লেখ করতে পারবেন। তবে, Fragment এ View অবজেক্টগুলি Fragment#onCreateView কল না করা পর্যন্ত স্ফীত হওয়ার জন্য প্রস্তুত নয়, তাই আপনার View এর প্রোপার্টি ইনিশিয়ালাইজেশন পিছিয়ে দেওয়ার একটি উপায় প্রয়োজন।
lateinit আপনাকে প্রপার্টি ইনিশিয়ালাইজেশন পিছিয়ে দিতে সাহায্য করে। lateinit ব্যবহার করার সময়, আপনার যত তাড়াতাড়ি সম্ভব আপনার প্রপার্টি ইনিশিয়ালাইজ করা উচিত।
নিচের উদাহরণটি onViewCreated এ View অবজেক্ট বরাদ্দ করার জন্য lateinit ব্যবহার দেখানো হয়েছে:
class LoginFragment : Fragment() {
private lateinit var usernameEditText: EditText
private lateinit var passwordEditText: EditText
private lateinit var loginButton: Button
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
usernameEditText = view.findViewById(R.id.username_edit_text)
passwordEditText = view.findViewById(R.id.password_edit_text)
loginButton = view.findViewById(R.id.login_button)
statusTextView = view.findViewById(R.id.status_text_view)
}
...
}
SAM রূপান্তর
আপনি OnClickListener ইন্টারফেসটি প্রয়োগ করে অ্যান্ড্রয়েডে ক্লিক ইভেন্টগুলি শুনতে পারেন। Button অবজেক্টগুলিতে একটি setOnClickListener() ফাংশন থাকে যা OnClickListener এর বাস্তবায়ন গ্রহণ করে।
OnClickListener একটি একক বিমূর্ত পদ্ধতি আছে, onClick() , যা আপনাকে অবশ্যই বাস্তবায়ন করতে হবে। যেহেতু setOnClickListener() সর্বদা একটি OnClickListener একটি আর্গুমেন্ট হিসেবে গ্রহণ করে এবং যেহেতু OnClickListener সর্বদা একই একক বিমূর্ত পদ্ধতি ব্যবহার করে, তাই এই বাস্তবায়নটি Kotlin-এ একটি বেনামী ফাংশন ব্যবহার করে উপস্থাপন করা যেতে পারে। এই প্রক্রিয়াটি Single Abstract Method রূপান্তর বা SAM রূপান্তর নামে পরিচিত।
SAM রূপান্তর আপনার কোডকে অনেক পরিষ্কার করে তুলতে পারে। নিচের উদাহরণে দেখানো হয়েছে কিভাবে SAM রূপান্তর ব্যবহার করে একটি OnClickListener একটি Button এর জন্য বাস্তবায়ন করতে হয়:
loginButton.setOnClickListener {
val authSuccessful: Boolean = viewModel.authenticate(
usernameEditText.text.toString(),
passwordEditText.text.toString()
)
if (authSuccessful) {
// Navigate to next screen
} else {
statusTextView.text = requireContext().getString(R.string.auth_failed)
}
}
setOnClickListener() এ পাস করা বেনামী ফাংশনের মধ্যে থাকা কোডটি ব্যবহারকারী loginButton ক্লিক করলে কার্যকর হয়।
সঙ্গী বস্তু
কম্প্যানিয়ন অবজেক্টগুলি এমন একটি প্রক্রিয়া প্রদান করে যা ভেরিয়েবল বা ফাংশনগুলিকে সংজ্ঞায়িত করে যা ধারণাগতভাবে একটি ধরণের সাথে সংযুক্ত থাকে কিন্তু কোনও নির্দিষ্ট বস্তুর সাথে আবদ্ধ নয়। কম্প্যানিয়ন অবজেক্টগুলি ভেরিয়েবল এবং পদ্ধতিগুলির জন্য জাভার static কীওয়ার্ড ব্যবহারের অনুরূপ।
নিচের উদাহরণে, TAG হল একটি String ধ্রুবক। LoginFragment এর প্রতিটি উদাহরণের জন্য String এর একটি অনন্য উদাহরণের প্রয়োজন নেই, তাই আপনার এটি একটি companion object-এ সংজ্ঞায়িত করা উচিত:
class LoginFragment : Fragment() {
...
companion object {
private const val TAG = "LoginFragment"
}
}
আপনি ফাইলের উপরের স্তরে TAG সংজ্ঞায়িত করতে পারেন, তবে ফাইলটিতে প্রচুর সংখ্যক ভেরিয়েবল, ফাংশন এবং ক্লাস থাকতে পারে যা উপরের স্তরেও সংজ্ঞায়িত করা হয়। কম্প্যানিয়ন অবজেক্টগুলি সেই ক্লাসের কোনও নির্দিষ্ট উদাহরণ উল্লেখ না করেই ভেরিয়েবল, ফাংশন এবং ক্লাস সংজ্ঞাকে সংযুক্ত করতে সহায়তা করে।
সম্পত্তি প্রতিনিধিত্ব
প্রোপার্টি শুরু করার সময়, আপনি অ্যান্ড্রয়েডের কিছু সাধারণ প্যাটার্ন পুনরাবৃত্তি করতে পারেন, যেমন একটি Fragment মধ্যে একটি ViewModel অ্যাক্সেস করা। অতিরিক্ত ডুপ্লিকেট কোড এড়াতে, আপনি Kotlin এর প্রোপার্টি ডেলিগেশন সিনট্যাক্স ব্যবহার করতে পারেন।
private val viewModel: LoginViewModel by viewModels()
Property deligation একটি সাধারণ বাস্তবায়ন প্রদান করে যা আপনি আপনার অ্যাপ জুড়ে পুনরায় ব্যবহার করতে পারেন। Android KTX আপনার জন্য কিছু property deligate প্রদান করে। উদাহরণস্বরূপ, viewModels একটি ViewModel পুনরুদ্ধার করে যা বর্তমান Fragment এর সাথে সংযুক্ত।
সম্পত্তি প্রতিনিধিত্ব প্রতিফলন ব্যবহার করে, যা কিছু কর্মক্ষমতা ওভারহেড যোগ করে। ট্রেডঅফ হল একটি সংক্ষিপ্ত বাক্য গঠন যা বিকাশের সময় সাশ্রয় করে।
বাতিলযোগ্যতা
কোটলিন আপনার অ্যাপ জুড়ে টাইপ-নিরাপত্তা বজায় রাখার জন্য কঠোর nullability নিয়ম প্রদান করে। কোটলিনে, অবজেক্টের রেফারেন্সে ডিফল্টরূপে null মান থাকতে পারে না। একটি ভেরিয়েবলে একটি null মান নির্ধারণ করতে, আপনাকে বেস টাইপের শেষে ? যোগ করে একটি nullable ভেরিয়েবল টাইপ ঘোষণা করতে হবে।
উদাহরণস্বরূপ, নিম্নলিখিত এক্সপ্রেশনটি কোটলিনে অবৈধ। name String ধরণের এবং বাতিলযোগ্য নয়:
val name: String = null
একটি null মান অনুমোদন করতে, আপনাকে একটি nullable String টাইপ, String? ব্যবহার করতে হবে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
val name: String? = null
আন্তঃকার্যক্ষমতা
Kotlin-এর কঠোর নিয়মগুলি আপনার কোডকে আরও নিরাপদ এবং সংক্ষিপ্ত করে তোলে। এই নিয়মগুলি NullPointerException থাকার সম্ভাবনা কমিয়ে দেয় যা আপনার অ্যাপটিকে ক্র্যাশ করতে পারে। তাছাড়া, এগুলি আপনার কোডে নাল চেক করার সংখ্যাও কমিয়ে দেয়।
প্রায়শই, অ্যান্ড্রয়েড অ্যাপ লেখার সময় আপনাকে নন-কোটলিন কোডেও কল করতে হয়, কারণ বেশিরভাগ অ্যান্ড্রয়েড এপিআই জাভা প্রোগ্রামিং ভাষায় লেখা হয়।
জাভা এবং কোটলিনের আচরণের মধ্যে পার্থক্যের ক্ষেত্রে নলাবিলিটি একটি গুরুত্বপূর্ণ ক্ষেত্র। নলাবিলিটি সিনট্যাক্সের ক্ষেত্রে জাভা কম কঠোর।
উদাহরণস্বরূপ, Account ক্লাসের কয়েকটি বৈশিষ্ট্য রয়েছে, যার মধ্যে name নামক একটি String বৈশিষ্ট্যও রয়েছে। জাভাতে nullability সম্পর্কে Kotlin-এর নিয়ম নেই, পরিবর্তে আপনি একটি null মান নির্ধারণ করতে পারবেন কিনা তা স্পষ্টভাবে ঘোষণা করার জন্য ঐচ্ছিক nullability অ্যানোটেশনের উপর নির্ভর করে।
যেহেতু অ্যান্ড্রয়েড ফ্রেমওয়ার্কটি মূলত জাভাতে লেখা, তাই nullability অ্যানোটেশন ছাড়াই API গুলিতে কল করার সময় আপনি এই পরিস্থিতির সম্মুখীন হতে পারেন।
প্ল্যাটফর্মের ধরণ
যদি আপনি Kotlin ব্যবহার করে Java Account ক্লাসে সংজ্ঞায়িত একটি unannotated name সদস্যকে রেফারেন্স করেন, তাহলে কম্পাইলার জানে না যে Kotlin-এ String একটি String নাকি String? এর সাথে ম্যাপ করে। এই অস্পষ্টতা একটি প্ল্যাটফর্ম টাইপ , String! এর মাধ্যমে উপস্থাপন করা হয়।
Kotlin কম্পাইলারের কাছে String! কোন বিশেষ অর্থ নেই। String! একটি String অথবা একটি String? উভয়কেই প্রতিনিধিত্ব করতে পারে, এবং কম্পাইলার আপনাকে উভয় ধরণের একটি মান নির্ধারণ করতে দেয়। মনে রাখবেন যে আপনি যদি টাইপটিকে একটি String হিসাবে উপস্থাপন করেন এবং একটি null মান নির্ধারণ করেন তবে আপনি একটি NullPointerException ফেলে দেওয়ার ঝুঁকিতে থাকবেন।
এই সমস্যা সমাধানের জন্য, জাভাতে কোড লেখার সময় আপনার nullability অ্যানোটেশন ব্যবহার করা উচিত। এই অ্যানোটেশনগুলি জাভা এবং কোটলিন উভয় ডেভেলপারকেই সাহায্য করে।
উদাহরণস্বরূপ, জাভাতে Account ক্লাসটি যেভাবে সংজ্ঞায়িত করা হয়েছে তা এখানে:
public class Account implements Parcelable {
public final String name;
public final String type;
private final @Nullable String accessId;
...
}
সদস্য ভেরিয়েবলগুলির মধ্যে একটি, accessId , @Nullable দিয়ে টীকাযুক্ত, যা নির্দেশ করে যে এটি একটি নাল মান ধারণ করতে পারে। Kotlin তখন accessId String? হিসাবে বিবেচনা করবে।
একটি ভেরিয়েবল কখনই নাল হতে পারে না তা বোঝাতে, @NonNull অ্যানোটেশনটি ব্যবহার করুন:
public class Account implements Parcelable {
public final @NonNull String name;
...
}
এই পরিস্থিতিতে, কোটলিনে name একটি অ-নালযোগ্য String হিসেবে বিবেচনা করা হয়।
সমস্ত নতুন অ্যান্ড্রয়েড এপিআই এবং অনেক বিদ্যমান অ্যান্ড্রয়েড এপিআই-তে নলাবিলিটি অ্যানোটেশন অন্তর্ভুক্ত করা হয়েছে। অনেক জাভা লাইব্রেরি কোটলিন এবং জাভা ডেভেলপার উভয়কেই আরও ভালভাবে সমর্থন করার জন্য নলাবিলিটি অ্যানোটেশন যুক্ত করেছে।
বাতিলযোগ্যতা পরিচালনা করা
যদি আপনি জাভা টাইপ সম্পর্কে নিশ্চিত না হন, তাহলে আপনার এটিকে nullable হিসেবে বিবেচনা করা উচিত। উদাহরণস্বরূপ, Account ক্লাসের name সদস্যটি টীকাযুক্ত নয়, তাই আপনার এটিকে nullable String? বলে ধরে নেওয়া উচিত।
যদি আপনি এমনভাবে name ট্রিম করতে চান যাতে এর মান লিডিং বা ট্রেইলিং হোয়াইটস্পেস অন্তর্ভুক্ত না করে, তাহলে আপনি কোটলিনের trim ফাংশন ব্যবহার করতে পারেন। আপনি কয়েকটি ভিন্ন উপায়ে নিরাপদে একটি String? ট্রিম করতে পারেন। এই উপায়গুলির মধ্যে একটি হল not-null assertion অপারেটর , !! ব্যবহার করা, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
val account = Account("name", "type")
val accountName = account.name!!.trim()
!! অপারেটর তার বাম দিকের সবকিছুকে non-null হিসেবে বিবেচনা করে, তাই এই ক্ষেত্রে, আপনি name non-null String হিসেবে বিবেচনা করছেন। যদি এর বাম দিকের এক্সপ্রেশনের ফলাফল null হয়, তাহলে আপনার অ্যাপটি একটি NullPointerException নিক্ষেপ করে। এই অপারেটরটি দ্রুত এবং সহজ, তবে এটি অল্প পরিমাণে ব্যবহার করা উচিত, কারণ এটি আপনার কোডে NullPointerException এর উদাহরণ পুনরায় প্রবর্তন করতে পারে।
একটি নিরাপদ পছন্দ হল সেফ-কল অপারেটর , ?. ব্যবহার করা, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
val account = Account("name", "type")
val accountName = account.name?.trim()
সেফ-কল অপারেটর ব্যবহার করে, যদি name non-null হয়, তাহলে name?.trim() এর ফলাফল হল একটি name মান যার লিডিং বা ট্রেইলিং হোয়াইটস্পেস নেই। যদি name null হয়, তাহলে name?.trim() এর ফলাফল হল null । এর মানে হল যে এই স্টেটমেন্টটি কার্যকর করার সময় আপনার অ্যাপ কখনই NullPointerException থ্রো করতে পারবে না।
যদিও সেফ-কল অপারেটর আপনাকে একটি সম্ভাব্য NullPointerException থেকে বাঁচায়, এটি পরবর্তী স্টেটমেন্টে একটি null মান পাস করে। পরিবর্তে আপনি একটি Elvis অপারেটর ( ?: ) ব্যবহার করে অবিলম্বে null কেস পরিচালনা করতে পারেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"
যদি এলভিস অপারেটরের বাম দিকের এক্সপ্রেশনের ফলাফল null হয়, তাহলে ডান দিকের মানটি accountName এ নির্ধারিত হয়। এই কৌশলটি একটি ডিফল্ট মান প্রদানের জন্য কার্যকর যা অন্যথায় null হবে।
আপনি নিম্নলিখিত উদাহরণে দেখানো হিসাবে, কোনও ফাংশন থেকে তাড়াতাড়ি ফিরে আসার জন্য এলভিস অপারেটর ব্যবহার করতে পারেন:
fun validateAccount(account: Account?) {
val accountName = account?.name?.trim() ?: "Default name"
// account cannot be null beyond this point
account ?: return
...
}
অ্যান্ড্রয়েড এপিআই পরিবর্তন
অ্যান্ড্রয়েড এপিআই গুলো ক্রমশ কোটলিন-বান্ধব হয়ে উঠছে। AppCompatActivity এবং Fragment সহ অ্যান্ড্রয়েডের অনেক সাধারণ এপিআইতে নাল্যাবিলিটি অ্যানোটেশন থাকে এবং Fragment#getContext মতো কিছু কলে আরও কোটলিন-বান্ধব বিকল্প রয়েছে।
উদাহরণস্বরূপ, একটি Fragment এর Context অ্যাক্সেস করা প্রায় সবসময়ই null নয়, কারণ Fragment এ করা বেশিরভাগ কল তখনই হয় যখন Fragment একটি Activity ( Context এর একটি সাবক্লাস) এর সাথে সংযুক্ত থাকে। যাইহোক, Fragment#getContext সর্বদা একটি non-null মান প্রদান করে না, কারণ এমন কিছু পরিস্থিতি রয়েছে যেখানে একটি Fragment একটি Activity এর সাথে সংযুক্ত থাকে না। সুতরাং, Fragment#getContext এর রিটার্ন টাইপ বাতিলযোগ্য।
যেহেতু Fragment#getContext থেকে ফিরে আসা Context nullable (এবং @Nullable হিসাবে টীকাযুক্ত), আপনার Kotlin কোডে এটিকে Context? হিসাবে বিবেচনা করতে হবে। এর অর্থ হল এর বৈশিষ্ট্য এবং ফাংশন অ্যাক্সেস করার আগে পূর্বে উল্লিখিত অপারেটরগুলির মধ্যে একটি প্রয়োগ করা nullability মোকাবেলা করার জন্য। এই পরিস্থিতিতে কিছু ক্ষেত্রে, Android-এ বিকল্প API রয়েছে যা এই সুবিধা প্রদান করে। উদাহরণস্বরূপ, Fragment#requireContext একটি নন-নাল Context ফেরত দেয় এবং একটি IllegalStateException ছুঁড়ে দেয় যদি একটি Context null হলে কল করা হয়। এইভাবে, আপনি নিরাপদ-কল অপারেটর বা সমাধানের প্রয়োজন ছাড়াই ফলাফল Context নন-নাল হিসাবে বিবেচনা করতে পারেন।
সম্পত্তির সূচনা
কোটলিনের প্রোপার্টিগুলি ডিফল্টভাবে ইনিশিয়ালাইজ করা হয় না। এনক্লোজিং ক্লাস ইনিশিয়ালাইজ করার সময় এগুলি ইনিশিয়ালাইজ করা আবশ্যক।
আপনি কয়েকটি ভিন্ন উপায়ে বৈশিষ্ট্যগুলি ইনিশিয়ালাইজ করতে পারেন। নিম্নলিখিত উদাহরণে ক্লাস ঘোষণায় একটি মান নির্ধারণ করে একটি index ভেরিয়েবলকে কীভাবে ইনিশিয়ালাইজ করতে হয় তা দেখানো হয়েছে:
class LoginFragment : Fragment() {
val index: Int = 12
}
এই ইনিশিয়ালাইজেশনটি একটি ইনিশিয়ালাইজার ব্লকেও সংজ্ঞায়িত করা যেতে পারে:
class LoginFragment : Fragment() {
val index: Int
init {
index = 12
}
}
উপরের উদাহরণগুলিতে, যখন একটি LoginFragment তৈরি করা হয় তখন index শুরু হয়।
তবে, আপনার কিছু বৈশিষ্ট্য থাকতে পারে যা অবজেক্ট নির্মাণের সময় শুরু করা যাবে না। উদাহরণস্বরূপ, আপনি একটি Fragment মধ্যে থেকে একটি View উল্লেখ করতে চাইতে পারেন, যার অর্থ হল লেআউটটি প্রথমে স্ফীত করতে হবে। যখন একটি Fragment তৈরি করা হয় তখন মুদ্রাস্ফীতি ঘটে না। পরিবর্তে, Fragment#onCreateView কল করার সময় এটি স্ফীত হয়।
এই পরিস্থিতি মোকাবেলার একটি উপায় হল ভিউটিকে বাতিলযোগ্য হিসাবে ঘোষণা করা এবং যত তাড়াতাড়ি সম্ভব এটি শুরু করা, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
class LoginFragment : Fragment() {
private var statusTextView: TextView? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView?.setText(R.string.auth_failed)
}
}
যদিও এটি প্রত্যাশা অনুযায়ী কাজ করে, এখন আপনাকে যখনই ভিউ রেফারেন্স করবেন তখনই View nullability পরিচালনা করতে হবে। একটি ভালো সমাধান হল View ইনিশিয়ালাইজেশনের জন্য lateinit ব্যবহার করা, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
class LoginFragment : Fragment() {
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView.setText(R.string.auth_failed)
}
}
lateinit কীওয়ার্ড আপনাকে কোনও বস্তু তৈরি করার সময় কোনও সম্পত্তির ইনিশিয়ালাইজেশন এড়াতে সাহায্য করে। যদি আপনার সম্পত্তিটি ইনিশিয়ালাইজ করার আগে রেফারেন্স করা হয়, তাহলে Kotlin একটি UninitializedPropertyAccessException ব্যবহার করে, তাই যত তাড়াতাড়ি সম্ভব আপনার সম্পত্তিটি ইনিশিয়ালাইজ করতে ভুলবেন না।