এই ডকুমেন্টটি কোটলিন প্রোগ্রামিং ল্যাঙ্গুয়েজে লেখা সোর্স কোডের জন্য গুগলের অ্যান্ড্রয়েড কোডিং স্ট্যান্ডার্ডের সম্পূর্ণ সংজ্ঞা হিসেবে কাজ করে। একটি কোটলিন সোর্স ফাইলকে তখনই গুগল অ্যান্ড্রয়েড স্টাইলের বলে গণ্য করা হবে, যদি এবং কেবল যদি এটি এখানে বর্ণিত নিয়মগুলো মেনে চলে।
অন্যান্য প্রোগ্রামিং স্টাইল গাইডের মতোই, এখানে শুধু ফরম্যাটিংয়ের নান্দনিক বিষয়ই নয়, বরং অন্যান্য ধরনের কনভেনশন বা কোডিং স্ট্যান্ডার্ডও অন্তর্ভুক্ত রয়েছে। তবে, এই ডকুমেন্টটি মূলত সেইসব কঠোর ও অপরিবর্তনীয় নিয়মের উপর আলোকপাত করে যা আমরা বিশ্বব্যাপী অনুসরণ করি, এবং এমন কোনো পরামর্শ দেওয়া থেকে বিরত থাকে যা (মানুষ বা টুল দ্বারা) স্পষ্টভাবে প্রয়োগযোগ্য নয়।
উৎস ফাইল
সকল সোর্স ফাইল অবশ্যই UTF-8 ফরম্যাটে এনকোড করতে হবে।
নামকরণ
যদি কোনো সোর্স ফাইলে কেবল একটি টপ-লেভেল ক্লাস থাকে, তাহলে ফাইলের নামটি কেস-সেনসিটিভ নামের সাথে .kt এক্সটেনশনসহ হওয়া উচিত। অন্যথায়, যদি কোনো সোর্স ফাইলে একাধিক টপ-লেভেল ডিক্লারেশন থাকে, তাহলে ফাইলের বিষয়বস্তু বর্ণনা করে এমন একটি নাম বেছে নিন, প্যাসকেলকেস (PascalCase) প্রয়োগ করুন (ফাইলের নামটি বহুবচন হলে ক্যামেলকেস (camelCase) গ্রহণযোগ্য), এবং শেষে .kt এক্সটেনশনটি যুক্ত করুন।
// MyClass.kt class MyClass { }
// Bar.kt class Bar { } fun Runnable.toBar(): Bar = Bar()
// Map.kt fun <T, O> Set<T>.map(func: (T) -> O): List<O> = emptyList() fun <T, O> List<T>.map(func: (T) -> O): List<O> = emptyList()
// extensions.kt fun MyClass.process() = { /* ... */ } fun MyResult.print() = { /* ... */ }
বিশেষ চরিত্র
ফাঁকা স্থান অক্ষর
লাইন টার্মিনেটর সিকোয়েন্স ছাড়া, ASCII হরাইজন্টাল স্পেস ক্যারেক্টার (0x20) হলো একমাত্র হোয়াইটস্পেস ক্যারেক্টার যা একটি সোর্স ফাইলের যেকোনো স্থানে দেখা যায়। এর থেকে বোঝা যায় যে:
- স্ট্রিং এবং ক্যারেক্টার লিটারেলের মধ্যে থাকা অন্য সব হোয়াইটস্পেস ক্যারেক্টারকে এস্কেপ করা হয়।
- ইন্ডেন্টেশনের জন্য ট্যাব ক্যারেক্টার ব্যবহার করা হয় না ।
বিশেষ পলায়ন ক্রম
যেসব অক্ষরের বিশেষ এস্কেপ সিকোয়েন্স ( \b , \n , \r , \t , \' , \" , \\ , এবং \$ ) আছে, সেগুলোর ক্ষেত্রে সংশ্লিষ্ট ইউনিকোড (যেমন, \u000a ) এস্কেপের পরিবর্তে সেই সিকোয়েন্সটিই ব্যবহৃত হয়।
নন-ASCII অক্ষর
অবশিষ্ট নন-ASCII অক্ষরগুলোর জন্য, হয় প্রকৃত ইউনিকোড অক্ষর (যেমন, ∞ ) অথবা এর সমতুল্য ইউনিকোড এস্কেপ (যেমন, \u221e ) ব্যবহার করা হয়। এই নির্বাচন শুধুমাত্র এই বিষয়ের উপর নির্ভর করে যে কোনটি কোডটিকে পড়া ও বোঝা সহজ করে তোলে। যেকোনো স্থানে মুদ্রণযোগ্য অক্ষরের জন্য ইউনিকোড এস্কেপ ব্যবহার নিরুৎসাহিত করা হয় এবং স্ট্রিং লিটারেল ও কমেন্টের বাইরে এর ব্যবহার কঠোরভাবে নিরুৎসাহিত করা হয়।
| উদাহরণ | আলোচনা |
|---|---|
val unitAbbrev = "μs" | সর্বোত্তম: কোনো মন্তব্য ছাড়াই যা পুরোপুরি স্পষ্ট। |
val unitAbbrev = "\u03bcs" // μs | খারাপ: প্রিন্টযোগ্য অক্ষরের সাথে এস্কেপ ব্যবহার করার কোনো কারণ নেই। |
val unitAbbrev = "\u03bcs" | দুর্বল: পাঠকের কোনো ধারণা নেই এটি কী। |
return "\ufeff" + content | ভালো: মুদ্রণ-অযোগ্য অক্ষরগুলোর জন্য এস্কেপ ব্যবহার করুন এবং প্রয়োজনে মন্তব্য করুন। |
কাঠামো
একটি .kt ফাইলে ক্রমানুসারে নিম্নলিখিত বিষয়গুলো থাকে:
- কপিরাইট এবং/অথবা লাইসেন্স শিরোনাম (ঐচ্ছিক)
- ফাইল-স্তরের টীকা
- প্যাকেজ বিবৃতি
- আমদানি বিবৃতি
- শীর্ষ-স্তরের ঘোষণা
এই বিভাগগুলোর প্রতিটির মাঝে ঠিক একটি ফাঁকা লাইন রয়েছে।
কপিরাইট / লাইসেন্স
ফাইলে যদি কোনো কপিরাইট বা লাইসেন্স হেডার রাখার প্রয়োজন হয়, তবে তা একদম শুরুতে একটি মাল্টি-লাইন কমেন্টের মধ্যে রাখা উচিত।
/* * Copyright 2017 Google, Inc. * * ... */
KDoc-স্টাইল বা এক-লাইনের কমেন্ট ব্যবহার করবেন না।
/** * Copyright 2017 Google, Inc. * * ... */
// Copyright 2017 Google, Inc. // // ...
ফাইল-স্তরের টীকা
'file' use-site টার্গেটযুক্ত অ্যানোটেশনগুলো যেকোনো হেডার কমেন্ট এবং প্যাকেজ ডিক্লারেশনের মাঝে স্থাপন করা হয়।
প্যাকেজ বিবৃতি
প্যাকেজ স্টেটমেন্ট কোনো কলাম সীমার অধীন নয় এবং এটি কখনো লাইন-র্যাপ করা হয় না।
আমদানি বিবৃতি
ক্লাস, ফাংশন এবং প্রপার্টির ইম্পোর্ট স্টেটমেন্টগুলো একটি একক তালিকায় একত্রিত করা হয় এবং ASCII অনুসারে সাজানো হয়।
যেকোনো ধরনের ওয়াইল্ডকার্ড ইম্পোর্ট অনুমোদিত নয়।
প্যাকেজ স্টেটমেন্টের মতোই, ইমপোর্ট স্টেটমেন্টের ক্ষেত্রে কোনো কলাম সীমা থাকে না এবং এগুলো কখনো লাইন-র্যাপ করা হয় না।
শীর্ষ-স্তরের ঘোষণা
একটি .kt ফাইলের শীর্ষ স্তরে এক বা একাধিক টাইপ, ফাংশন, প্রোপার্টি বা টাইপ অ্যালিয়াস ঘোষণা করা যায়।
একটি ফাইলের বিষয়বস্তু একটিমাত্র থিমের উপর কেন্দ্র করে হওয়া উচিত। এর উদাহরণ হতে পারে একটিমাত্র পাবলিক টাইপ অথবা একগুচ্ছ এক্সটেনশন ফাংশন, যা একাধিক রিসিভার টাইপের উপর একই অপারেশন সম্পাদন করে। সম্পর্কহীন ডিক্লারেশনগুলোকে তাদের নিজস্ব ফাইলে আলাদা করা উচিত এবং একটি ফাইলের মধ্যে পাবলিক ডিক্লারেশনের সংখ্যা যথাসম্ভব কম রাখা উচিত।
একটি ফাইলের বিষয়বস্তুর সংখ্যা বা ক্রমের উপর কোনো সুস্পষ্ট বিধিনিষেধ আরোপ করা হয় না।
সোর্স ফাইলগুলো সাধারণত উপর থেকে নিচে পড়া হয়, যার অর্থ হলো, এদের ক্রমটি এমন হওয়া উচিত যাতে উপরের দিকের ডিক্লারেশনগুলো নিচেরগুলো বুঝতে সাহায্য করে। বিভিন্ন ফাইল তাদের বিষয়বস্তু ভিন্নভাবে সাজাতে পারে। একইভাবে, একটি ফাইলে ১০০টি প্রোপার্টি, অন্যটিতে ১০টি ফাংশন এবং আরও একটিতে মাত্র একটি ক্লাস থাকতে পারে।
গুরুত্বপূর্ণ বিষয় হলো, প্রতিটি ফাইল একটি যৌক্তিক ক্রম অনুসরণ করে, যা এর রক্ষণাবেক্ষণকারীকে জিজ্ঞাসা করা হলে তিনি ব্যাখ্যা করতে পারবেন। উদাহরণস্বরূপ, নতুন ফাংশনগুলো অভ্যাসবশত ফাইলের শেষে যোগ করা হয় না, কারণ তাতে “যোগ করার তারিখ অনুযায়ী কালানুক্রমিক” বিন্যাস তৈরি হবে, যা কোনো যৌক্তিক বিন্যাস নয়।
শ্রেণী সদস্যের অর্ডার
একটি ক্লাসের মধ্যে মেম্বারদের ক্রম শীর্ষ-স্তরের ডিক্লারেশনের নিয়মগুলোই অনুসরণ করে।
ফরম্যাটিং
ব্রেসেস
when ব্রাঞ্চ এবং if এক্সপ্রেশনের ক্ষেত্রে বন্ধনীর প্রয়োজন হয় না, যদি সেগুলোতে একটির বেশি else ব্রাঞ্চ না থাকে এবং যা একটিমাত্র লাইনে আঁটে।
if (string.isEmpty()) return val result = if (string.isEmpty()) DEFAULT_VALUE else string when (value) { 0 -> return // … }
অন্যথায়, যেকোনো if , for , when , branch, do , এবং while স্টেটমেন্ট ও এক্সপ্রেশনের জন্য বন্ধনী (braces) আবশ্যক, এমনকি যখন বডি খালি থাকে বা শুধুমাত্র একটি স্টেটমেন্ট থাকে তখনও।
if (string.isEmpty()) return // WRONG! if (string.isEmpty()) { return // Okay } if (string.isEmpty()) return // WRONG else doLotsOfProcessingOn(string, otherParametersHere) if (string.isEmpty()) { return // Okay } else { doLotsOfProcessingOn(string, otherParametersHere) }
অ-খালি ব্লক
অ-খালি ব্লক এবং ব্লক-সদৃশ গঠনের ক্ষেত্রে বন্ধনীগুলো কার্নিঘান ও রিচি শৈলী ("মিশরীয় বন্ধনী") অনুসরণ করে:
- প্রথম বন্ধনীর আগে কোনো লাইন ব্রেক নেই।
- প্রথম বন্ধনীর পরে লাইন ব্রেক।
- বন্ধনী বন্ধনীর আগে লাইন ব্রেক।
- বন্ধনীটির পরে লাইন ব্রেক তখনই হবে, যখন সেই বন্ধনীটি কোনো স্টেটমেন্ট অথবা কোনো ফাংশন, কনস্ট্রাক্টর বা নামযুক্ত ক্লাসের বডি শেষ করে। উদাহরণস্বরূপ, বন্ধনীটির পরে যদি
elseবা একটি কমা থাকে, তবে তার পরে কোনো লাইন ব্রেক হবে না ।
return Runnable { while (condition()) { foo() } }
return object : MyClass() { override fun foo() { if (condition()) { try { something() } catch (e: ProblemException) { recover() } } else if (otherCondition()) { somethingElse() } else { lastThing() } } }
enum ক্লাসের ক্ষেত্রে কয়েকটি ব্যতিক্রম নিচে দেওয়া হলো।
খালি ব্লক
একটি খালি ব্লক বা ব্লক-সদৃশ কাঠামো অবশ্যই K&R শৈলীতে হতে হবে।
try { doSomething() } catch (e: Exception) {} // WRONG!
try { doSomething() } catch (e: Exception) { } // Okay
অভিব্যক্তি
এক্সপ্রেশন হিসেবে ব্যবহৃত কোনো if/else কন্ডিশনাল থেকে শুধুমাত্র তখনই বন্ধনী বাদ দেওয়া যেতে পারে, যদি সম্পূর্ণ এক্সপ্রেশনটি এক লাইনে আঁটে।
val value = if (string.isEmpty()) 0 else 1 // Okay
val value = if (string.isEmpty()) // WRONG! 0 else 1
val value = if (string.isEmpty()) { // Okay 0 } else { 1 }
খাঁজ
প্রতিবার একটি নতুন ব্লক বা ব্লক-সদৃশ কাঠামো খোলা হলে, ইন্ডেন্ট চারটি স্পেস বেড়ে যায়। ব্লকটি শেষ হলে, ইন্ডেন্ট পূর্ববর্তী স্তরে ফিরে আসে। এই ইন্ডেন্ট স্তরটি পুরো ব্লক জুড়ে কোড এবং কমেন্ট উভয়ের ক্ষেত্রেই প্রযোজ্য।
প্রতি লাইনে একটি বিবৃতি
প্রতিটি বিবৃতির পরে একটি লাইন ব্রেক থাকে। সেমিকোলন ব্যবহার করা হয় না।
লাইন মোড়ানো
কোডের প্রতিটি কলামের জন্য ১০০ অক্ষরের সীমা রয়েছে। নিচে উল্লিখিত ব্যতিক্রম ছাড়া, এই সীমা অতিক্রমকারী যেকোনো লাইনকে অবশ্যই লাইন-র্যাপ করতে হবে, যেমনটি নিচে ব্যাখ্যা করা হয়েছে।
ব্যতিক্রম:
- যেসব লাইনে কলামের সীমা মেনে চলা সম্ভব নয় (উদাহরণস্বরূপ, KDoc-এ একটি দীর্ঘ URL)
-
packageএবংimportবিবৃতি - কমেন্টের মধ্যে থাকা কমান্ড লাইন যা কাট-এন্ড-পেস্ট করে শেলে নেওয়া যেতে পারে।
কোথায় ভাঙতে হবে
লাইন-র্যাপিংয়ের মূল নির্দেশিকা হলো: উচ্চতর বাক্যগঠনগত স্তরে ভাঙতে পছন্দ করা। আরও:
- যখন কোনো অপারেটর বা ইনফিক্স ফাংশনের নামে লাইন ভাঙা হয়, তখন লাইনটি ওই অপারেটর বা ইনফিক্স ফাংশনের নামের পরে ভাঙে।
- যখন একটি লাইন নিম্নলিখিত “অপারেটর-সদৃশ” চিহ্নগুলিতে ভাঙা হয়, তখন ছেদটি চিহ্নটির আগে আসে:
- বিন্দু বিভাজক (
.,?.)। - সদস্য রেফারেন্সের দুটি কোলন (
::)।
- বিন্দু বিভাজক (
- একটি মেথড বা কনস্ট্রাক্টরের নাম তার পরের খোলা বন্ধনীর (
() সাথে সংযুক্ত থাকে। - একটি কমা (
,) তার পূর্ববর্তী টোকেনের সাথে সংযুক্ত থাকে। - একটি ল্যাম্বডা অ্যারো (
->) তার পূর্ববর্তী আর্গুমেন্ট তালিকার সাথে সংযুক্ত থাকে।
কার্যাবলী
যখন কোনো ফাংশন সিগনেচার একটি লাইনে আঁটে না, তখন প্রতিটি প্যারামিটার ডিক্লারেশনকে আলাদা লাইনে ভাগ করুন। এই ফরম্যাটে সংজ্ঞায়িত প্যারামিটারগুলিতে একটি সিঙ্গেল ইনডেন্ট (+4) ব্যবহার করতে হবে। বন্ধনী ( ) ) এবং রিটার্ন টাইপ কোনো অতিরিক্ত ইনডেন্ট ছাড়াই তাদের নিজস্ব লাইনে রাখা হয়।
fun <T> Iterable<T>.joinToString( separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "" ): String { // ... }
অভিব্যক্তি ফাংশন
যখন কোনো ফাংশনে কেবল একটিমাত্র এক্সপ্রেশন থাকে, তখন সেটিকে একটি এক্সপ্রেশন ফাংশন হিসেবে উপস্থাপন করা যায়।
override fun toString(): String { return "Hey" }
override fun toString(): String = "Hey"
বৈশিষ্ট্য
যখন কোনো প্রপার্টি ইনিশিয়ালাইজার একটি লাইনে আঁটে না, তখন সমান চিহ্নের ( = ) পরে ব্রেক করুন এবং একটি ইনডেন্ট ব্যবহার করুন।
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
get এবং/অথবা set ফাংশন ডিক্লেয়ারকারী প্রপার্টিগুলোকে সাধারণ ইনডেন্ট (+4) সহ আলাদা লাইনে রাখতে হবে। ফাংশনের মতোই একই নিয়ম ব্যবহার করে এগুলোকে ফরম্যাট করুন।
var directory: File? = null set(value) { // … }
val defaultExtension: String get() = "kt"
ফাঁকা স্থান
উল্লম্ব
একটিমাত্র ফাঁকা লাইন দেখা যায়:
- একটি ক্লাসের পরপর সদস্যদের মধ্যে : যেমন প্রোপার্টি, কনস্ট্রাক্টর, ফাংশন, নেস্টেড ক্লাস, ইত্যাদি।
- ব্যতিক্রম: পরপর দুটি প্রপার্টির মধ্যে (যেখানে অন্য কোনো কোড থাকবে না) একটি ফাঁকা লাইন ঐচ্ছিক। প্রপার্টিগুলোর যৌক্তিক দলবদ্ধকরণ তৈরি করতে এবং প্রপার্টিগুলোকে তাদের সহায়ক প্রপার্টির (যদি থাকে) সাথে যুক্ত করতে প্রয়োজন অনুযায়ী এই ধরনের ফাঁকা লাইন ব্যবহার করা হয়।
- ব্যতিক্রম: এনাম কনস্ট্যান্টের মধ্যে থাকা ফাঁকা লাইনগুলো সম্পর্কে নিচে আলোচনা করা হয়েছে।
- কোডকে যৌক্তিক উপবিভাগে বিন্যস্ত করার জন্য প্রয়োজন অনুসারে স্টেটমেন্টগুলোর মাঝে।
- ঐচ্ছিকভাবে কোনো ফাংশনের প্রথম স্টেটমেন্টের আগে, কোনো ক্লাসের প্রথম মেম্বারের আগে, অথবা কোনো ক্লাসের শেষ মেম্বারের পরে (এটি উৎসাহিতও করা হয় না, নিরুৎসাহিতও করা হয় না)।
- এই নথির অন্যান্য অংশের (যেমন কাঠামো অংশ) প্রয়োজন অনুযায়ী।
পরপর একাধিক ফাঁকা লাইন দেওয়া যেতে পারে, তবে তা উৎসাহিত করা হয় না এবং বাধ্যতামূলকও নয়।
অনুভূমিক
ভাষা বা অন্যান্য শৈলীগত নিয়ম দ্বারা আবশ্যক না হলে, এবং লিটারেল, কমেন্ট ও কে-ডক (KDoc) ব্যতীত, একটি একক ASCII স্পেস শুধুমাত্র নিম্নলিখিত স্থানগুলিতেও ব্যবহৃত হয়:
- একই লাইনে
if,for, বাcatchমতো যেকোনো সংরক্ষিত শব্দকে তার পরে আসা খোলা বন্ধনী (() থেকে আলাদা করা।// WRONG! for(i in 0..1) { }
// Okay for (i in 0..1) { }
- যেকোনো সংরক্ষিত শব্দ, যেমন
elseবাcatch, কে সেই লাইনে তার আগে থাকা একটি বন্ধনী (}) থেকে আলাদা করা।// WRONG! }else { }
// Okay } else { }
- যেকোনো খোলা কোঁকড়া বন্ধনীর (
{) আগে।// WRONG! if (list.isEmpty()){ }
// Okay if (list.isEmpty()) { }
- যেকোনো বাইনারি অপারেটরের উভয় পাশে।
// WRONG! val two = 1+1
এটি নিম্নলিখিত “অপারেটর-সদৃশ” প্রতীকগুলির ক্ষেত্রেও প্রযোজ্য:// Okay val two = 1 + 1
- ল্যাম্বডা এক্সপ্রেশনে তীর চিহ্ন (
->)।// WRONG! ints.map { value->value.toString() }
// Okay ints.map { value -> value.toString() }
- একটি মেম্বার রেফারেন্সের দুটি কোলন (
:::)।// WRONG! val toString = Any :: toString
// Okay val toString = Any::toString
- বিন্দু বিভাজক (
.)।// WRONG it . toString()
// Okay it.toString()
- রেঞ্জ অপারেটর (
..)।// WRONG for (i in 1 .. 4) { print(i) }
// Okay for (i in 1..4) { print(i) }
- ল্যাম্বডা এক্সপ্রেশনে তীর চিহ্ন (
- কোলন (
:এর আগে শুধুমাত্র তখনই ব্যবহৃত হয়, যখন এটি কোনো ক্লাস ডিক্লারেশনে বেস ক্লাস বা ইন্টারফেস নির্দিষ্ট করার জন্য, অথবা জেনেরিক কনস্ট্রেইন্টের জন্যwhereক্লজে ব্যবহৃত হয়।// WRONG! class Foo: Runnable
// Okay class Foo : Runnable
// WRONG fun <T: Comparable> max(a: T, b: T)
// Okay fun <T : Comparable<T>> max(a: T, b: T)
// WRONG fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay fun <T> max(a: T, b: T) where T : Comparable<T> {}
- কমা (
,) বা কোলন (:) এর পরে।// WRONG! val oneAndTwo = listOf(1,2)
// Okay val oneAndTwo = listOf(1, 2)
// WRONG! class Foo :Runnable
// Okay class Foo : Runnable
- লাইন-শেষের কমেন্ট শুরু করার জন্য ব্যবহৃত ডাবল স্ল্যাশ (
//) এর উভয় পাশে। এখানে একাধিক স্পেস দেওয়া যেতে পারে, তবে তা বাধ্যতামূলক নয়।// WRONG! var debugging = false//disabled by default
// Okay var debugging = false // disabled by default
এই নিয়মটি কোনো লাইনের শুরুতে বা শেষে অতিরিক্ত জায়গার প্রয়োজন বা নিষেধ হিসেবে কখনো ব্যাখ্যা করা হয় না; এটি শুধুমাত্র লাইনের ভেতরের ফাঁকা স্থান সংক্রান্ত।
নির্দিষ্ট গঠন
Enum ক্লাস
যেসব এনামের কোনো ফাংশন নেই এবং কনস্ট্যান্টগুলোর কোনো ডকুমেন্টেশন নেই, সেগুলোকে ঐচ্ছিকভাবে একটি একক লাইনে ফরম্যাট করা যেতে পারে।
enum class Answer { YES, NO, MAYBE }
যখন কোনো enum-এর কনস্ট্যান্টগুলো আলাদা লাইনে রাখা হয়, তখন তাদের মধ্যে ফাঁকা লাইনের প্রয়োজন হয় না, তবে যদি সেগুলো কোনো বডি সংজ্ঞায়িত করে, সেক্ষেত্রে ব্যতিক্রম।
enum class Answer { YES, NO, MAYBE { override fun toString() = """¯\_(ツ)_/¯""" } }
যেহেতু enum ক্লাসগুলোও ক্লাস, তাই ক্লাস ফরম্যাট করার অন্যান্য সমস্ত নিয়ম প্রযোজ্য।
টীকা
সদস্য বা প্রকারের টীকাগুলো টীকাকৃত কাঠামোর ঠিক আগে আলাদা লাইনে স্থাপন করা হয়।
@Retention(SOURCE) @Target(FUNCTION, PROPERTY_SETTER, FIELD) annotation class Global
আর্গুমেন্ট ছাড়া টীকা একটি লাইনে রাখা যেতে পারে।
@JvmField @Volatile var disposable: Disposable? = null
যখন আর্গুমেন্ট ছাড়া শুধুমাত্র একটি অ্যানোটেশন থাকে, তখন সেটি ডিক্লারেশনের সাথে একই লাইনে রাখা যেতে পারে।
@Volatile var disposable: Disposable? = null @Test fun selectAll() { // … }
@[...] সিনট্যাক্স শুধুমাত্র একটি সুস্পষ্ট ব্যবহার-সাইট টার্গেটের সাথে এবং শুধুমাত্র একটি লাইনে আর্গুমেন্ট ছাড়া ২ বা ততোধিক অ্যানোটেশন একত্রিত করার জন্য ব্যবহার করা যেতে পারে।
@field:[JvmStatic Volatile] var disposable: Disposable? = null
অন্তর্নিহিত রিটার্ন/প্রপার্টি টাইপ
যদি কোনো এক্সপ্রেশন ফাংশন বডি বা প্রপার্টি ইনিশিয়ালাইজার একটি স্কেলার ভ্যালু হয় অথবা বডি থেকে রিটার্ন টাইপ স্পষ্টভাবে অনুমান করা যায়, তাহলে তা বাদ দেওয়া যেতে পারে।
override fun toString(): String = "Hey" // becomes override fun toString() = "Hey"
private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png") // becomes private val ICON = IconLoader.getIcon("/icons/kotlin.png")
লাইব্রেরি লেখার সময়, যদি তা পাবলিক এপিআই-এর অংশ হয়, তবে সুস্পষ্ট টাইপ ডিক্লারেশনটি বজায় রাখুন।
নামকরণ
আইডেন্টিফায়ারগুলিতে শুধুমাত্র ASCII অক্ষর ও সংখ্যা ব্যবহৃত হয় এবং নিচে উল্লিখিত অল্প কিছু ক্ষেত্রে আন্ডারস্কোর ব্যবহৃত হয়। সুতরাং প্রতিটি বৈধ আইডেন্টিফায়ার নাম \w+ রেগুলার এক্সপ্রেশন দ্বারা মেলানো হয়।
name_ , mName , s_name , এবং kName উদাহরণগুলিতে দেখা বিশেষ উপসর্গ বা প্রত্যয়গুলি, ব্যাকিং প্রপার্টির ক্ষেত্র ব্যতীত ব্যবহৃত হয় না ( ব্যাকিং প্রপার্টি দেখুন)।
প্যাকেজের নাম
প্যাকেজের নামগুলো সবই ছোট হাতের অক্ষরে হয় এবং পরপর শব্দগুলো কেবল একসাথে জুড়ে দেওয়া হয় (কোনো আন্ডারস্কোর থাকে না)।
// Okay package com.example.deepspace // WRONG! package com.example.deepSpace // WRONG! package com.example.deep_space
নাম টাইপ করুন
ক্লাসের নাম প্যাসকেলকেস (PascalCase) অক্ষরে লেখা হয় এবং এগুলো সাধারণত বিশেষ্য বা বিশেষ্য পদগুচ্ছ হয়ে থাকে। উদাহরণস্বরূপ, Character বা ImmutableList । ইন্টারফেসের নামও বিশেষ্য বা বিশেষ্য পদগুচ্ছ হতে পারে (উদাহরণস্বরূপ, List ), তবে কখনও কখনও এর পরিবর্তে বিশেষণ বা বিশেষণ পদগুচ্ছও হতে পারে (উদাহরণস্বরূপ, Readable )।
টেস্ট ক্লাসগুলোর নামকরণ করা হয় যে ক্লাসটিকে টেস্ট করা হচ্ছে তার নাম দিয়ে এবং শেষে Test যুক্ত করা হয়। উদাহরণস্বরূপ, HashTest বা HashIntegrationTest ।
ফাংশনের নাম
ফাংশনের নাম ক্যামেলকেস (camelCase) অক্ষরে লেখা হয় এবং এগুলো সাধারণত ক্রিয়াপদ বা ক্রিয়াপদগুচ্ছ হয়ে থাকে। উদাহরণস্বরূপ, sendMessage বা stop ।
নামের যৌক্তিক উপাদানগুলোকে আলাদা করার জন্য টেস্ট ফাংশনের নামে আন্ডারস্কোর ব্যবহারের অনুমতি আছে।
@Test fun pop_emptyStack() { // … }
@Composable দিয়ে টীকাযুক্ত যে ফাংশনগুলো Unit রিটার্ন করে, সেগুলোকে টাইপের মতোই প্যাসকেল-কেস করা হয় এবং বিশেষ্য (noun) হিসেবে নামকরণ করা হয়।
@Composable fun NameTag(name: String) { // … }
ফাংশনের নামে স্পেস থাকা উচিত নয়, কারণ এটি সব প্ল্যাটফর্মে সমর্থিত নয় (বিশেষত, অ্যান্ড্রয়েডে এটি পুরোপুরি সমর্থিত নয়)।
// WRONG! fun `test every possible case`() {} // OK fun testEveryPossibleCase() {}
ধ্রুবক নাম
ধ্রুবকের নামগুলো আপারকেস (UPPER_SNAKE_CASE) পদ্ধতিতে লেখা হয়: সব অক্ষর বড় হাতের হয় এবং শব্দগুলো আন্ডারস্কোর দিয়ে আলাদা করা থাকে। কিন্তু ধ্রুবক আসলে কী ?
কনস্ট্যান্ট হলো এমন val প্রোপার্টি যার কোনো কাস্টম get ফাংশন নেই, যার ভেতরের বিষয়বস্তু গভীরভাবে অপরিবর্তনীয় এবং যার ফাংশনগুলোর কোনো শনাক্তযোগ্য সাইড-ইফেক্ট নেই। এর মধ্যে অপরিবর্তনীয় টাইপ এবং অপরিবর্তনীয় টাইপের অপরিবর্তনীয় কালেকশন অন্তর্ভুক্ত, সেইসাথে স্কেলার এবং স্ট্রিংও যদি সেগুলোকে const হিসেবে চিহ্নিত করা হয়। যদি কোনো ইনস্ট্যান্সের পর্যবেক্ষণযোগ্য অবস্থার কোনো অংশ পরিবর্তিত হতে পারে, তবে সেটি কনস্ট্যান্ট নয়। শুধুমাত্র অবজেক্টটিকে কখনো পরিবর্তন না করার উদ্দেশ্য থাকাই যথেষ্ট নয়।
const val NUMBER = 5 val NAMES = listOf("Alice", "Bob") val AGES = mapOf("Alice" to 35, "Bob" to 32) val COMMA_JOINED = NAMES.joinToString(", ") val EMPTY_ARRAY = arrayOf<SomeMutableType>()
এই নামগুলো সাধারণত বিশেষ্য বা বিশেষ্য পদগুচ্ছ হয়ে থাকে।
ধ্রুবক মান শুধুমাত্র কোনো object ভিতরে অথবা শীর্ষ-স্তরের ঘোষণা হিসেবেই সংজ্ঞায়িত করা যায়। অন্যথায় ধ্রুবকের শর্ত পূরণ করলেও class ভিতরে সংজ্ঞায়িত মানগুলোর জন্য অবশ্যই একটি অ-ধ্রুবক নাম ব্যবহার করতে হবে।
যেসব ধ্রুবক স্কেলার মান, সেগুলোর ক্ষেত্রে অবশ্যই const মডিফায়ারটি ব্যবহার করতে হবে।
অ-ধ্রুবক নাম
অ-ধ্রুবক নামগুলো ক্যামেলকেস (camelCase) অক্ষরে লেখা হয়। এগুলো ইনস্ট্যান্স প্রোপার্টি, লোকাল প্রোপার্টি এবং প্যারামিটার নামের ক্ষেত্রে প্রযোজ্য।
val variable = "var" val nonConstScalar = "non-const" val mutableCollection: MutableSet<String> = HashSet() val mutableElements = listOf(mutableInstance) val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2) val logger = Logger.getLogger(MyClass::class.java.name) val nonEmptyArray = arrayOf("these", "can", "change")
এই নামগুলো সাধারণত বিশেষ্য বা বিশেষ্য পদগুচ্ছ হয়ে থাকে।
সহায়ক সম্পত্তি
যখন কোনো সহায়ক সম্পত্তির প্রয়োজন হয়, তখন তার নামটি স্থাবর সম্পত্তির নামের সাথে হুবহু মিলতে হবে, তবে নামের শুরুতে একটি আন্ডারস্কোর যুক্ত করতে হবে।
private var _table: Map<String, Int>? = null val table: Map<String, Int> get() { if (_table == null) { _table = HashMap() } return _table ?: throw AssertionError() }
ভেরিয়েবলের নাম টাইপ করুন
প্রতিটি টাইপ ভেরিয়েবলের নামকরণ দুটি শৈলীর যেকোনো একটিতে করা হয়:
- একটি বড় হাতের অক্ষর, যার পরে ঐচ্ছিকভাবে একটি সংখ্যা থাকতে পারে (যেমন
E,T,X,T2)। - ক্লাসের জন্য ব্যবহৃত ফর্ম্যাটে একটি নাম, যার পরে বড় হাতের অক্ষর
Tথাকে (যেমনRequestT,FooBarT)
উটের ক্ষেত্রে
কখনও কখনও একটি ইংরেজি বাক্যাংশকে ক্যামেল কেসে রূপান্তর করার একাধিক যুক্তিসঙ্গত উপায় থাকে, যেমন যখন সংক্ষিপ্ত রূপ বা "IPv6" বা "iOS"-এর মতো অস্বাভাবিক গঠন উপস্থিত থাকে। পূর্বাভাসযোগ্যতা উন্নত করতে, নিম্নলিখিত পদ্ধতিটি ব্যবহার করুন।
নামটির গদ্য রূপ দিয়ে শুরু করা যাক:
- বাক্যটিকে সাধারণ ASCII-তে রূপান্তর করুন এবং যেকোনো অ্যাপস্ট্রফি মুছে ফেলুন। উদাহরণস্বরূপ, “Müller's algorithm” হয়ে যেতে পারে “Muellers algorithm”।
- এই ফলাফলটিকে শব্দে বিভক্ত করুন, স্পেস এবং অবশিষ্ট বিরামচিহ্ন (সাধারণত হাইফেন) অনুসারে। সুপারিশ: যদি কোনো শব্দের সাধারণ ব্যবহারে আগে থেকেই প্রচলিত ক্যামেল-কেস চেহারা থাকে, তবে সেটিকে তার গঠনকারী অংশগুলিতে বিভক্ত করুন (যেমন, “AdWords” হয়ে যাবে “ad words”)। উল্লেখ্য যে, “iOS”-এর মতো একটি শব্দ ঠিক ক্যামেল-কেস নয়; এটি কোনো প্রচলিত নিয়ম মানে না, তাই এই সুপারিশটি প্রযোজ্য নয়।
- এখন সবকিছু (সংক্ষিপ্ত রূপ সহ) ছোট হাতের অক্ষরে লিখুন, তারপর নিচের যেকোনো একটি করুন:
- প্যাসকেল কেস পেতে প্রতিটি শব্দের প্রথম অক্ষরকে বড় হাতের অক্ষরে রূপান্তর করুন।
- ক্যামেল কেস পেতে, প্রথম শব্দটি ছাড়া বাকি সব শব্দের প্রথম অক্ষরটিকে বড় হাতের অক্ষরে রূপান্তর করুন।
- অবশেষে, সমস্ত শব্দকে একত্রিত করে একটি একক শনাক্তকারী তৈরি করুন।
উল্লেখ্য যে, মূল শব্দগুলোর বড় হাতের অক্ষরের বিন্যাস প্রায় সম্পূর্ণভাবে উপেক্ষা করা হয়।
| গদ্য রূপ | সঠিক | ভুল |
|---|---|---|
| "এক্সএমএল এইচটিটিপি অনুরোধ" | XmlHttpRequest | XMLHTTPRequest |
| "নতুন গ্রাহক আইডি" | newCustomerId | newCustomerID |
| "অভ্যন্তরীণ স্টপওয়াচ" | innerStopwatch | innerStopWatch |
| "iOS-এ IPv6 সমর্থন করে" | supportsIpv6OnIos | supportsIPv6OnIOS |
| "ইউটিউব ইম্পোর্টার" | YouTubeImporter | YoutubeImporter * |
(* গ্রহণযোগ্য, কিন্তু সুপারিশযোগ্য নয়।)
ডকুমেন্টেশন
ফরম্যাটিং
এই উদাহরণে KDoc ব্লকের মৌলিক বিন্যাস দেখা যায়:
/** * Multiple lines of KDoc text are written here, * wrapped normally… */ fun method(arg: String) { // … }
...অথবা এই এক-লাইনের উদাহরণে:
/** An especially short bit of KDoc. */
মৌলিক রূপটি সর্বদা গ্রহণযোগ্য। যখন সম্পূর্ণ KDoc ব্লকটি (মন্তব্য চিহ্ন সহ) একটি লাইনে আঁটে, তখন এক-লাইনের রূপটি ব্যবহার করা যেতে পারে। মনে রাখবেন যে এটি কেবল তখনই প্রযোজ্য যখন @return মতো কোনো ব্লক ট্যাগ থাকে না।
অনুচ্ছেদ
অনুচ্ছেদগুলোর মাঝে এবং ব্লক ট্যাগগুলোর সমষ্টি (যদি থাকে) তার আগে একটি ফাঁকা লাইন বসে—অর্থাৎ, এমন একটি লাইন যেখানে শুধু অ্যালাইন করা শুরুর অ্যাস্টারিস্ক ( * ) চিহ্নটি থাকে।
ব্লক ট্যাগ
ব্যবহৃত যেকোনো স্ট্যান্ডার্ড “ব্লক ট্যাগ” @constructor , @receiver , @param , @property , @return , @throws , @see এই ক্রমে বসে এবং এগুলোর বিবরণ কখনো খালি থাকে না। যখন কোনো ব্লক ট্যাগ একটি লাইনে ধরে না, তখন পরবর্তী লাইনগুলো @ -এর অবস্থান থেকে ৪ স্পেস ইনডেন্ট করা হয়।
সারসংক্ষেপ খণ্ড
প্রতিটি KDoc ব্লক একটি সংক্ষিপ্ত সারাংশ খণ্ড দিয়ে শুরু হয়। এই খণ্ডটি খুবই গুরুত্বপূর্ণ: এটিই পাঠ্যের একমাত্র অংশ যা ক্লাস এবং মেথড সূচকের মতো নির্দিষ্ট প্রসঙ্গে প্রদর্শিত হয়।
এটি একটি খণ্ডবাক্য—একটি বিশেষ্য পদগুচ্ছ বা ক্রিয়া পদগুচ্ছ, কোনো সম্পূর্ণ বাক্য নয়। এটি " A `Foo` is a... " বা " This method returns... " দিয়ে শুরু হয় না, কিংবা " Save the record. "-এর মতো কোনো সম্পূর্ণ অনুজ্ঞাসূচক বাক্য গঠন করতেও বাধ্য নয়। কিন্তু, খণ্ডবাক্যটিকে একটি সম্পূর্ণ বাক্যের মতোই বড় হাতের অক্ষরে লেখা হয় এবং এতে যতিচিহ্ন ব্যবহার করা হয়।
ব্যবহার
নীচে উল্লিখিত কয়েকটি ব্যতিক্রম ছাড়া, ন্যূনতমভাবে প্রতিটি public টাইপ এবং সেই টাইপের প্রতিটি public বা protected মেম্বারের জন্য KDoc উপস্থিত থাকে।
ব্যতিক্রম: স্ব-ব্যাখ্যামূলক ফাংশন
getFoo মতো “সরল ও সুস্পষ্ট” ফাংশন এবং foo মতো প্রোপার্টিগুলোর ক্ষেত্রে KDoc ঐচ্ছিক, যেখানে “Returns the foo” ছাড়া আর বলার মতো সত্যিই অন্য কিছু থাকে না।
একজন সাধারণ পাঠকের জানার প্রয়োজন হতে পারে এমন প্রাসঙ্গিক তথ্য বাদ দেওয়ার ন্যায্যতা প্রমাণ করতে এই ব্যতিক্রমটির উদ্ধৃতি দেওয়া সমীচীন নয়। উদাহরণস্বরূপ, getCanonicalName নামের কোনো ফাংশন বা canonicalName নামের কোনো প্রপার্টির ক্ষেত্রে, এর ডকুমেন্টেশন বাদ দেবেন না (এই যুক্তিতে যে সেখানে শুধু লেখা থাকবে /** Returns the canonical name. */ ), যদি একজন সাধারণ পাঠকের "ক্যানোনিকাল নেম" শব্দটির অর্থ সম্পর্কে কোনো ধারণাই না থাকে!
ব্যতিক্রম: ওভাররাইড
যে মেথড কোনো সুপারটাইপ মেথডকে ওভাররাইড করে, সেখানে KDoc সবসময় উপস্থিত থাকে না।