Przewodnik stylistyczny Kotlin

Ten dokument stanowi pełną definicję standardów Google dotyczących kodowania kodu źródłowego na Androida w języku programowania Kotlin. Plik źródłowy Kotlin jest opisany jako utworzony w stylu Google Android tylko wtedy, gdy jest zgodny z określonymi regułami.

Podobnie jak w przypadku innych przewodników poświęconych stylom programowania, omówione tu kwestie obejmują nie tylko kwestie estetyczne związane z formatowaniem, ale także inne typy konwencji czy standardów kodowania. W tym dokumencie skupiliśmy się jednak głównie na trudnych i uniwersalnych zasadach, których przestrzegamy, i unikamy udzielania rad, których nie da się jednoznacznie wyegzekwować (przez człowieka lub narzędzie).

Ostatnia aktualizacja: 6.09.2023 r.

Pliki źródłowe

Wszystkie pliki źródłowe muszą mieć kodowanie UTF-8.

Nazwy

Jeśli plik źródłowy zawiera tylko jedną klasę najwyższego poziomu, nazwa pliku powinna zawierać nazwę z uwzględnieniem wielkości liter oraz rozszerzenie .kt. Jeśli plik źródłowy zawiera wiele deklaracji najwyższego poziomu, wybierz nazwę opisującą jego zawartość, zastosuj parametr PascalCase (akceptowalna, jeśli nazwa pliku jest liczbą mnogą), i dołącz rozszerzenie .kt.

// MyClass.kt
class MyClass { }
// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …
// extensions.kt
fun MyClass.process() = // …
fun MyResult.print() = // …

Znaki specjalne

Odstępy

Oprócz sekwencji zakończenia wiersza jedynym znakiem odstępu występującym w dowolnym miejscu w pliku źródłowym jest poziomy znak odstępu ASCII (0x20). Oznacza to, że:

  • Wszystkie pozostałe znaki odstępu w literałach znaków i ciągach znaków korzystają ze zmiany znaczenia.
  • Znaki tabulacji nie służą do wcięcia.

Specjalne sekwencje zmiany znaczenia

W przypadku każdego znaku ze specjalną sekwencją zmiany znaczenia (\b, \n, \r, \t, \', \", \\ i \$) jest ona używana, a nie odpowiedniego Unicode (np. \u000a).

Znaki spoza tabeli znaków ASCII

W przypadku pozostałych znaków spoza tabeli znaków ASCII może to być rzeczywisty znak Unicode (np. ) lub odpowiadającego mu kodowania Unicode (np. \u221e). Wybór zależy od tego, co sprawia, że kod jest łatwiejszy do odczytania i zrozumienia. Odradzamy stosowanie zmian w standardzie Unicode w dowolnej lokalizacji w przypadku znaków drukowanych. Zdecydowanie odradzamy też stosowanie ich w przypadku literałów i komentarzy w postaci ciągów znaków.

Przykład Dyskusja
val unitAbbrev = "μs" Najlepsze: być jasne, nawet bez komentarza.
val unitAbbrev = "\u03bcs" // μs Kiepsko: nie ma powodu, by stosować znak zmiany znaczenia zawierający drukowany znak.
val unitAbbrev = "\u03bcs" Kiepsko: czytelnik nie ma pojęcia, co to jest.
return "\ufeff" + content Dobrze: w przypadku znaków niedrukowalnych należy używać znaków zmiany znaczenia. W razie potrzeby dodaj komentarz.

Struktura

Plik .kt zawiera następujące elementy (w kolejności):

  • Nagłówek dotyczący praw autorskich lub licencji (opcjonalnie)
  • Adnotacje na poziomie pliku
  • Instrukcja dotycząca pakietu
  • Importuj wyciągi
  • Deklaracje najwyższego poziomu

Każda z tych sekcji oddziela dokładnie 1 pusty wiersz.

Jeśli w pliku znajduje się nagłówek dotyczący praw autorskich lub licencji, powinien on znajdować się na samej górze w komentarzu wielowierszowym.

/*
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
 

Nie używaj komentarza w stylu KDoc ani komentarza z jednym wierszem.

/**
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
// Copyright 2017 Google, Inc.
//
// ...

Adnotacje na poziomie pliku

Adnotacje z elementem docelowym typu „use-site” typu „file” znajdują się między dowolnym komentarzem w nagłówku a deklaracją pakietu.

Instrukcja dotycząca pakietu

Instrukcja pakietu nie podlega żadnym limitom kolumn i nigdy nie jest zawijana wierszy.

Importuj wyciągi

Instrukcje importowania klas, funkcji i właściwości są zgrupowane na jednej liście i posortowane według zbioru ASCII.

Importy symboli wieloznacznych (dowolnego typu) są niedozwolone.

Podobnie jak instrukcja dotycząca pakietu, instrukcje importu nie podlegają limitowi kolumn i nigdy nie są zawijane wierszem.

Deklaracje najwyższego poziomu

Plik .kt może zadeklarować jeden lub więcej typów, funkcji, właściwości lub aliasów typów na najwyższym poziomie.

Zawartość pliku powinna dotyczyć jednego motywu. Może to być na przykład pojedynczy typ publiczny lub zbiór funkcji rozszerzeń wykonujących tę samą operację na wielu typach odbiorników. Niepowiązane deklaracje powinny znajdować się w osobnych plikach, a deklaracje publiczne w jednym pliku należy zminimalizować.

Liczby ani kolejność zawartości pliku nie ograniczają w sposób wyraźny.

Pliki źródłowe są zwykle odczytywane od góry do dołu, co oznacza, że kolejność powinna odzwierciedlać fakt, że deklaracje znajdujące się u góry będą informować o tych dalszych. Kolejność treści może być różna w zależności od pliku. Podobnie jeden plik może zawierać 100 właściwości, 10 kolejnych funkcji, a jeszcze jeden – pojedynczą klasę.

Ważne jest, aby w każdym pliku zastosowano pewną kolejność logiczna, którą osoba zarządzająca może wyjaśnić, jeśli pojawi się taka prośba. Na przykład nowe funkcje nie są po prostu dodawane na końcu pliku, ponieważ spowodowałoby to porządkowanie „chronologicznie wg daty dodania”, które nie jest porządkowaniem logicznym.

Określanie kolejności osób na zajęciach

Kolejność członków klasy jest zgodna z tymi samymi regułami co deklaracja najwyższego poziomu.

Formatowanie

Aparaty ortodontyczne

Nawiasy klamrowe nie są wymagane w przypadku gałęzi when i wyrażeń if, które mają nie więcej niż 1 gałąź else i mieszczą się w jednym wierszu.

if (string.isEmpty()) return

val result =
    if (string.isEmpty()) DEFAULT_VALUE else string

when (value) {
    0 -> return
    // …
}

Nawiasy klamrowe są też wymagane w przypadku wszystkich instrukcji i wyrażeń if, for, when oraz do i while, nawet jeśli treść jest pusta lub zawiera tylko jedną instrukcję.

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

Niepuste bloki

Nawiasy klamrowe są zgodne ze stylem Kernighana i Ritchiego („nawiasy egipskie”) w przypadku niepustych bloków i konstrukcji przypominających bloki:

  • Brak podziału wiersza przed otwierającym nawiasem klamrowym.
  • Podział wiersza po otwierającym nawiasie klamrowym.
  • Podział wiersza przed zamykającym nawiasem klamrowym.
  • Podział wiersza po zamykającym nawiasie klamrowym, tylko wtedy, gdy ten nawias kończy instrukcję lub kończy treść funkcji, konstruktora lub klasy nazwanej. Na przykład po nawiasie kwadratowym nie ma podziału wiersza, jeśli po nim następuje else lub przecinek.
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()
        }
    }
}

Poniżej znajdziesz kilka wyjątków dotyczących klas wyliczeniowych.

Puste bloki

Pusty blok lub konstrukcja przypominająca blok musi być w stylu K&R.

try {
    doSomething()
} catch (e: Exception) {} // WRONG!
try {
    doSomething()
} catch (e: Exception) {
} // Okay

Wyrażenia

Warunek if/else używany jako wyrażenie może pomijać nawiasy klamrowe tylko wtedy, gdy całe wyrażenie mieści się w jednym wierszu.

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
}

Wcięcie

Za każdym razem, gdy otwierany jest nowy konstrukt blokowy lub blokowy, wcięcie zwiększa się o cztery odstępy. Gdy blok się zakończy, wcięcie zostanie przywrócone do poprzedniego poziomu. Poziom wcięcia dotyczy zarówno kodu, jak i komentarzy w całym bloku.

Jedna instrukcja w wierszu

Po każdej instrukcji następuje podział wiersza. Średniki nie są używane.

Zawijanie wierszy

Kolumna kodu może zawierać maksymalnie 100 znaków. Z wyjątkiem sytuacji opisanych poniżej, wiersze, które przekraczają ten limit, muszą być zawijane w sposób opisany poniżej.

Wyjątki:

  • Wiersze, w których nie można przestrzegać limitu kolumn (np. długi adres URL w pliku KDoc)
  • Wyciągi package i import
  • Wiersze poleceń w komentarzu, które można wycinać i wklejać w powłoce.

Gdzie się rozbić

Najważniejsza dyrektywa zawijania wierszy to: wolą łamać tekst na wyższym poziomie składni. Poza tym:

  • Jeśli wiersz jest rozłączony w nazwie operatora lub funkcji infix, przerwa występuje po nazwie operatora lub funkcji infix.
  • Jeśli linia jest przerywana na podstawie następujących symboli operatora, przerwa znajduje się przed tym symbolem:
    • Separator kropek (., ?.).
    • Dwa dwukropki odwołania do elementu (::).
  • Nazwa metody lub konstruktora pozostaje dołączona do otwierającego nawiasu ((), który następuje po nim.
  • Do poprzedzającego tokenu pozostaje przecinek (,).
  • Strzałka lambda (->) pozostaje dołączona do listy argumentów, która ją poprzedza.

Funkcje

Jeśli podpis funkcji nie mieści się w jednym wierszu, rozdziel każdą deklarację parametru na osobny wiersz. Parametry zdefiniowane w tym formacie powinny mieć pojedyncze wcięcie (+4). Nawias zamykający ()) i typ zwrotu znajdują się w osobnym wierszu bez dodatkowego wcięcia.

fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = ""
): String {
    // …
}
Funkcje wyrażeń

Jeśli funkcja zawiera tylko jedno wyrażenie, można ją przedstawić jako funkcję wyrażenia.

override fun toString(): String {
    return "Hey"
}
override fun toString(): String = "Hey"

Właściwości

Jeśli inicjator usługi nie mieści się w jednym wierszu, rozdziel go po znaku równości (=) i użyj wcięcia.

private val defaultCharset: Charset? =
    EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)

Właściwości deklarujące funkcję get lub set powinny znajdować się w osobnych wierszach z normalnym wcięciem (+4). Sformatuj je, stosując takie same reguły jak funkcje.

var directory: File? = null
    set(value) {
        // …
    }
Właściwości tylko do odczytu mogą używać krótszej składni, która mieści się w jednym wierszu.
val defaultExtension: String get() = "kt"

Odstęp

Pionowa

Pojawi się jeden pusty wiersz:

  • Pomiędzy kolejnymi członkami klasy: właściwości, konstruktory, funkcje, zagnieżdżone klasy itp.
    • Wyjątek: opcjonalny pusty wiersz między 2 kolejnymi właściwościami (bez żadnego innego kodu). Takie puste wiersze są wykorzystywane w razie potrzeby do tworzenia logicznych grup właściwości i kojarzenia ich z ich właściwościami bazowymi (jeśli występują).
    • Wyjątek: poniżej znajdziesz puste linie między stałymi wyliczeniami.
  • Między instrukcją w razie potrzeby, aby uporządkować kod w podsekcje logiczne.
  • Opcjonalnie przed pierwszą instrukcją w funkcji, przed pierwszym elementem klasy lub po ostatnim elemencie klasy (ani nie zachęcam, ani nie zniechęcają).
  • Zgodnie z wymaganiami innych sekcji tego dokumentu (takich jak sekcja Struktura).

Kilka następujących po sobie pustych wierszy jest dozwolony, ale nie jest to zalecane ani wymagane.

Pozioma

Pojedyncza spacja ASCII oprócz tych, które są wymagane przez reguły języka lub inne style stylu, a także literały, komentarze i dokument KDoc, występuje tylko w tych miejscach:

  • Oddzielenie dowolnego zastrzeżonego słowa, np. if, for lub catch, od otwartego nawiasu ((), które następuje po nim w danym wierszu.
    // WRONG!
    for(i in 0..1) {
    }
    
    // Okay
    for (i in 0..1) {
    }
    
  • Oddzielenie dowolnego zastrzeżonego słowa, np. else lub catch, od zamykającego nawiasu klamrowego (}), który poprzedza je w danym wierszu.
    // WRONG!
    }else {
    }
    
    // Okay
    } else {
    }
    
  • Przed każdym otwartym nawiasem klamrowym ({).
    // WRONG!
    if (list.isEmpty()){
    }
    
    // Okay
    if (list.isEmpty()) {
    }
    
  • Po obu stronach dowolnego operatora binarnego.
    // WRONG!
    val two = 1+1
    
    // Okay
    val two = 1 + 1
    
    Dotyczy to również tych symboli „operatorów”:
    • strzałka w wyrażeniu lambda (->).
      // WRONG!
      ints.map { value->value.toString() }
      
      // Okay
      ints.map { value -> value.toString() }
      
    Ale nie:
    • dwa dwukropki (::) odwołania do członka.
      // WRONG!
      val toString = Any :: toString
      
      // Okay
      val toString = Any::toString
      
    • separatora kropek (.).
      // WRONG
      it . toString()
      
      // Okay
      it.toString()
      
    • operator zakresu (..).
      // WRONG
      for (i in 1 .. 4) {
        print(i)
      }
      
      // Okay
      for (i in 1..4) {
        print(i)
      }
      
  • Przed dwukropkiem (:) tylko wtedy, gdy jest on używany w deklaracji klasy do określenia klasy podstawowej lub interfejsów albo w klauzuli where na potrzeby ograniczeń ogólnych.
    // WRONG!
    class Foo: Runnable
    
    // Okay
    class Foo : Runnable
    
    // WRONG
    fun <T: Comparable> max(a: T, b: T)
    
    // Okay
    fun <T : Comparable> 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>
    
  • Po przecinku (,) lub dwukropku (:).
    // WRONG!
    val oneAndTwo = listOf(1,2)
    
    // Okay
    val oneAndTwo = listOf(1, 2)
    
    // WRONG!
    class Foo :Runnable
    
    // Okay
    class Foo : Runnable
    
  • Po obu stronach podwójnego ukośnika (//) zaczyna się komentarz na końcu wiersza. Możesz wstawić kilka spacji, ale nie jest to wymagane.
    // WRONG!
    var debugging = false//disabled by default
    
    // Okay
    var debugging = false // disabled by default
    

Ta reguła nigdy nie jest interpretowana jako wymagająca lub zabronionej dodatkowej przestrzeni na początku lub na końcu wiersza – ma zastosowanie tylko do przestrzeni wewnętrznej.

Konkretne konstrukcje

Klasy w liczbie

Wyliczenie bez funkcji i dokumentacji dotyczącej stałych wartości może być opcjonalnie sformatowane jako pojedynczy wiersz.

enum class Answer { YES, NO, MAYBE }

Gdy stałe w wyliczeniu są umieszczone w osobnych wierszach, pusty wiersz nie jest między nimi wymagany, chyba że definiują one treść.

enum class Answer {
    YES,
    NO,

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

Klasy wyliczeniowe są klasami, więc mają zastosowanie wszystkie pozostałe reguły formatowania.

Adnotacje

Adnotacje o użytkowniku lub typie są umieszczane w oddzielnych wierszach bezpośrednio przed konstrukcją z adnotacjami.

@Retention(SOURCE)
@Target(FUNCTION, PROPERTY_SETTER, FIELD)
annotation class Global

Adnotacje bez argumentów można umieścić w jednym wierszu.

@JvmField @Volatile
var disposable: Disposable? = null

Gdy dostępna jest tylko jedna adnotacja bez argumentów, można ją umieścić w tym samym wierszu co deklaracja.

@Volatile var disposable: Disposable? = null

@Test fun selectAll() {
    // …
}

Składni @[...] można używać tylko w przypadku jawnego celu związanego z używaniem witryny i tylko do łączenia w jednym wierszu co najmniej 2 adnotacji bez argumentów.

@field:[JvmStatic Volatile]
var disposable: Disposable? = null

Pośrednie zwroty lub typy usług

Jeśli treść funkcji wyrażenia lub inicjator właściwości jest wartością skalarną lub zwracany typ można łatwo wywnioskować z treści, można go pominąć.

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

Gdy tworzysz bibliotekę, zachowaj jawną deklarację typu, jeśli stanowi ona część publicznego interfejsu API.

Nazwy

Identyfikatory mogą zawierać tylko litery i cyfry z zestawu ASCII oraz (w nielicznych przypadkach wymienionych poniżej) podkreślenia. W ten sposób każda prawidłowa nazwa identyfikatora jest dopasowywana do wyrażenia regularnego \w+.

Specjalne prefiksy i sufiksy, takie jak te w przykładach name_, mName, s_name i kName, nie są używane z wyjątkiem właściwości kopii zapasowej (patrz Właściwości kopii zapasowej).

Nazwy pakietów

Nazwy pakietów są zapisywane małymi literami, a kolejne słowa są po prostu połączone (bez znaków podkreślenia).

// Okay
package com.example.deepspace
// WRONG!
package com.example.deepSpace
// WRONG!
package com.example.deep_space

Wpisz nazwy

Nazwy klas należy zapisywać w języku PascalCase i zwykle są to rzeczowniki lub frazy. np. Character lub ImmutableList. Nazwy interfejsów mogą też być rzeczownikami lub wyrażeniami rzeczownikowymi (np. List), ale czasami mogą też być przymiotnikami lub wyrażeniami przymiotnikowymi (np. Readable).

Nazwy klas testowych zaczynają się od nazwy testowanej klasy i kończą na Test. Na przykład HashTest lub HashIntegrationTest.

Nazwy funkcji

Nazwy funkcji są zapisywane wielbłąda i zazwyczaj są czasownikami lub wyrażeniami typu czasowniki. np. sendMessage lub stop.

W nazwach funkcji testowych mogą występować podkreślenia, które rozdzielają logiczne składniki nazwy.

@Test fun pop_emptyStack() {
    // …
}

Funkcje z adnotacjami @Composable, które zwracają wartość Unit, mają format PascalCased i nazywane jako rzeczowniki, jakby były typem.

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

Nazwy funkcji nie powinny zawierać spacji, bo nie jest to obsługiwane na każdej platformie (w szczególności nie jest to w pełni obsługiwane na Androidzie).

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

Nazwy stałe

W nazwach stałych wykorzystuje się wyrażenie UPPER_SNAKE_CASE, czyli wielkie litery i rozdzielone podkreśleniami. Ale co to dokładnie jest stała?

Stałe to właściwości val, które nie mają niestandardowej funkcji get, których zawartość jest głęboko stała i które funkcje nie mają wykrywalnych efektów ubocznych. Obejmuje to typy stałe i stałe kolekcje typów stałych, a także skalary i ciągi znaków, jeśli są oznaczone jako const. Jeśli dowolny obserwowany stan instancji może się zmienić, nie jest to stała. Samo zamiary niedokonywania zmian w obiekcie to za mało.

const val NUMBER = 5
val NAMES = listOf("Alice", "Bob")
val AGES = mapOf("Alice" to 35, "Bob" to 32)
val COMMA_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf()

Są to zwykle rzeczowniki lub frazy.

Wartości stałe można zdefiniować tylko w elemencie object lub w deklaracji najwyższego poziomu. Wartości, które w przeciwnym razie spełniają wymaganie stałej, ale są zdefiniowane w elemencie class, muszą mieć nazwę niestałą.

Stałe, które są wartościami skalarnymi, muszą korzystać z modyfikatora const.

Nazwy niestałe

Nazwy niestałe zapisują się w języku CamlCase. Te ustawienia mają zastosowanie do właściwości instancji, właściwości lokalnych i nazw parametrów.

val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet = 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")

Są to zwykle rzeczowniki lub frazy.

Właściwości kopii zapasowej

Jeśli potrzebna jest właściwość zapasowa, jej nazwa powinna być identyczna z nazwą rzeczywistej właściwości, chyba że jest poprzedzona znakiem podkreślenia.

private var _table: Map? = null

val table: Map
    get() {
        if (_table == null) {
            _table = HashMap()
        }
        return _table ?: throw AssertionError()
    }

Wpisz nazwy zmiennych

Każda zmienna typu ma nazwę w jednym z dwóch stylów:

  • Pojedyncza duża litera, po której może następować jedna cyfra (np. E, T, X, T2)
  • Nazwa w formie używanej przez zajęcia z wielką literą T (np. RequestT, FooBarT).

Etui wielbłąd

Czasami istnieje więcej niż jeden rozsądny sposób na przekształcenie angielskiej frazy wielbłąda z użyciem wielkich liter, np. użycie akronimów lub nietypowych terminów, takich jak „IPv6” czy „iOS”. Aby poprawić przewidywalność, użyj poniższego schematu.

Zaczyna się od imienia i nazwiska w formie prozy:

  1. Przekonwertuj wyrażenie na zwykły kod ASCII i usuń apostrofy. Na przykład „algorytm Mullera” może zostać zmieniony na „algorytm Muellera”.
  2. Podziel wynik na słowa, dzieląc je na spacje i pozostałe znaki interpunkcyjne (zwykle łączniki). Zalecane: jeśli jakieś słowo ma już konwencjonalną wielkość liter wielbłąda, podziel to na części składowe (np. „AdWords” zostanie zmieniony na „słowa reklamowe”. Pamiętaj, że słowo „iOS” nie jest w pełni zapisane wielkimi literami i jest niezgodne z jakąkolwiek konwencją, więc ta rekomendacja nie ma zastosowania.
  3. Teraz wszystkie treści (w tym akronimy) wpisz małymi literami, a potem wykonaj jedną z tych czynności:
    • Zaczynamy każde słowo wielką literą, aby zapisać je z użyciem wielkich liter.
    • Zaczyna się wielkimi literami pierwszy znak każdego wyrazu, z wyjątkiem pierwszego, który zostanie użyty do zapisu wielbłąda.
  4. Na koniec połącz wszystkie słowa w jeden identyfikator.

Zwróć uwagę, że wielkość liter w pierwotnych słowach jest prawie całkowicie pomijana.

Formularz zgłoszeniowy Dobrze Nieprawidłowo
„XML Http Request” XmlHttpRequest XMLHTTPRequest
„nowy identyfikator klienta” newCustomerId newCustomerID
"wewnętrzny stoper" innerStopwatch innerStopWatch
„obsługuje IPv6 w iOS” supportsIpv6OnIos supportsIPv6OnIOS
„Importer YouTube” YouTubeImporter YoutubeImporter*

(* Dozwolone, ale niezalecane).

Dokumentacja

Formatowanie

Podstawowe formatowanie bloków KDoc wygląda w tym przykładzie:

/**
 * Multiple lines of KDoc text are written here,
 * wrapped normally…
 */
fun method(arg: String) {
    // …
}

...lub w tym jednowierszowym przykładzie:

/** An especially short bit of KDoc. */

Zawsze akceptowana jest forma podstawowa. Formularz jednowierszowy można zastąpić, gdy cały blok KDoc (w tym znaczniki komentarzy) mieści się w jednym wierszu. Pamiętaj, że dzieje się tak tylko wtedy, gdy nie ma tagów blokujących, np. @return.

Akapity

Między akapitami i przed grupą tagów blokowych (jeśli istnieje) jest wyświetlany jeden pusty wiersz, czyli wiersz zawierający tylko wyrównaną na początku gwiazdkę (*).

Zablokuj tagi

Wszystkie standardowe „tagi blokujące” występują w takiej kolejności: @constructor, @receiver, @param, @property, @return, @throws, @see i nigdy nie mają pustego opisu. Jeśli tag blokowania nie mieści się w jednym wierszu, kolejne wiersze są wcięte o 4 spacje od pozycji znacznika @.

Fragment podsumowania

Każdy blok KDoc zaczyna się od krótkiego fragmentu podsumowania. Ten fragment jest bardzo ważny: jest to jedyna część tekstu, która pojawia się w określonych kontekstach, takich jak indeksy klas i metod.

Jest to fragment, czyli wyrażenie rzeczownikowe lub wyrażenie czasownikowe, a nie całe zdanie. Nie zaczyna się od „A `Foo` is a...” ani „This method returns...” ani też nie musi stanowić pełnego zdania, takiego jak „Save the record.”.

Wykorzystanie

Minimalnie w przypadku każdego typu public i każdego elementu public lub protected tego typu występuje KDoc, z kilkoma wyjątkami wymienionymi poniżej.

Wyjątek: funkcje, które nie wymagają wyjaśnienia

KDoc jest opcjonalny w przypadku „prostych, oczywistych” funkcji, takich jak getFoo i właściwości takich jak foo, w sytuacjach, gdy naprawdę nie warto powiedzieć niczego więcej, jak „Zwraca foo”.

Nie należy powoływać się na ten wyjątek po to, by usprawiedliwić pominięcie istotnych informacji, które przeciętny czytelnik powinien znać. Na przykład w przypadku funkcji o nazwie getCanonicalName lub właściwości o nazwie canonicalName nie pomijaj dokumentacji (z uzasadnieniem, że mówi ona tylko /** Returns the canonical name. */), jeśli typowy czytelnik może nie wiedzieć, co oznacza termin „nazwa kanoniczna”.

Wyjątek: zastąpienia

Plik KDoc nie zawsze jest dostępny w metodzie, która zastępuje metodę nadrzędną.