Halo, Habr! Saya membawa perhatian Anda pada terjemahan penulis dari halaman dokumentasi Konvensi Coding Kotlin dari JetBrains.
→ Dokumentasi asli
Konten:
Menggunakan panduan Gaya di Intellij Idea
Untuk menerapkan pemformatan di Intellij Idea sesuai dengan manual saat ini, Anda perlu menginstal plugin Kotlin versi 1.2.20 atau lebih baru, buka Pengaturan | Editor | Gaya Kode | Kotlin, klik tautan "Set dari ..." di sudut kanan atas dan pilih "Gaya standar" / Panduan gaya Kotlin "dari menu tarik-turun.
Untuk memverifikasi bahwa kode Anda diformat sesuai dengan gaya yang disarankan, buka pengaturan inspeksi dan aktifkan centang "Kotlin | Gaya masalah | File tidak diformat sesuai dengan pengaturan proyek". Aturan validasi lainnya, seperti konvensi penamaan, diaktifkan secara default.
Struktur proyek
Struktur folder
Dalam proyek menggunakan bahasa yang berbeda, file dengan kode Kotlin harus terletak di folder yang sama dengan kode dalam bahasa lain dan menggunakan struktur file yang sama yang diterima untuk bahasa utama. Misalnya, untuk Java, file harus terletak di struktur folder sesuai dengan nama paket.
Dalam proyek yang hanya menggunakan Kotlin, struktur folder yang disarankan adalah: gunakan folder untuk mengatur paket dengan direktori root dilewati, mis. jika semua kode dalam proyek berada dalam paket "org.example.kotlin" dan paket-paketnya, maka file sumber yang termasuk dalam paket "org.example.kotlin" harus berada di direktori root proyek, dan file dengan kode sumber paket "org. example.kotlin.foo.bar "harus terletak di subdirektori" foo / bar "relatif terhadap root proyek.
Nama file sumber
Jika file Kotlin hanya berisi satu kelas (mungkin terkait dengan deklarasi tingkat atas), maka itu harus dinamai, serta kelas dengan ekstensi .kt
. Jika file berisi beberapa kelas atau hanya memiliki deklarasi tingkat atas, pilih nama yang menjelaskan isi file tersebut dan beri nama file yang sesuai. Gunakan punuk unta dengan huruf besar pertama untuk memberi nama file (mis. ProcessDeclarations.kt
).
Nama file harus menjelaskan apa yang dilakukan kode dalam file tersebut. Artinya, Anda harus menghindari kata-kata yang tidak berarti seperti "Util" untuk menamai file.
Mengatur file sumber
Menempatkan beberapa deklarasi (kelas, fungsi atau properti tingkat atas) dalam file sumber Kotlin yang sama diterima jika deklarasi ini terkait erat satu sama lain secara semantik dan ukuran file tetap masuk akal (tidak lebih dari beberapa ratus baris).
Secara khusus, ketika mendefinisikan fungsi ekstensi untuk kelas yang berlaku untuk semua aspek penerapan kelas ini, letakkan mereka di file yang sama di mana kelas itu sendiri didefinisikan. Ketika mendefinisikan fungsi ekstensi yang bermakna hanya untuk konteks spesifik menggunakan kelas ini, tempatkan di sebelah kode yang menggunakan fungsi ekstensi. Jangan membuat file hanya untuk menyimpan "semua ekstensi Foo."
Struktur kelas
Biasanya, konten kelas diurutkan dalam urutan berikut:
- Deklarasi Properti dan Blok Inisialisasi
- Konstruktor Sekunder
- Deklarasi Metode
- Objek Pendamping
Jangan mengurutkan deklarasi metode secara alfabet atau visual, dan jangan pisahkan metode biasa dari metode ekstensi. Sebagai gantinya, kumpulkan kode yang terhubung secara logis sehingga seseorang yang membaca kelas dari atas ke bawah dapat mengikuti logika apa yang terjadi. Pilih satu urutan pengurutan (kode tingkat lebih tinggi terlebih dahulu [barang tingkat tinggi dulu] dan detailnya nanti atau sebaliknya) dan patuhi itu.
Tempatkan kelas bersarang di sebelah kode yang menggunakan kelas ini. Jika kelas dimaksudkan untuk penggunaan eksternal dan tidak dirujuk dalam kelas, letakkan di akhir setelah objek pendamping.
Kerangka Implementasi Antarmuka
Saat mengimplementasikan antarmuka, pertahankan struktur yang sama dengan antarmuka yang sedang dilaksanakan (jika perlu, gantilah dengan metode pribadi tambahan yang digunakan untuk implementasi)
Struktur yang Digantikan
Redefinisi selalu disatukan, satu demi satu.
Aturan Penamaan
Kotlin mengikuti konvensi penamaan yang sama seperti Jawa. Khususnya:
Nama paket huruf kecil dan jangan gunakan garis bawah (org.example.myproject). Menggunakan nama banyak kata pada umumnya tidak disarankan, tetapi jika Anda perlu menggunakan beberapa kata, Anda bisa menggabungkannya atau menggunakan punuk unta (org.examle.myProject).
Nama kapital dan objek dimulai dengan huruf kapital dan menggunakan punuk unta:
open class DeclarationProcessor { ... } object EmptyDeclarationProcessor : DeclarationProcessor() { ... }
Nama Fungsi
Nama fungsi, properti, dan variabel lokal dimulai dengan huruf kecil dan tidak mengandung garis bawah:
fun processDeclarations() { ... } var declarationCount = ...
Pengecualian: fungsi pabrik yang digunakan untuk membuat instance kelas mungkin memiliki nama yang sama dengan kelas yang dibuat:
abstract class Foo { ... } class FooImpl : Foo { ... } fun Foo(): Foo { return FooImpl(...) }
Nama metode pengujian
Dalam pengujian (dan hanya dalam pengujian) diizinkan untuk menggunakan nama metode dengan spasi yang terlampir dalam koma terbalik. (Perhatikan bahwa nama metode seperti itu saat ini tidak didukung oleh runtime Android.) Garis bawah dalam nama metode juga diperbolehkan dalam kode pengujian.
class MyTestCase { @Test fun `ensure everything works`() { ... } @Test fun ensureEverythingWorks_onAndroid() { ... } }
Penamaan Properti
Nama konstan (properti berlabel const
, atau properti tingkat atas atau objek val
tanpa fungsi get
kustom yang berisi data tidak dapat diubah) harus dikapitalisasi, dipisahkan oleh garis bawah:
const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName"
Nama tingkat atas atau properti objek yang berisi objek dengan perilaku atau data yang bisa berubah harus menggunakan nama umum dalam punuk unta:
val mutableCollection: MutableSet<String> = HashSet()
Nama properti yang mereferensikan objek Singleton dapat menggunakan gaya penamaan yang sama dengan deklarasi kelas:
val PersonComparator: Comparator<Person> = ...
Untuk penghitungan, Anda dapat menggunakan nama yang ditulis dalam huruf kapital yang dipisahkan oleh garis bawah atau gaya punuk unta, dimulai dengan huruf kapital, tergantung penggunaan.
enum class Color { RED, GREEN }
enum class Color { RedColor, GreenColor }
Catatan Penerjemah: Hanya saja jangan mencampur gaya yang berbeda. Pilih satu gaya dan pertahankan sesuai desain Anda.
Menamai Properti Tersembunyi
Jika kelas memiliki dua properti yang secara konseptual sama, tetapi satu adalah bagian dari API publik dan yang lainnya adalah bagian dari implementasi, gunakan garis bawah sebagai awalan untuk nama properti tersembunyi:
class C { private val _elementList = mutableListOf<Element>() val elementList: List<Element> get() = _elementList }
Memilih Nama yang Tepat
Nama kelas biasanya kata benda atau frasa yang menjelaskan apa kelasnya:
List, PersonReader
Nama metode biasanya merupakan kata kerja atau tindakan frase yang menjelaskan apa yang dilakukan metode:
close, readPersons
Nama juga harus menunjukkan apakah metode mengubah objek atau mengembalikan yang baru. Misalnya, sort
adalah sort yang mengubah koleksi, dan sorted
adalah kembalinya salinan koleksi yang baru disortir.
Nama harus dengan jelas menunjukkan tujuan entitas, jadi lebih baik untuk menghindari penggunaan kata-kata yang tidak berarti ( Manager
, Wrapper
, dll.) Dalam nama.
Saat menggunakan akronim sebagai bagian dari nama iklan, gunakan huruf kapital jika terdiri dari dua huruf ( IOStream
); atau hanya huruf besar, jika lebih panjang ( XmlFormatter
, HttpInputStream
).
Dalam kebanyakan kasus, Kotlin mengikuti konvensi pemformatan Java.
Gunakan 4 spasi untuk indentasi. Jangan gunakan tab.
Untuk kawat gigi, letakkan penahan pembuka di ujung garis di mana struktur dimulai dan penopang penutup pada garis yang terpisah, sejajar secara horizontal dengan struktur bukaan.
if (elements != null) { for (element in elements) {
(Catatan: di Kotlin, titik koma adalah opsional, jadi pembungkus garis itu penting. Desain bahasa melibatkan kurung kurawal dalam gaya Java, dan Anda mungkin menemukan perilaku eksekusi kode yang tidak terduga jika Anda mencoba menggunakan gaya pemformatan yang berbeda.)
Ruang horisontal
Tempatkan spasi di sekitar operator biner (a + b)
. Pengecualian: jangan letakkan spasi di sekitar operator "range to" (0..i)
Jangan letakkan spasi di sekitar operator unary (a++)
Tempatkan spasi di antara kata-kata kontrol kunci ( if
, when
, for
dan while
) dan tanda kurung pembuka yang sesuai.
Jangan meletakkan spasi di depan braket pembuka di deklarasi utama konstruktor, metode, atau metode.
class A(val x: Int) fun foo(x: Int) { ... } fun bar() { foo(1) }
Jangan pernah beri spasi setelah (
, [
atau sebelum ]
, )
.
Jangan pernah menempatkan ruang di sekitar titik .
atau operator ?.
:
foo.bar().filter { it > 2 }.joinToString() foo?.()
Beri spasi setelah slash ganda untuk komentar //
:
Jangan letakkan spasi di sekitar kurung sudut yang digunakan untuk menunjukkan parameter tipe:
Class Map<K, V> { ... }
Jangan letakkan spasi di sekitar titik dua untuk menunjukkan referensi ke metode ::
class:
Foo::class String::length
Jangan menaruh spasi sebelumnya ?
digunakan untuk menandai null
:
String?
Secara umum, hindari segala jenis perataan horisontal. Mengganti nama pengidentifikasi dengan nama dengan panjang yang berbeda tidak akan memengaruhi pemformatan kode.
Usus besar
Beri spasi sebelum titik dua :
dalam kasus berikut:
- ketika digunakan untuk memisahkan tipe dari tipe super;
abstract class Foo<out T : Any>
- ketika mendelegasikan ke konstruktor superclass atau konstruktor lain dari kelas yang sama;
constructor(x: String) : super(x) { ... } constructor(x: String) : this(x) { ... }
- setelah objek kata kunci.
val x = object : IFoo { ... }
Jangan beri spasi sebelumnya :
saat memisahkan iklan dan tipenya.
abstract fun foo(a: Int): T
Selalu beri spasi setelah :
abstract class Foo<out T : Any> : IFoo { abstract fun foo(a: Int): T } class FooImpl : Foo() { constructor(x: String) : this(x) { ... } val x = object : IFoo { ... } }
Kelas dengan beberapa parameter konstruktor dasar dan nama pendek dapat ditulis pada satu baris:
class Person(id: Int, name: String)
Kelas dengan nama yang lebih panjang atau jumlah parameter harus diformat sehingga setiap parameter utama konstruktor berada pada baris terpisah dengan lekukan. Selain itu, braket penutup harus berada di jalur baru. Jika kita menggunakan warisan, maka panggilan ke konstruktor dari superclass atau daftar antarmuka yang diimplementasikan harus terletak pada baris yang sama dengan braket:
class Person( id: Int, name: String, surname: String ) : Human(id, name) { ... }
Saat menentukan antarmuka dan memanggil konstruktor superclass, konstruktor superclass pertama-tama harus ditemukan, kemudian nama antarmuka pada baris baru dibenarkan-kiri:
class Person( id: Int, name: String, surname: String ) : Human(id, name), KotlinMaker { ... }
Untuk kelas dengan daftar panjang tipe super, Anda perlu memberi garis istirahat setelah titik dua dan menyelaraskan semua nama tipe super secara horizontal ke kiri:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
Untuk memisahkan dengan jelas tajuk kelas dan tubuhnya ketika tajuk kelas panjang, baik tempatkan baris kosong setelah tajuk kelas (seperti dalam contoh di atas), atau tempatkan brace pembuka pada garis yang terpisah:
class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } }
Gunakan indentasi biasa (4 spasi) untuk parameter konstruktor.
Dasar Pemikiran: ini memastikan bahwa properti yang dideklarasikan di konstruktor utama memiliki indentasi yang sama dengan properti yang dideklarasikan di tubuh kelas.
Pengubah
Jika iklan berisi beberapa pengubah, selalu atur dalam urutan berikut:
public / protected / private / internal expect / actual final / open / abstract / sealed / const external override lateinit tailrec vararg suspend inner enum / annotation companion inline infix operator data
Tempatkan semua anotasi sebelum pengubah:
@Named("Foo") private val foo: Foo
Jika Anda tidak bekerja di perpustakaan, abaikan pengubah yang berlebihan (mis. Publik).
Anotasi biasanya ditempatkan pada baris terpisah sebelum deklarasi yang dilampirkan, dan dengan indentasi yang sama:
@Target(AnnotationTarget.PROPERTY) annotation class JsonExclude
Anotasi tanpa argumen dapat ditemukan di satu baris:
@JsonExclude @JvmField var x: String
Satu anotasi tanpa argumen dapat ditempatkan pada baris yang sama dengan deklarasi yang sesuai:
@Test fun foo() { ... }
Anotasi file
Anotasi ke file ditempatkan setelah komentar pada file (jika ada), sebelum pernyataan paket dan dipisahkan dari paket oleh baris kosong (untuk menekankan fakta bahwa mereka ditujukan pada file, bukan paket).
@file:JvmName("FooBar") package foo.bar
Jika metode tanda tangan tidak cocok pada satu baris, gunakan sintaks berikut:
fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType ): ReturnType {
Gunakan indentasi biasa (4 spasi) untuk parameter fungsi.
Dasar Pemikiran: konsistensi dengan parameter konstruktor
Lebih disukai menggunakan ekspresi tanpa kurung kurawal untuk fungsi yang terdiri dari satu baris.
fun foo(): Int {
Jika isi fungsi baris tunggal tidak cocok pada baris yang sama dengan deklarasi, tuliskan tanda = di baris pertama. Lekuk tubuh ekspresi dengan 4 spasi.
fun f(x: String) = x.length
Untuk properti hanya baca yang sederhana, lebih baik menggunakan pemformatan baris tunggal:
val isEmpty: Boolean get() = size == 0
Untuk properti yang lebih kompleks, selalu gunakan get
dan set
pada baris yang berbeda:
val foo: String get() { ... }
Untuk properti dengan inisialisasi, jika initializer terlalu panjang, tambahkan jeda baris setelah tanda sama dengan dan indentasi empat spasi untuk string inisialisasi:
private val defaultCharset: Charset? = EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
Jika kondisi dalam pernyataan kontrol if
atau when
multi-line, selalu gunakan kurung kurawal di sekitar badan pernyataan. Indentasi setiap baris berikutnya dari kondisi dengan 4 spasi relatif terhadap awal pernyataan. Letakkan kurung penutup kondisi bersama dengan kurung kurawal pembukaan pada baris yang terpisah:
if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) ) { return createKotlinNotConfiguredPanel(module) }
Pembenaran: perataan yang rapi dan pemisahan kondisi tubuh yang jelas dan kondisi tubuh
Tempatkan yang else
, catch
, finally
kata kunci, serta kata kunci while
dari loop do / while pada baris yang sama dengan braket keriting penutupan sebelumnya:
if (condition) {
Jika kondisi saat instruksi terdiri dari beberapa blok, disarankan untuk memisahkan mereka dari satu sama lain dengan garis kosong:
private fun parsePropertyValue(propName: String, token: Token) { when (token) { is Token.ValueToken -> callback.visitValue(propName, token.value) Token.LBRACE -> {
Tempatkan blok pendek when
pernyataan pada baris yang sama tanpa kurung kurawal.
when (foo) { true -> bar()
Saat menggunakan daftar parameter yang panjang, tempatkan baris setelah tanda kurung. Letakkan 4 spasi dan kelompokkan argumen yang secara logis terkait pada satu baris.
drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true )
Gunakan spasi di sekitar tanda sama antara nama parameter dan nilainya.
Saat menggunakan panggilan berantai, lakukan .
atau ?.
operator pada saluran baru dengan satu lekukan dalam 4 spasi:
val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile { it is PsiComment || it is PsiWhiteSpace }
Panggilan pertama dalam rantai biasanya harus memiliki satu baris di depannya, tetapi itu normal untuk tidak membuatnya jika kode lebih baik dibaca dan masuk akal.
Dalam ekspresi lambda, spasi harus digunakan di sekitar kurung kurawal dan di sekitar panah yang memisahkan parameter dari tubuh. Jika suatu panggilan menerima karakter lambda tunggal, itu harus digunakan di luar tanda kurung jika memungkinkan.
list.filter { it > 10 }
Saat menetapkan label ke ekspresi lambda, jangan beri spasi antara label dan kurung pembuka:
fun foo() { ints.forEach lit@{
Ketika mendeklarasikan nama parameter dalam ekspresi lambda multi-baris, letakkan nama pada baris pertama, lalu panah, dan pada baris baru, awal dari badan fungsi:
appendCommaSeparated(properties) { prop -> val propertyValue = prop.get(obj)
Jika daftar parameter tidak cocok pada satu baris, letakkan panah pada baris yang berbeda:
foo { context: Context, environment: Env -> context.configureEnv(environment) }
Saat menggunakan dokumentasi multi-baris, letakkan /**
di baris terpisah, dan mulai setiap baris berikutnya dengan tanda bintang:
Dokumentasi singkat dapat ditempatkan pada satu baris:
Secara umum, hindari menggunakan param dan kembalikan tag. Sebagai gantinya, sertakan deskripsi parameter dan kembalikan nilai langsung dalam komentar dokumentasi dan tambahkan referensi parameter di mana pun mereka disebutkan. Gunakan param dan kembali hanya ketika deskripsi panjang diperlukan yang tidak sesuai dengan makna teks utama.
Menghindari Konstruksi yang Tidak Perlu
Banyak konstruksi sintaksis di Kotlin adalah opsional dan disorot oleh lingkungan pengembangan sebagai tidak perlu, Anda tidak boleh menggunakannya dalam kode Anda hanya untuk membuat kode Anda "jelas".
Gunakan Unit kata kunci
Dalam fungsi, penggunaan kata kunci Unit tidak boleh digunakan:
fun foo() {
Titik koma
Hindari menggunakan tanda titik koma di setiap kesempatan.
Pola string
Jangan gunakan kurung kurawal saat memasukkan variabel sederhana ke string template. Gunakan kurung kurawal hanya untuk ekspresi panjang.
println("$name has ${children.size} children")
Penggunaan fitur bahasa secara idiomatis
Kekekalan
Lebih baik menggunakan data yang tidak bisa diubah sebelum data yang bisa diubah. Selalu mendeklarasikan variabel dan properti lokal sebagai val
, bukan var
, kecuali mereka benar-benar berubah.
Selalu gunakan antarmuka koleksi yang tidak dapat diubah ( Collection
, List
, Set
, Map
) untuk mendeklarasikan koleksi yang tidak berubah. Pada setiap kesempatan, saat menggunakan metode pabrik untuk membuat koleksi, gunakan implementasi yang mengembalikan koleksi tidak berubah:
: .
.
[Type alias]
, , :
typealias MouseClickHandler = (Any, MouseEvent) -> Unit typealias PersonIndex = Map<String, Person>
-
-, , it
. - .
-
. - , . , - .
( @
) .
, , boolean
, .
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
try
, if
when
, return
:
return if (x) foo() else bar()
if
when
if
when
when (x) { null -> ... else -> ... } if (x == null) ... else ...
, when
.
Boolean?
Boolean?
, if (value == true)
if (value == false)
, if (value ?: false)
if (value != null && value)
.
filtet
, map
.. . : forEach
( for
null forEach
)
, , , .
until
( ):
for (i in 0..n - 1) { ... }
.
\n
escape- .
, trimIndent
, , trimMargin
, :
assertEquals( """ Foo Bar """.trimIndent(), value ) val a = """if(a > 1) { | return a |}""".trimMargin()
. , , .
:
. , , , , . API, , . , .
infix
, , . : and
, to
, zip
. : add
.
infix
, .
, , . , , . , , .
class Point(val x: Double, val y: Double) { companion object { fun fromPolar(angle: Double, radius: Double) = Point(...) } }
, , .
: , , Kotlin null
, null
public
/, , Kotlin:
fun apiCall(): String = MyJavaApi.getProperty("name")
(package-level class-level) Kotlin:
class Person { val name: String = MyJavaApi.getProperty("name") }
, , Kotlin :
fun main() { val name = MyJavaApi.getProperty("name") println(name) }
apply
/ with
/ run
/ also
/ let
Kotlin . , :
- ? , ,
it
, this
( also
let
). also
, .
- ? ,
apply
also
. , with
, let
run
.
- null ? ,
apply
, let
run
. , with
also
.
API:
- ( API)
- ( )
- KDoc public api, , /