অ্যান্ড্রয়েডের সাথে সাধারণ কোটলিন প্যাটার্ন ব্যবহার করুন

এই বিষয়টি অ্যান্ড্রয়েডের জন্য বিকাশ করার সময় কোটলিন ভাষার সবচেয়ে দরকারী দিকগুলির উপর ফোকাস করে৷

টুকরা সঙ্গে কাজ

নিম্নলিখিত বিভাগগুলি কোটলিনের সেরা বৈশিষ্ট্যগুলিকে হাইলাইট করতে Fragment উদাহরণগুলি ব্যবহার করে৷

উত্তরাধিকার

আপনি 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)
}

শূন্যতা এবং প্রারম্ভিকতা

পূর্ববর্তী উদাহরণে, ওভাররাইড করা পদ্ধতির কিছু প্যারামিটারে প্রশ্ন চিহ্নের সাথে প্রত্যয় যুক্ত আছে ? . এটি নির্দেশ করে যে এই প্যারামিটারগুলির জন্য পাস করা আর্গুমেন্টগুলি শূন্য হতে পারে। তাদের শূন্যতা নিরাপদে পরিচালনা করতে ভুলবেন না।

কোটলিনে, অবজেক্ট ঘোষণা করার সময় আপনাকে অবশ্যই একটি বস্তুর বৈশিষ্ট্যগুলি শুরু করতে হবে। এটি বোঝায় যে আপনি যখন একটি ক্লাসের একটি উদাহরণ পান, আপনি অবিলম্বে এর যেকোন অ্যাক্সেসযোগ্য বৈশিষ্ট্য উল্লেখ করতে পারেন। একটি Fragment View অবজেক্টগুলি, তবে, Fragment#onCreateView কল না করা পর্যন্ত স্ফীত হওয়ার জন্য প্রস্তুত নয়, তাই আপনার একটি View এর জন্য সম্পত্তি প্রারম্ভিকতা স্থগিত করার একটি উপায় প্রয়োজন৷

lateinit আপনাকে প্রপার্টি ইনিশিয়ালাইজেশন পিছিয়ে দিতে দেয়। lateinit ব্যবহার করার সময়, আপনার যত তাড়াতাড়ি সম্ভব আপনার সম্পত্তি শুরু করা উচিত।

নিম্নলিখিত উদাহরণটি onViewCreatedView অবজেক্ট বরাদ্দ করতে 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 ইন্টারফেস প্রয়োগ করে Android এ ক্লিক ইভেন্ট শুনতে পারেন। Button অবজেক্টে একটি setOnClickListener() ফাংশন থাকে যা OnClickListener এর বাস্তবায়নে লাগে।

OnClickListener একটি একক বিমূর্ত পদ্ধতি রয়েছে, onClick() , যা আপনাকে অবশ্যই প্রয়োগ করতে হবে। যেহেতু setOnClickListener() সর্বদা একটি OnClickListener একটি আর্গুমেন্ট হিসাবে নেয়, এবং যেহেতু OnClickListener সবসময় একই একক বিমূর্ত পদ্ধতি থাকে, এই বাস্তবায়নটি Kotlin-এ একটি বেনামী ফাংশন ব্যবহার করে উপস্থাপন করা যেতে পারে। এই প্রক্রিয়াটি একক বিমূর্ত পদ্ধতি রূপান্তর , বা SAM রূপান্তর নামে পরিচিত।

SAM রূপান্তর আপনার কোডকে যথেষ্ট পরিচ্ছন্ন করে তুলতে পারে। নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি Button জন্য একটি OnClickListener বাস্তবায়ন করতে SAM রূপান্তর ব্যবহার করতে হয়:

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 একটি অনন্য উদাহরণের প্রয়োজন নেই, তাই আপনার এটি একটি সহচর বস্তুতে সংজ্ঞায়িত করা উচিত:

class LoginFragment : Fragment() {

    ...

    companion object {
        private const val TAG = "LoginFragment"
    }
}

আপনি ফাইলের শীর্ষ স্তরে TAG সংজ্ঞায়িত করতে পারেন, তবে ফাইলটিতে প্রচুর পরিমাণে ভেরিয়েবল, ফাংশন এবং ক্লাস থাকতে পারে যা উপরের স্তরে সংজ্ঞায়িত করা হয়। কম্প্যানিয়ন অবজেক্টগুলি সেই ক্লাসের কোনও নির্দিষ্ট উদাহরণ উল্লেখ না করে ভেরিয়েবল, ফাংশন এবং ক্লাস সংজ্ঞা সংযোগ করতে সহায়তা করে।

সম্পত্তি অর্পণ

বৈশিষ্ট্যগুলি শুরু করার সময়, আপনি Android এর কিছু সাধারণ নিদর্শনগুলি পুনরাবৃত্তি করতে পারেন, যেমন একটি Fragment মধ্যে একটি ViewModel অ্যাক্সেস করা। অতিরিক্ত ডুপ্লিকেট কোড এড়াতে, আপনি Kotlin এর সম্পত্তি প্রতিনিধি সিনট্যাক্স ব্যবহার করতে পারেন।

private val viewModel: LoginViewModel by viewModels()

সম্পত্তি অর্পণ একটি সাধারণ বাস্তবায়ন প্রদান করে যা আপনি আপনার অ্যাপ জুড়ে পুনরায় ব্যবহার করতে পারেন। Android KTX আপনার জন্য কিছু সম্পত্তি প্রতিনিধি প্রদান করে। viewModels , উদাহরণস্বরূপ, একটি ViewModel পুনরুদ্ধার করে যা বর্তমান Fragment স্কোপ করা হয়।

সম্পত্তি অর্পণ প্রতিফলন ব্যবহার করে, যা কিছু কর্মক্ষমতা ওভারহেড যোগ করে। ট্রেডঅফ একটি সংক্ষিপ্ত সিনট্যাক্স যা বিকাশের সময় বাঁচায়।

শূন্যতা

Kotlin আপনার অ্যাপ জুড়ে টাইপ-নিরাপত্তা বজায় রাখে এমন কঠোর বাতিল করার নিয়ম প্রদান করে। কোটলিনে, অবজেক্টের রেফারেন্সে ডিফল্টরূপে শূন্য মান থাকতে পারে না। একটি ভেরিয়েবলের জন্য একটি নাল মান নির্ধারণ করতে, আপনাকে যোগ করে একটি বাতিলযোগ্য পরিবর্তনশীল প্রকার ঘোষণা করতে হবে ? বেস টাইপের শেষ পর্যন্ত।

উদাহরণ হিসেবে, নিচের অভিব্যক্তিটি কোটলিনে অবৈধ। name String ধরণের এবং বাতিলযোগ্য নয়:

val name: String = null

একটি নাল মান অনুমোদন করার জন্য, আপনাকে অবশ্যই একটি বাতিলযোগ্য String টাইপ ব্যবহার করতে হবে, String? , নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

val name: String? = null

ইন্টারঅপারেবিলিটি

Kotlin এর কঠোর নিয়ম আপনার কোড নিরাপদ এবং আরো সংক্ষিপ্ত. এই নিয়মগুলি একটি NullPointerException থাকার সম্ভাবনা কম করে যা আপনার অ্যাপকে ক্র্যাশ করতে পারে৷ তাছাড়া, তারা আপনার কোডে নাল চেকের সংখ্যা কমিয়ে দেয়।

প্রায়শই, একটি অ্যান্ড্রয়েড অ্যাপ লেখার সময় আপনাকে অবশ্যই নন-কোটলিন কোডে কল করতে হবে, কারণ বেশিরভাগ অ্যান্ড্রয়েড এপিআই জাভা প্রোগ্রামিং ভাষায় লেখা হয়।

শূন্যতা হল একটি মূল ক্ষেত্র যেখানে জাভা এবং কোটলিন আচরণে ভিন্ন। জাভা শূন্যতা সিনট্যাক্সের সাথে কম কঠোর।

উদাহরণ হিসাবে, Account ক্লাসের name একটি String বৈশিষ্ট্য সহ কয়েকটি বৈশিষ্ট্য রয়েছে। জাভাতে শূন্যতার বিষয়ে কোটলিনের নিয়ম নেই, পরিবর্তে আপনি একটি নাল মান নির্ধারণ করতে পারেন কিনা তা স্পষ্টভাবে ঘোষণা করার জন্য ঐচ্ছিক শূন্যতার টীকাগুলির উপর নির্ভর করে।

যেহেতু অ্যান্ড্রয়েড ফ্রেমওয়ার্ক প্রাথমিকভাবে জাভাতে লেখা হয়েছে, তাই আপনি নললেবিলিটি টীকা ছাড়াই এপিআই-এ কল করার সময় এই পরিস্থিতিতে পড়তে পারেন।

প্ল্যাটফর্ম প্রকার

আপনি যদি জাভা Account ক্লাসে সংজ্ঞায়িত একটি অব্যক্ত name সদস্যকে উল্লেখ করতে কোটলিন ব্যবহার করেন, তবে কম্পাইলার জানেন না যে String একটি String বা একটি String? কোটলিনে। এই অস্পষ্টতা একটি প্ল্যাটফর্ম টাইপ , String! .

String! কোটলিন কম্পাইলারের কোন বিশেষ অর্থ নেই। String! একটি String বা একটি String? , এবং কম্পাইলার আপনাকে উভয় প্রকারের একটি মান নির্ধারণ করতে দেয়। মনে রাখবেন যে আপনি একটি NullPointerException নিক্ষেপ করার ঝুঁকি নিয়ে থাকেন যদি আপনি টাইপটিকে একটি String হিসাবে উপস্থাপন করেন এবং একটি নাল মান নির্ধারণ করেন।

এই সমস্যাটি মোকাবেলা করার জন্য, আপনি যখনই জাভাতে কোড লিখবেন তখনই আপনার শূন্যতা টীকা ব্যবহার করা উচিত। এই টীকাগুলি জাভা এবং কোটলিন উভয় ডেভেলপারদের সাহায্য করে।

উদাহরণস্বরূপ, এখানে Account ক্লাসটি জাভাতে সংজ্ঞায়িত করা হয়েছে:

public class Account implements Parcelable {
    public final String name;
    public final String type;
    private final @Nullable String accessId;

    ...
}

সদস্য ভেরিয়েবলগুলির মধ্যে একটি, accessId , @Nullable দিয়ে টীকা করা হয়েছে, এটি ইঙ্গিত করে যে এটি একটি নাল মান ধরে রাখতে পারে। কোটলিন তখন accessId String? .

একটি ভেরিয়েবল কখনই শূন্য হতে পারে না তা নির্দেশ করতে, @NonNull টীকাটি ব্যবহার করুন:

public class Account implements Parcelable {
    public final @NonNull String name;
    ...
}

এই পরিস্থিতিতে, কোটলিনে name একটি অ-নূলযোগ্য String হিসাবে বিবেচনা করা হয়৷

সমস্ত নতুন অ্যান্ড্রয়েড এপিআই এবং অনেকগুলি বিদ্যমান অ্যান্ড্রয়েড এপিআই-এ শূন্যতা টীকা অন্তর্ভুক্ত করা হয়েছে। অনেক জাভা লাইব্রেরি কোটলিন এবং জাভা ডেভেলপার উভয়কে আরও ভালভাবে সমর্থন করার জন্য শূন্যতা টীকা যুক্ত করেছে।

শূন্যতা হ্যান্ডলিং

আপনি যদি জাভা টাইপ সম্পর্কে অনিশ্চিত হন তবে আপনার এটি বাতিলযোগ্য বলে বিবেচনা করা উচিত। একটি উদাহরণ হিসাবে, Account ক্লাসের সদস্যের name টীকা করা হয় না, তাই আপনার এটি একটি বাতিলযোগ্য String বলে ধরে নেওয়া উচিত।

আপনি যদি name ট্রিম করতে চান যাতে এর মান অগ্রণী বা পিছনের সাদা স্থান অন্তর্ভুক্ত না করে, আপনি Kotlin এর trim ফাংশন ব্যবহার করতে পারেন। আপনি নিরাপদে একটি String? কয়েকটি ভিন্ন উপায়ে। এই উপায়গুলির মধ্যে একটি হল নট-নাল অ্যাসারশন অপারেটর ব্যবহার করা, !! , নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

val account = Account("name", "type")
val accountName = account.name!!.trim()

দ্য !! অপারেটর তার বাম দিকের সবকিছুকে নন-নাল হিসাবে বিবেচনা করে, তাই এই ক্ষেত্রে, আপনি name একটি নন-নাল String হিসাবে বিবেচনা করছেন। যদি তার বাম দিকের অভিব্যক্তিটির ফলাফল নাল হয়, তাহলে আপনার অ্যাপটি একটি NullPointerException নিক্ষেপ করে। এই অপারেটরটি দ্রুত এবং সহজ, কিন্তু এটি অল্প ব্যবহার করা উচিত, কারণ এটি আপনার কোডে NullPointerException এর উদাহরণগুলিকে পুনরায় প্রবর্তন করতে পারে৷

একটি নিরাপদ পছন্দ হল নিরাপদ-কল অপারেটর ব্যবহার করা, ?. , নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

val account = Account("name", "type")
val accountName = account.name?.trim()

সেফ-কল অপারেটর ব্যবহার করে, যদি name নন-নাল হয়, তাহলে name?.trim() এর ফলাফল হল একটি নামের মান যা অগ্রণী বা পিছনের হোয়াইটস্পেস ছাড়াই। যদি name নাল হয়, তাহলে name?.trim() এর ফলাফল null হয়। এর মানে হল যে এই বিবৃতিটি কার্যকর করার সময় আপনার অ্যাপ কখনই একটি NullPointerException নিক্ষেপ করতে পারে না।

যদিও নিরাপদ-কল অপারেটর আপনাকে একটি সম্ভাব্য NullPointerException থেকে বাঁচায়, এটি পরবর্তী বিবৃতিতে একটি নাল মান পাস করে। আপনি পরিবর্তে একটি এলভিস অপারেটর ( ?: ) ব্যবহার করে অবিলম্বে নাল কেসগুলি পরিচালনা করতে পারেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"

যদি এলভিস অপারেটরের বাম দিকের অভিব্যক্তির ফলাফল শূন্য হয়, তাহলে ডানদিকের মানটি accountName এ বরাদ্দ করা হয়। এই কৌশলটি একটি ডিফল্ট মান প্রদানের জন্য দরকারী যা অন্যথায় শূন্য হবে।

আপনি এলভিস অপারেটর ব্যবহার করতে পারেন একটি ফাংশন থেকে তাড়াতাড়ি ফিরে আসতে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

fun validateAccount(account: Account?) {
    val accountName = account?.name?.trim() ?: "Default name"

    // account cannot be null beyond this point
    account ?: return

    ...
}

অ্যান্ড্রয়েড এপিআই পরিবর্তন

অ্যান্ড্রয়েড এপিআই ক্রমশ কোটলিন-বান্ধব হয়ে উঠছে। AppCompatActivity এবং Fragment সহ Android-এর অনেক-সাধারণ API-এ শূন্যতা টীকা থাকে এবং Fragment#getContext এর মতো কিছু কলে আরও কোটলিন-বান্ধব বিকল্প রয়েছে।

উদাহরণস্বরূপ, একটি Fragment Context অ্যাক্সেস করা প্রায় সবসময়ই নন-নাল থাকে, যেহেতু আপনি একটি Fragment যে কলগুলি করেন তার বেশিরভাগই ঘটে যখন Fragment একটি Activity সাথে সংযুক্ত থাকে ( Context একটি উপশ্রেণী)। এটি বলেছে, Fragment#getContext সর্বদা একটি নন-নাল মান ফেরত দেয় না, কারণ এমন পরিস্থিতিতে রয়েছে যেখানে একটি Fragment একটি Activity সাথে সংযুক্ত থাকে না। সুতরাং, Fragment#getContext এর রিটার্ন প্রকারটি বাতিলযোগ্য।

যেহেতু Fragment#getContext থেকে প্রত্যাবর্তিত Context বাতিলযোগ্য (এবং @Nullable হিসাবে টীকা করা হয়েছে), আপনাকে অবশ্যই এটিকে একটি Context? আপনার কোটলিন কোডে। এর অর্থ হল পূর্বে উল্লিখিত অপারেটরগুলির একটিকে এর বৈশিষ্ট্য এবং ফাংশনগুলি অ্যাক্সেস করার আগে শূন্যতা মোকাবেলায় প্রয়োগ করা। এই ধরনের কিছু পরিস্থিতিতে, অ্যান্ড্রয়েডে বিকল্প API রয়েছে যা এই সুবিধা প্রদান করে। Fragment#requireContext , উদাহরণস্বরূপ, একটি নন-নাল Context প্রদান করে এবং একটি IllegalStateException ছুড়ে দেয় যদি একটি Context নাল হয়ে যায়। এইভাবে, আপনি নিরাপদ-কল অপারেটর বা সমাধানের প্রয়োজন ছাড়াই ফলাফলের Context নন-নাল হিসাবে বিবেচনা করতে পারেন।

সম্পত্তি প্রারম্ভিকতা

Kotlin-এর বৈশিষ্ট্যগুলি ডিফল্টরূপে আরম্ভ করা হয় না। যখন তাদের এনক্লোসিং ক্লাস আরম্ভ করা হয় তখন তাদের অবশ্যই আরম্ভ করা উচিত।

আপনি কয়েকটি ভিন্ন উপায়ে বৈশিষ্ট্য শুরু করতে পারেন। নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি 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 বাতিলতা পরিচালনা করতে হবে। একটি ভাল সমাধান হল 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 নিক্ষেপ করে, তাই যত তাড়াতাড়ি সম্ভব আপনার সম্পত্তি শুরু করতে ভুলবেন না।