Kotlin স্টাইল গাইড

এই ডকুমেন্ট Kotlin প্রোগ্রামিং ল্যাঙ্গুয়েজে সোর্স কোডের জন্য Google-এর Android কোডিং স্ট্যান্ডার্ডের সম্পূর্ণ সংজ্ঞা হিসেবে কাজ করে। কোনও Kotlin সোর্স ফাইলকে Google Android স্টাইলে লেখা হয়েছে বলে তখনই বর্ণনা করা যাবে যদি সেটি এখানে উল্লেখ করা নিয়ম মেনে চলে।

অন্যান্য প্রোগ্রামিং স্টাইল গাইডের মতো, এখানে যেসব সমস্যা নিয়ে আলোচনা করা হয়েছে, সেগুলি শুধু ফর্ম্যাটিংয়ের নান্দনিক সমস্যা নয়, তার সাথে অন্যান্য ধরনের প্রথা বা কোডিং স্ট্যান্ডার্ডও রয়েছে। তবে, এই ডকুমেন্টে মূলত সেইসব কঠোর নিয়মকানুন সম্পর্কে বলা হয়েছে যা আমরা বিশ্বজুড়ে মেনে চলি এবং এমন পরামর্শ দেওয়া এড়িয়ে যাওয়া হয়েছে যা স্পষ্টভাবে প্রয়োগ করা যায় না (মানুষ বা টুল, কারও দ্বারাই)।

উৎস ফাইলগুলি

সব সোর্স ফাইলকে 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 target সহ অ্যানোটেশন যেকোনও হেডার কমেন্ট ও প্যাকেজ ঘোষণার মধ্যে প্লেস করা হয়।

প্যাকেজ স্টেটমেন্ট

প্যাকেজ স্টেটমেন্টে কোনও কলাম সীমা থাকে না এবং এটি কখনওই লাইন-র‍্যাপ করা হয় না।

স্টেটমেন্ট ইমপোর্ট করা

ক্লাস, ফাংশন ও প্রপার্টির জন্য ইমপোর্ট স্টেটমেন্ট একটি তালিকায় গ্রুপ করা হয় এবং 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 ব্রাঞ্চ, do, এবং while স্টেটমেন্ট ও এক্সপ্রেশনের জন্য ব্রেসেস প্রয়োজন, এমনকি বডি খালি থাকলেও অথবা শুধুমাত্র একটি স্টেটমেন্ট থাকলেও।

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)
}

নন-এমটি ব্লক

খালি নয় এমন ব্লক ও ব্লকের মতো কনস্ট্রাক্টের জন্য ব্রেস Kernighan and Ritchie স্টাইল ("Egyptian brackets") মেনে চলে:

  • ওপেনিং ব্রেসের আগে কোনও লাইন ব্রেক নেই।
  • ওপেনিং ব্রেসের পরে লাইন ব্রেক।
  • শেষ বন্ধনীর আগে লাইন ব্রেক।
  • বন্ধনী শেষ হওয়ার পরে লাইন ব্রেক, শুধুমাত্র তখনই যখন সেই বন্ধনী কোনও স্টেটমেন্ট বা ফাংশন, কনস্ট্রাক্টর বা নামযুক্ত ক্লাসের বডি শেষ করে। যেমন, ব্রেসের পরে 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()
        }
    }
}

এনাম ক্লাসের জন্য কয়েকটি ব্যতিক্রম নিচে দেওয়া হল।

খালি ব্লক

খালি ব্লক বা ব্লক-এর মতো কনস্ট্রাক্ট অবশ্যই 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 স্টেটমেন্ট
  • কমেন্টে কমান্ড লাইন যা শেল-এ কাট-অ্যান্ড-পেস্ট করা যেতে পারে

কোথায় বিরতি নেবেন

লাইন-র‍্যাপিংয়ের মূল নির্দেশ হল: উচ্চতর সিনট্যাক্টিক লেভেলে ব্রেক করার চেষ্টা করুন। এছাড়াও:

  • কোনও অপারেটর বা ইনফিক্স ফাংশনের নামে লাইন ব্রেক হলে, ব্রেকটি অপারেটর বা ইনফিক্স ফাংশনের নামের পরে দেওয়া হয়।
  • নিম্নলিখিত "অপারেটর-লাইক" চিহ্নগুলিতে লাইন ভেঙে গেলে, চিহ্নটির আগে লাইন ব্রেক হয়:
    • ডট সেপারেটর (., ?.)।
    • মেম্বার রেফারেন্সের দুটি কোলন (::)।
  • কোনও মেথড বা কনস্ট্রাক্টর নাম, সেটির পরে থাকা খোলা প্যারেন্থেসিসের (() সাথে যুক্ত থাকে।
  • কমা (,) টোকেনের সাথে যুক্ত থাকে।
  • একটি ল্যাম্বডা তীরচিহ্ন (->) এর আগে থাকা আর্গুমেন্ট তালিকার সাথে যুক্ত থাকে।

অপেক্ষক

ফাংশন সিগনেচার একটি লাইনে না ধরলে, প্রতিটি প্যারামিটার ঘোষণা আলাদা আলাদা লাইনে লিখুন। এই ফর্ম্যাটে সংজ্ঞায়িত প্যারামিটারগুলিতে একটি ইন্ডেন্ট (+৪) ব্যবহার করা উচিত। শেষের প্যারেন্থেসিস ()) ও রিটার্ন টাইপ আলাদা আলাদা লাইনে কোনও অতিরিক্ত ইন্ডেন্ট ছাড়াই লেখা হয়।

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 ফাংশন ঘোষণা করা প্রপার্টিগুলিকে স্বাভাবিক ইনডেন্ট (+৪) সহ তাদের নিজস্ব লাইনে রাখতে হবে। ফাংশনের মতো একই নিয়ম ব্যবহার করে সেগুলি ফর্ম্যাট করুন।

var directory: File? = null
    set(value) {
        // …
    }
রিড-অনলি প্রপার্টি একটি লাইনে ফিট করে এমন সংক্ষিপ্ত সিনট্যাক্স ব্যবহার করতে পারে।

val defaultExtension: String get() = "kt"

Whitespace

উল্লম্ব

একটি ফাঁকা লাইন দেখা যাবে:

  • কোনও ক্লাসের দুটি পরপর মেম্বারের মধ্যে: প্রপার্টি, কনস্ট্রাক্টর, ফাংশন, নেস্টেড ক্লাস ইত্যাদি।
    • ব্যতিক্রম: দুটি পরপর প্রপার্টির মধ্যে খালি লাইন (সেগুলির মধ্যে অন্য কোনও কোড না থাকলে) ঐচ্ছিক। প্রপার্টির লজিক্যাল গ্রুপিং তৈরি করতে এবং সেগুলির ব্যাক-আপ প্রপার্টির সাথে প্রপার্টি অ্যাসোসিয়েট করতে প্রয়োজন অনুযায়ী এই ধরনের ফাঁকা লাইন ব্যবহার করা হয়, যদি থাকে।
    • ব্যতিক্রম: এনুম ধ্রুবকের মধ্যে খালি লাইনগুলি নিচে আলোচনা করা হয়েছে।
  • স্টেটমেন্টের মধ্যে, কোডকে যৌক্তিক সাবসেকশনে ভাগ করার জন্য প্রয়োজন অনুযায়ী।
  • ফাংশনের প্রথম স্টেটমেন্টের বিকল্প হিসেবে আগে, ক্লাসের প্রথম মেম্বারের আগে অথবা ক্লাসের শেষ মেম্বারের পরে (এটি করার জন্য উৎসাহ বা নিরুৎসাহ কোনওটিই দেওয়া হয় না)।
  • এই ডকুমেন্টের অন্যান্য বিভাগে (যেমন, স্ট্রাকচার বিভাগ) যেভাবে বলা হয়েছে।

পরপর একাধিক ফাঁকা লাইন ব্যবহার করা যায়, তবে এটি উৎসাহজনক নয় বা কখনও প্রয়োজন হয় না।

অনুভূমিক

ভাষা বা অন্যান্য স্টাইল সংক্রান্ত নিয়মের প্রয়োজন ছাড়া, এবং লিটেরাল, কমেন্ট ও 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 class Answer {
    YES,
    NO,

    MAYBE {
        override fun toString() = """¯\_(ツ)_/¯"""
    }
}

যেহেতু এনুম ক্লাস হল ক্লাস, তাই ক্লাস ফর্ম্যাটিংয়ের জন্য অন্যান্য সব নিয়ম প্রযোজ্য হয়।

টিকা

অ্যানোটেটেড কনস্ট্রাক্টের ঠিক আগে আলাদা আলাদা লাইনে মেম্বার বা টাইপ অ্যানোটেশন প্লেস করা হয়।

@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")

লাইব্রেরি লেখার সময়, নিম্নলিখিত ক্ষেত্রে স্পষ্ট ধরনের ঘোষণা বজায় রাখুন: এটি পাবলিক API-এর অংশ হলে।

নামকরণ

আইডেন্টিফায়ারে শুধুমাত্র 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 রিটার্ন করে সেগুলি PascalCase-এ লেখা হয় এবং বিশেষ্য হিসেবে নামকরণ করা হয়, যেন সেগুলি কোনও টাইপ।

@Composable
fun NameTag(name: String) {
    // …
}

ফাংশনের নামে স্পেস থাকলে চলবে না, কারণ এটি সব প্ল্যাটফর্মে কাজ করে না (বিশেষত, Android-এ এটি সম্পূর্ণভাবে কাজ করে না)।

// WRONG!
fun `test every possible case`() {}
// OK
fun testEveryPossibleCase() {}

ধ্রুবকের নাম

ধ্রুবকের নামে UPPER_SNAKE_CASE ব্যবহার করা হয়: সব বড় হাতের অক্ষর, আন্ডারস্কোর দিয়ে আলাদা করা শব্দ। কিন্তু ধ্রুবক ঠিক কী?

কনস্ট্যান্ট হল কাস্টম get ফাংশন ছাড়া val প্রপার্টি, যার কন্টেন্ট সম্পূর্ণ অপরিবর্তনীয় এবং যার ফাংশনে কোনও শনাক্তযোগ্য পার্শ্বপ্রতিক্রিয়া নেই। এতে পরিবর্তন করা যায় না এমন ধরন ও পরিবর্তন করা যায় না এমন ধরনের পরিবর্তন করা যায় না এমন কালেকশন অন্তর্ভুক্ত থাকে, সেইসাথে 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-এর মধ্যে অথবা টপ-লেভেল ডিক্লারেশন হিসেবেই ডিফাইন করা যায়। অন্যথায়, a constant-এর প্রয়োজনীয়তা পূরণ করে এমন ভ্যালু, কিন্তু 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"-এর মতো সংক্ষিপ্ত রূপ বা অস্বাভাবিক গঠন উপস্থিত থাকে। পূর্বাভাসযোগ্যতা উন্নত করতে, নিম্নলিখিত স্কিম ব্যবহার করুন।

নামের গদ্য রূপ দিয়ে শুরু করা:

  1. বাক্যাংশটিকে সাধারণ ASCII-তে কনভার্ট করুন এবং কোনও অ্যাপোস্ট্রফি থাকলে তা সরিয়ে দিন। যেমন, “Müller’s algorithm” পরিবর্তিত হয়ে “Muellers algorithm” হয়ে যেতে পারে।
  2. এই ফলাফলকে শব্দে ভাগ করুন, স্পেস ও বাকি থাকা যতিচিহ্ন (সাধারণত হাইফেন) দিয়ে ভাগ করুন। সাজেস্ট করা: কোনও শব্দ যদি আগে থেকেই সাধারণ ব্যবহারে কনভেনশনাল ক্যামেল-কেস হিসেবে দেখা যায়, তাহলে এটিকে এর উপাদান অংশে ভাগ করুন (যেমন, “AdWords” হয়ে যায় “ad words”)। মনে রাখবেন যে “iOS”-এর মতো শব্দ আসলে ক্যামেল-কেস নয়; এটি কোনও কনভেনশন মেনে চলে না, তাই এই সাজেশন প্রযোজ্য নয়।
  3. এখন সব অক্ষর ছোট হাতের করুন (সংক্ষিপ্ত রূপ সহ), তারপর নিম্নলিখিতগুলির মধ্যে একটি করুন:
    • প্যাসকেল কেস পেতে প্রতিটি শব্দের প্রথম অক্ষর বড় হাতের অক্ষরে লিখুন।
    • ক্যামেল কেস পেতে, প্রথম অক্ষরটি বাদ দিয়ে প্রতিটি শব্দের প্রথম অক্ষর বড় হাতের অক্ষরে লিখুন।
  4. সবশেষে, সব শব্দগুলিকে একটি আইডেন্টিফায়ারে যোগ করুন।

মনে রাখবেন, মূল শব্দের কেসিং প্রায় সম্পূর্ণভাবে উপেক্ষা করা হয়।

গদ্য সঠিক ভুল
"XML Http Request" XmlHttpRequest XMLHTTPRequest
"নতুন গ্রাহক আইডি" newCustomerId newCustomerID
"অভ্যন্তরীণ স্টপওয়াচ" innerStopwatch innerStopWatch
"iOS-এ IPv6 কাজ করে" supportsIpv6OnIos supportsIPv6OnIOS
"YouTube ইম্পোর্টার" 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 ধরনের জন্য KDoc থাকে, এবং এই ধরনের প্রতিটি public বা protected মেম্বারের জন্য থাকে, নিচে উল্লেখ করা কয়েকটি ব্যতিক্রম সহ।

ব্যতিক্রম: স্ব-ব্যাখ্যামূলক ফাংশন

“সহজ, স্পষ্ট” ফাংশনের জন্য KDoc ঐচ্ছিক, যেমন getFoo এবং প্রপার্টি যেমন foo, সেইসব ক্ষেত্রে যেখানে সত্যিই এবং সত্যই বলার মতো আর কিছুই নেই, শুধু “foo রিটার্ন করে” ছাড়া।

সাধারণ পাঠক জানতে চাইতে পারেন এমন প্রাসঙ্গিক তথ্য বাদ দেওয়ার ব্যাপারে এই ব্যতিক্রমের উল্লেখ করা উচিত নয়। যেমন, getCanonicalName নামের কোনও ফাংশন বা canonicalName নামের কোনও প্রপার্টির ক্ষেত্রে, এর ডকুমেন্টেশন বাদ দেবেন না (এই যুক্তি দিয়ে যে এটিতে শুধুমাত্র /** Returns the canonical name. */ বলা হবে) যদি কোনও সাধারণ পাঠকের "ক্যাননিকাল নাম" শব্দটির অর্থ সম্পর্কে কোনও ধারণা না থাকে!

ব্যতিক্রম: ওভাররাইড

সুপারটাইপ মেথডকে ওভাররাইড করা মেথডে KDoc সবসময় থাকে না।