Lücken gewonnen. Übersetzen der Dokumentation zu Kotlin-Codierungskonventionen aus JetBrains

Hallo Habr! Ich mache Sie auf die Übersetzung der Dokumentationsseite der Kotlin Coding Conventions von JetBrains durch den Autor aufmerksam.


Originaldokumentation


Inhalt:



Verwenden des Styleguides bei Intellij Idea


Um die Formatierung in Intellij Idea gemäß dem aktuellen Handbuch anzuwenden, müssen Sie das Kotlin-Plugin Version 1.2.20 oder höher installieren. Gehen Sie zu Einstellungen | Editor | Codestil | Kotlin, klicken Sie auf den Link "Festlegen von ..." in der oberen rechten Ecke und wählen Sie "Vordefinierter Stil" / Kotlin Style Guide "aus dem Dropdown-Menü.


Um zu überprüfen, ob Ihr Code gemäß dem empfohlenen Stil formatiert ist, rufen Sie die Inspektionseinstellung auf und aktivieren Sie die Option "Kotlin | Stilprobleme | Datei ist nicht gemäß den Projekteinstellungen formatiert". Andere Validierungsregeln, z. B. Namenskonventionen, sind standardmäßig aktiviert.


Projektstruktur


Ordnerstruktur


In Projekten, die verschiedene Sprachen verwenden, müssen Dateien mit Kotlin-Code im selben Ordner wie der Code in anderen Sprachen liegen und dieselbe Dateistruktur verwenden, die für die Hauptsprache akzeptiert wird. Für Java müssen Dateien beispielsweise in der Ordnerstruktur entsprechend dem Paketnamen liegen.


In Projekten, die nur Kotlin verwenden, lautet die empfohlene Ordnerstruktur: Verwenden Sie Ordner, um Pakete mit übersprungenem Stammverzeichnis zu organisieren, d. H. Befindet sich der gesamte Code im Projekt im Paket "org.example.kotlin" und seinen Paketen, sollten sich die zum Paket "org.example.kotlin" gehörenden Quelldateien im Stammverzeichnis des Projekts und die Dateien mit dem Quellcode des Pakets "org" befinden. example.kotlin.foo.bar "sollte im Unterverzeichnis" foo / bar "relativ zum Stammverzeichnis des Projekts liegen.


Name der Quelldateien


Wenn die Kotlin-Datei nur eine Klasse enthält (möglicherweise im Zusammenhang mit der Deklaration der obersten Ebene), sollte sie benannt werden, sowie eine Klasse mit der Erweiterung .kt . Wenn die Datei mehrere Klassen enthält oder nur Deklarationen der obersten Ebene enthält, wählen Sie einen Namen, der beschreibt, was die Datei enthält, und benennen Sie die Datei entsprechend. Verwenden Sie ProcessDeclarations.kt mit einem Großbuchstaben, um Dateien zu benennen (z. B. ProcessDeclarations.kt ).


Der Dateiname sollte beschreiben, was der Code in der Datei tut. Das heißt, Sie sollten bedeutungslose Wörter wie "Util" zum Benennen von Dateien vermeiden.


Quelldateien organisieren


Das Platzieren mehrerer Deklarationen (Klassen, Funktionen oder Eigenschaften der obersten Ebene) in derselben Kotlin-Quelldatei ist willkommen, wenn diese Deklarationen semantisch eng miteinander verbunden sind und die Dateigröße angemessen bleibt (nicht mehr als einige hundert Zeilen).


Wenn Sie Erweiterungsfunktionen für eine Klasse definieren, die für alle Aspekte der Anwendung dieser Klasse gelten, legen Sie sie in derselben Datei ab, in der die Klasse selbst definiert ist. Wenn Sie Erweiterungsfunktionen definieren, die nur für den spezifischen Kontext der Verwendung dieser Klasse von Bedeutung sind, platzieren Sie sie neben Code, der die Erweiterungsfunktion verwendet. Erstellen Sie keine Dateien nur zum Speichern von "allen Foo-Erweiterungen".


Klassenstruktur


Normalerweise wird der Inhalt einer Klasse in der folgenden Reihenfolge sortiert:


  • Eigenschaftserklärungen und Initialisierungsblöcke
  • Sekundärkonstruktoren
  • Methodenerklärungen
  • Begleitobjekte

Sortieren Sie Methodendeklarationen nicht alphabetisch oder visuell und trennen Sie gewöhnliche Methoden nicht von Erweiterungsmethoden. Fügen Sie stattdessen den logisch verbundenen Code zusammen, damit jemand, der die Klasse von oben nach unten liest, der Logik des Geschehens folgen kann. Wählen Sie eine Sortierreihenfolge (Code auf höherer Ebene zuerst [Material auf höherer Ebene zuerst] und Details später oder umgekehrt) und halten Sie sich daran.


Platzieren Sie die verschachtelten Klassen neben dem Code, der diese Klassen verwendet. Wenn Klassen für die externe Verwendung vorgesehen sind und innerhalb der Klasse nicht referenziert werden, setzen Sie sie am Ende nach dem Begleitobjekt.


Interface Implementation Framework


Behalten Sie beim Implementieren einer Schnittstelle die gleiche Struktur wie die zu implementierende Schnittstelle bei (falls erforderlich, wechseln Sie sie mit zusätzlichen privaten Methoden ab, die für die Implementierung verwendet werden).


Struktur überschreibt


Neudefinitionen werden immer nacheinander zusammengestellt.


Namensregeln


Kotlin folgt den gleichen Namenskonventionen wie Java. Insbesondere:


Paketnamen in Kleinbuchstaben und keine Unterstriche verwenden (org.example.myproject). Die Verwendung von Namen mit mehreren Wörtern wird im Allgemeinen nicht empfohlen. Wenn Sie jedoch mehrere Wörter verwenden müssen, können Sie diese entweder einfach miteinander kombinieren oder den Kamelbuckel (org.examle.myProject) verwenden.


Groß- und Objektnamen beginnen mit einem Großbuchstaben und verwenden Kamelbuckel:


 open class DeclarationProcessor { ... } object EmptyDeclarationProcessor : DeclarationProcessor() { ... } 

Funktionsname


Namen von Funktionen, Eigenschaften und lokalen Variablen beginnen mit einem Kleinbuchstaben und enthalten keine Unterstriche:


 fun processDeclarations() { ... } var declarationCount = ... 

Ausnahme: Factory-Funktionen, die zum Instanziieren von Klassen verwendet werden, haben möglicherweise denselben Namen wie die zu erstellende Klasse:


 abstract class Foo { ... } class FooImpl : Foo { ... } fun Foo(): Foo { return FooImpl(...) } 

Name der Testmethoden


In Tests (und nur in Tests) dürfen Methodennamen mit Leerzeichen in Anführungszeichen verwendet werden. (Beachten Sie, dass solche Methodennamen derzeit von der Android-Laufzeit nicht unterstützt werden.) Unterstriche in Methodennamen sind auch im Testcode zulässig.


 class MyTestCase { @Test fun `ensure everything works`() { ... } @Test fun ensureEverythingWorks_onAndroid() { ... } } 

Benennung der Eigenschaft


Konstantennamen (Eigenschaften mit der Bezeichnung const oder Eigenschaften der obersten Ebene oder ein val Objekt ohne benutzerdefinierte get Funktion, die unveränderliche Daten enthält) müssen groß geschrieben werden und durch Unterstriche getrennt werden:


 const val MAX_COUNT = 8 val USER_NAME_FIELD = "UserName" 

Namen oder Objekteigenschaften der obersten Ebene, die Objekte mit Verhalten oder veränderlichen Daten enthalten, sollten im Kamelbuckel gebräuchliche Namen verwenden:


 val mutableCollection: MutableSet<String> = HashSet() 

Eigenschaftsnamen, die auf Singleton-Objekte verweisen, können denselben Namensstil wie Klassendeklarationen verwenden:


 val PersonComparator: Comparator<Person> = ... 

Für Aufzählungen können Sie Namen verwenden, die in Großbuchstaben geschrieben sind, die durch Unterstriche getrennt sind, oder im Kamelbuckelstil, beginnend mit einem Großbuchstaben, je nach Verwendung.


 enum class Color { RED, GREEN } 

 enum class Color { RedColor, GreenColor } 

Anmerkung des Übersetzers: Mischen Sie nur nicht verschiedene Stile. Wählen Sie einen Stil und bleiben Sie bei Ihrem Design.


Versteckte Eigenschaften benennen


Wenn eine Klasse zwei Eigenschaften hat, die konzeptionell identisch sind, aber eine Teil der öffentlichen API und die andere Teil der Implementierung ist, verwenden Sie den Unterstrich als Präfix für den Namen der versteckten Eigenschaft:


 class C { private val _elementList = mutableListOf<Element>() val elementList: List<Element> get() = _elementList } 

Auswahl der richtigen Namen


Der Klassenname ist normalerweise ein Substantiv oder eine Phrase, die erklärt, was die Klasse ist:


 List, PersonReader 

Der Name der Methode ist normalerweise eine Verb- oder Phrasenaktion, die erklärt, was die Methode tut:


 close, readPersons 

Der Name sollte auch angeben, ob die Methode das Objekt ändert oder ein neues zurückgibt. sort ist beispielsweise eine Sortierung, die die Sammlung ändert, und sorted ist die Rückgabe einer neuen sortierten Kopie der Sammlung.


Namen sollten den Zweck der Entität klar angeben, daher ist es besser, die Verwendung bedeutungsloser Wörter ( Manager , Wrapper usw.) in den Namen zu vermeiden.


Wenn Sie das Akronym als Teil des Anzeigennamens verwenden, verwenden Sie Großbuchstaben, wenn es aus zwei Buchstaben besteht ( IOStream ). oder nur den ersten Buchstaben in Großbuchstaben, wenn er länger ist ( XmlFormatter , HttpInputStream ).


Formatierung


In den meisten Fällen folgt Kotlin den Java-Formatierungskonventionen.


Verwenden Sie 4 Leerzeichen zum Einrücken. Verwenden Sie keine Registerkarten.


Platzieren Sie bei Klammern die Öffnungsklammer am Ende der Linie, an der die Struktur beginnt, und die Schließklammer in einer separaten Linie, die horizontal zur Öffnungsstruktur ausgerichtet ist.


 if (elements != null) { for (element in elements) { // ... } } 

(Hinweis: In Kotlin ist ein Semikolon optional, daher ist das Umbrechen von Zeilen wichtig. Das Sprachdesign umfasst geschweifte Klammern im Java-Stil. Wenn Sie versuchen, einen anderen Formatierungsstil zu verwenden, kann es zu unerwartetem Verhalten bei der Codeausführung kommen.)


Horizontale Räume


Platzieren Sie Leerzeichen um binäre Operatoren (a + b) . Ausnahme: (0..i) Sie keine Leerzeichen um den Operator "range to" (0..i)


Setzen Sie keine Leerzeichen um unäre Operatoren (a++)


Platzieren Sie Leerzeichen zwischen den Schlüsselsteuerwörtern ( if , when , for und while ) und den entsprechenden öffnenden Klammern.


Setzen Sie in der primären Deklaration eines Konstruktors, einer Methode oder einer Methode kein Leerzeichen vor die öffnende Klammer.


 class A(val x: Int) fun foo(x: Int) { ... } fun bar() { foo(1) } 

Setzen Sie niemals ein Leerzeichen nach ( , [ oder vor ] , ) .


Platzieren Sie niemals Platz um einen Punkt . oder Betreiber ?. ::


 foo.bar().filter { it > 2 }.joinToString() foo?.() 

Setzen Sie nach dem doppelten Schrägstrich ein Leerzeichen für den Kommentar // :


 //   

Setzen Sie keine Leerzeichen um die spitzen Klammern, die zur Angabe der Typparameter verwendet werden:


 Class Map<K, V> { ... } 

Setzen Sie keine Leerzeichen um den Doppelpunkt, um einen Verweis auf die :: class-Methode anzugeben:


 Foo::class String::length 

Stellen Sie kein Leerzeichen vor ? wird verwendet, um einen null zu markieren:


 String? 

Vermeiden Sie im Allgemeinen jede Art von horizontaler Ausrichtung. Das Umbenennen eines Bezeichners in einen Namen unterschiedlicher Länge sollte die Formatierung des Codes nicht beeinflussen.


Doppelpunkt


Setzen Sie ein Leerzeichen vor den Doppelpunkt : in den folgenden Fällen:


  • wenn es verwendet wird, um den Typ vom Supertyp zu trennen;

 abstract class Foo<out T : Any> 

  • beim Delegieren an einen Konstruktor der Oberklasse oder einen anderen Konstruktor derselben Klasse;

 constructor(x: String) : super(x) { ... } constructor(x: String) : this(x) { ... } 

  • nach dem Schlüsselwortobjekt.

 val x = object : IFoo { ... } 

Setzen Sie kein Leerzeichen davor : wenn eine Anzeige von ihrem Typ getrennt wird.


 abstract fun foo(a: Int): T 

Setzen Sie immer ein Leerzeichen nach :


 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 { ... } } 

Formatierung der Klassendeklaration


Klassen mit mehreren grundlegenden Konstruktorparametern und Kurznamen können in eine Zeile geschrieben werden:


 class Person(id: Int, name: String) 

Klassen mit längeren Namen oder der Anzahl der Parameter müssen so formatiert werden, dass sich jeder Hauptparameter des Konstruktors in einer separaten Zeile mit Einzug befindet. Außerdem muss sich die schließende Klammer in einer neuen Zeile befinden. Wenn wir die Vererbung verwenden, sollte sich der Aufruf des Konstruktors der Oberklasse oder die Liste der implementierten Schnittstellen in derselben Zeile wie die Klammer befinden:


 class Person( id: Int, name: String, surname: String ) : Human(id, name) { ... } 

Wenn Sie die Schnittstelle angeben und den Konstruktor der Oberklasse aufrufen, muss zuerst der Konstruktor der Oberklasse gefunden werden, dann ist der Name der Schnittstelle in einer neuen Zeile linksbündig:


 class Person( id: Int, name: String, surname: String ) : Human(id, name), KotlinMaker { ... } 

Bei Klassen mit einer langen Liste von Supertypen müssen Sie nach dem Doppelpunkt einen Zeilenumbruch einfügen und alle Supertypnamen horizontal links ausrichten:


 class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } } 

Um die Klassenüberschrift und ihren Hauptteil bei langer Klassenüberschrift klar zu trennen, setzen Sie entweder eine leere Zeile nach der Klassenüberschrift (wie im obigen Beispiel) oder setzen Sie die öffnende Klammer in eine separate Zeile:


 class MyFavouriteVeryLongClassHolder : MyLongHolder<MyFavouriteVeryLongClass>(), SomeOtherInterface, AndAnotherOne { fun foo() { ... } } 

Verwenden Sie für Konstruktorparameter einen regulären Einzug (4 Leerzeichen).


Begründung: Dadurch wird sichergestellt, dass die im Hauptkonstruktor deklarierten Eigenschaften denselben Einzug haben wie die im Hauptteil der Klasse deklarierten Eigenschaften.


Modifikatoren


Wenn eine Anzeige mehrere Modifikatoren enthält, ordnen Sie diese immer in der folgenden Reihenfolge an:


 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 

Stellen Sie alle Anmerkungen vor die Modifikatoren:


 @Named("Foo") private val foo: Foo 

Wenn Sie nicht an einer Bibliothek arbeiten, lassen Sie redundante Modifikatoren (z. B. public) weg.


Anmerkungen formatieren


Anmerkungen werden normalerweise in separaten Zeilen vor der Erklärung, an die sie angehängt sind, und mit demselben Einzug platziert:


 @Target(AnnotationTarget.PROPERTY) annotation class JsonExclude 

Anmerkungen ohne Argumente können in einer Zeile stehen:


 @JsonExclude @JvmField var x: String 

Eine Anmerkung ohne Argumente kann in dieselbe Zeile wie die entsprechende Deklaration eingefügt werden:


 @Test fun foo() { ... } 

Dateianmerkungen


Anmerkungen zu Dateien werden nach dem Kommentar zur Datei (falls vorhanden) vor der Paketanweisung eingefügt und durch eine leere Zeile vom Paket getrennt (um die Tatsache hervorzuheben, dass sie auf die Datei und nicht auf das Paket gerichtet sind).


 /** License, copyright and whatever */ @file:JvmName("FooBar") package foo.bar 

Funktionsformatierung


Wenn die Methodensignatur nicht in eine Zeile passt, verwenden Sie die folgende Syntax:


 fun longMethodName( argument: ArgumentType = defaultValue, argument2: AnotherArgumentType ): ReturnType { // body } 

Verwenden Sie für Funktionsparameter einen regulären Einzug (4 Leerzeichen).


Begründung: Konsistenz mit Konstruktorparametern

Es ist vorzuziehen, einen Ausdruck ohne geschweifte Klammern für Funktionen zu verwenden, die aus einer Zeile bestehen.


 fun foo(): Int { // bad return 1 } fun foo() = 1 // good 

Einzeilige Ausdrucksformatierung


Wenn der Text einer einzeiligen Funktion nicht in dieselbe Zeile wie die Deklaration passt, setzen Sie das Zeichen = in die erste Zeile. Den Körper des Ausdrucks um 4 Leerzeichen einrücken.


 fun f(x: String) = x.length 

Eigenschaftsformatierung


Für einfache schreibgeschützte Eigenschaften ist die einzeilige Formatierung vorzuziehen:


 val isEmpty: Boolean get() = size == 0 

Verwenden Sie für komplexere Eigenschaften immer get und set in separaten Zeilen:


 val foo: String get() { ... } 

Wenn der Initialisierer für Eigenschaften mit Initialisierung zu lang ist, fügen Sie nach dem Gleichheitszeichen einen Zeilenumbruch und einen Einzug von vier Leerzeichen für die Initialisierungszeichenfolge hinzu:


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

Anweisungen zur Formatierungssteuerung


Wenn die Bedingung in der if oder when Steueranweisung mehrzeilig ist, verwenden Sie immer geschweifte Klammern um den Hauptteil der Anweisung. Jede nachfolgende Zeile der Bedingung um 4 Leerzeichen relativ zum Beginn der Anweisung einrücken. Setzen Sie die schließenden Klammern des Zustands zusammen mit der öffnenden geschweiften Klammer in eine separate Zeile:


 if (!component.isSyncing && !hasAnyKotlinRuntimeInScope(module) ) { return createKotlinNotConfiguredPanel(module) } 

Begründung: saubere Ausrichtung und klare Trennung von Zustandskörper und Zustandskörper

Platzieren Sie die Schlüsselwörter else , catch und finally sowie das Schlüsselwort while der do / while-Schleife in derselben Zeile wie die vorherige schließende geschweifte Klammer:


 if (condition) { // body } else { // else part } try { // body } finally { // cleanup } 

Wenn die when Bedingungen der Anweisungen aus mehreren Blöcken bestehen, wird empfohlen, sich durch eine leere Zeile voneinander zu trennen:


 private fun parsePropertyValue(propName: String, token: Token) { when (token) { is Token.ValueToken -> callback.visitValue(propName, token.value) Token.LBRACE -> { // ... } } } 

Platzieren Sie die kurzen Blöcke von when Anweisungen in derselben Zeile ohne geschweifte Klammern.


 when (foo) { true -> bar() // good false -> { baz() } // bad } 

Formatierungsmethodenaufrufe


Wenn Sie eine lange Liste von Parametern verwenden, setzen Sie den Zeilenumbruch nach der Klammer. Ziehen Sie 4 Leerzeichen ein und gruppieren Sie die logisch verknüpften Argumente in einer Zeile.


 drawSquare( x = 10, y = 10, width = 100, height = 100, fill = true ) 

Verwenden Sie Leerzeichen um das Gleichheitszeichen zwischen dem Parameternamen und seinem Wert.


Formatieren eines Kettenfunktionsaufrufs


Wenn Sie verkettete Anrufe verwenden, setzen Sie . oder ?. Operatoren in einer neuen Zeile mit einer Einrückung in 4 Feldern:


 val anchor = owner ?.firstChild!! .siblings(forward = true) .dropWhile { it is PsiComment || it is PsiWhiteSpace } 

Der erste Aufruf in der Kette sollte normalerweise einen Zeilenumbruch vor sich haben, aber es ist normal, ihn nicht zu machen, wenn der Code besser gelesen wird und es sinnvoll ist.


Formatieren von Lambda-Ausdrücken


In Lambda-Ausdrücken sollten Leerzeichen um geschweifte Klammern und um den Pfeil verwendet werden, der die Parameter vom Körper trennt. Wenn ein Aufruf ein einzelnes Lambda-Zeichen akzeptiert, sollte es nach Möglichkeit außerhalb von Klammern verwendet werden.


 list.filter { it > 10 } 

Setzen Sie beim Zuweisen eines Etiketts zu einem Lambda-Ausdruck kein Leerzeichen zwischen das Etikett und die öffnende Klammer:


 fun foo() { ints.forEach lit@{ // ... } } 

Wenn Sie Parameternamen in einem mehrzeiligen Lambda-Ausdruck deklarieren, platzieren Sie die Namen in der ersten Zeile, dann im Pfeil und in der neuen Zeile am Anfang des Funktionskörpers:


 appendCommaSeparated(properties) { prop -> val propertyValue = prop.get(obj) // ... } 

Wenn die Parameterliste nicht in eine Zeile passt, setzen Sie den Pfeil in eine separate Zeile:


 foo { context: Context, environment: Env -> context.configureEnv(environment) } 

Papierkram


Wenn Sie eine mehrzeilige Dokumentation verwenden, setzen Sie /** in eine separate Zeile und beginnen Sie jede nachfolgende Zeile mit einem Sternchen:


 /** * This is a documentation comment * on multiple lines. */ 

Eine kurze Dokumentation kann in einer Zeile stehen:


 /** This is a short documentation comment. */ 

Vermeiden Sie im Allgemeinen die Verwendung von param- und return- Tags. Fügen Sie stattdessen eine Beschreibung der Parameter und Rückgabewerte direkt in den Dokumentationskommentar ein und fügen Sie Parameterreferenzen hinzu, wo immer sie erwähnt werden. Verwenden Sie param und geben Sie nur zurück, wenn eine lange Beschreibung erforderlich ist, die nicht in die Bedeutung des Haupttextes passt.


 // Avoid doing this: /** * Returns the absolute value of the given number. * @param number The number to return the absolute value for. * @return The absolute value. */ fun abs(number: Int) = ... // Do this instead: /** * Returns the absolute value of the given [number]. */ fun abs(number: Int) = ... 

Vermeiden unnötiger Konstruktionen


Viele syntaktische Konstrukte in Kotlin sind optional und werden von der Entwicklungsumgebung als unnötig hervorgehoben. Sie sollten sie nicht in Ihrem Code verwenden, nur um Ihren Code „klar“ zu machen.


Verwenden Sie das Schlüsselwort Unit


In Funktionen sollte die Verwendung des Schlüsselworts Unit nicht verwendet werden:


 fun foo() { // ": Unit" is omitted here } 

Semikolon


Vermeiden Sie es, bei jeder Gelegenheit ein Semikolon zu verwenden.


String-Muster


Verwenden Sie keine geschweiften Klammern, wenn Sie eine einfache Variable in eine Vorlagenzeichenfolge einfügen. Verwenden Sie geschweifte Klammern nur für lange Ausdrücke.


 println("$name has ${children.size} children") 

Idiomatische Verwendung von Sprachfunktionen


Unveränderlichkeit


Es ist vorzuziehen, unveränderliche Daten vor veränderlichen Daten zu verwenden. Deklarieren Sie lokale Variablen und Eigenschaften immer als val und nicht als var , es sei denn, sie ändern sich wirklich.


Verwenden Sie immer unveränderliche Sammlungsschnittstellen ( Collection , List , Set , Map ), um Sammlungen zu deklarieren, die sich nicht ändern. Verwenden Sie bei jeder Gelegenheit, wenn Sie Factory-Methoden zum Erstellen einer Sammlung verwenden, eine Implementierung, die unveränderliche Sammlungen zurückgibt:


 // Bad: use of mutable collection type for value which will not be mutated fun validateValue(actualValue: String, allowedValues: HashSet<String>) { ... } // Good: immutable collection type used instead fun validateValue(actualValue: String, allowedValues: Set<String>) { ... } // Bad: arrayListOf() returns ArrayList<T>, which is a mutable collection type val allowedValues = arrayListOf("a", "b", "c") // Good: listOf() returns List<T> val allowedValues = listOf("a", "b", "c") 

: .



.


 // Bad fun foo() = foo("a") fun foo(a: String) { ... } // Good fun foo(a: String = "a") { ... } 

[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 (x) return foo() else return bar() // return when(x) { 0 -> "zero" else -> "nonzero" } //   ,    when(x) { 0 -> return "zero" else -> return "nonzero" } 

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) { ... } // bad for (i in 0 until n) { ... } // good 


.


\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 , .

 // Context object is 'it' class Baz { var currentBar: Bar? val observable: Observable val foo = createBar().also { currentBar = it // Accessing property of Baz observable.registerCallback(it) // Passing context object as argument } } // Receiver not used in the block val foo = createBar().also { LOG.info("Bar created") } // Context object is 'this' class Baz { val foo: Bar = createBar().apply { color = RED // Accessing only properties of Bar text = "Foo" } } 

  • ? , apply also . , with , let run .

 // Return value is context object class Baz { val foo: Bar = createBar().apply { color = RED // Accessing only properties of Bar text = "Foo" } } // Return value is block result class Baz { val foo: Bar = createNetworkConnection().let { loadBar() } } 

  • null ? , apply , let run . , with also .

 // Context object is nullable person.email?.let { sendEmail(it) } // Context object is non-null and accessible directly with(person) { println("First name: $firstName, last name: $lastName") } 


API:


  • ( API)
  • ( )
  • KDoc public api, , /

Source: https://habr.com/ru/post/de431990/


All Articles