Котлин язык программирования: как устроен, где применяется и чем он лучше Java / Skillbox Media

Содержание

Основной синтаксис — Kotlin

Это подборка базового синтаксиса с примерами.
В конце каждого раздела вы найдете ссылку на более подробное описание соответствующей темы.

Вы также можете изучить все основы Kotlin в бесплатном курсе Основы Kotlin от JetBrains Academy.

Определение имени пакета и импорт

Имя пакета указывается в начале исходного файла, так же как и в Java.

package my.demo
import java.util.*
// ...

Но в отличие от Java, нет необходимости, чтобы структура пакетов совпадала со структурой папок:
исходные файлы могут располагаться в произвольном месте на диске.

См. Пакеты.

Точка входа в программу

В Kotlin точкой входа в программу является функция main.

fun main() {
    println("Hello world!")
}

Другая форма main может принимать массив строк String.

fun main(args: Array<String>) {
    println(args.contentToString())
}

Вывод в стандартный поток

print выводит свой аргумент в стандартный поток вывода.

print("Hello ")
print("world!")

println выводит свой аргумент и добавляет перевод строки, так что следующее, что вы выведите, появится на следующей строке.

println("Hello world!")
println(42)

Функции

Функция принимает два аргумента Int и возвращает Int.

fun sum(a: Int, b: Int): Int {
    return a + b
}

В качестве тела функции может выступать выражение. Тогда тип возвращаемого значения определяется автоматически.

fun sum(a: Int, b: Int) = a + b

Функция, не возвращающая никакого значения (void в Java).

fun printSum(a: Int, b: Int): Unit {
    println("сумма $a и $b равна ${a + b}")
}

Тип возвращаемого значения Unit может быть опущен.

fun printSum(a: Int, b: Int) {
    println("сумма $a и $b равна ${a + b}")
}

См. Функции.

Переменные

Неизменяемые (только для чтения) локальные переменные определяются с помощью ключевого слова val. Присвоить им значение можно только один раз.

val a: Int = 1   // Инициализация при объявлении
val b = 1        // Тип `Int` определен автоматически
val c: Int       // Указывать тип обязательно, если переменная не инициализирована сразу
c = 1            // Последующее присвоение

Изменяемые переменные объявляются с помощью ключевого слова var.

var x = 5 // Тип `Int` определен автоматически
x += 1

Вы можете объявлять глобальные переменные.

val PI = 3.14
var x = 0
fun incrementX() { 
    x += 1 
}

См. Свойства и поля.

Создание классов и экземпляров

Для создания класса используйте ключевое слово class.

class Shape

Свойства класса могут быть перечислены при его объявлении или в его теле.

class Rectangle(var height: Double, var length: Double) {
    var perimeter = (height + length) * 2 
}

Конструктор по умолчанию с параметрами, перечисленными при объявлении класса, доступен автоматически.

val rectangle = Rectangle(5.0, 2.0)
println("Периметр равен ${rectangle.perimeter}")

Чтобы объявить наследование между классами используйте двоеточие (:). По умолчанию классы являются финальными,
поэтому, чтобы сделать класс наследуемым, используйте open.

open class Shape
class Rectangle(var height: Double, var length: Double): Shape() {
    var perimeter = (height + length) * 2 
}

См. Классы и наследование и Объекты и экземпляры.

Комментарии

Также, как любой другой популярный современный язык, Kotlin поддерживает однострочные и многострочные (блочные) комментарии.

// Это однострочный комментарий
/* Это блочный комментарий
   из нескольких строк. */

Блочные комментарии в Kotlin могут быть вложенными.

/* Этот комментарий начинается здесь
/* содержит вложенный комментарий */
и заканчивается здесь. */

См. Документация Kotlin кода для информации о документации в комментариях.

Строковые шаблоны

Допустимо использование переменных внутри строк в формате $name или ${name}:

fun main(args: Array<String>) {
  if (args. size == 0) return
  print("Первый аргумент: ${args[0]}")
}
var a = 1
// просто имя переменной в шаблоне:
val s1 = "a равно $a" 
a = 2
// произвольное выражение в шаблоне:
val s2 = "${s1.replace("равно", "было равно")}, но теперь равно $a"
/*
  Результат работы программы:
  a было равно 1, но теперь равно 2
*/

См. Строковые шаблоны.

Условные выражения

fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a
    } else {
        return b
    }
}

В Kotlin if может быть использован как выражение (т. е. ifelse возвращает значение):

fun maxOf(a: Int, b: Int) = if (a > b) a else b

См. Выражение if.

Цикл for

val items = listOf("яблоко", "банан", "киви")
for (item in items) {
    println(item)
}

или

val items = listOf("яблоко", "банан", "киви")
for (index in items.indices) {
    println("${index} фрукт - это ${items[index]}")
}

См. Цикл for.

Цикл while

val items = listOf("яблоко", "банан", "киви")
var index = 0
while (index < items.size) {
    println("${index} фрукт - это ${items[index]}")
    index++
}

См. Цикл while.

Выражение when

fun describe(obj: Any): String =
    when (obj) {
        1          -> "Один"
        "Hello"    -> "Приветствие"
        is Long    -> "Long"
        !is String -> "Не строка"
        else       -> "Unknown"
    }

См. Выражение when.

Интервалы

Проверка на вхождение числа в интервал с помощью оператора in.

val x = 10
val y = 9
if (x in 1..y+1) {
    println("принадлежит диапазону")
}

Проверка значения на выход за пределы интервала.

val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
    println("-1 не принадлежит диапазону")
}
if (list.size !in list.indices) {
    println("размер списка также выходит за допустимый диапазон индексов списка")
}

Перебор значений в заданном интервале.

for (x in 1..5) {
    print(x)
}

Или по арифметической прогрессии.

for (x in 1..10 step 2) {
    print(x)
}
println()
for (x in 9 downTo 0 step 3) {
    print(x)
}

См. Интервалы.

Коллекции

Итерация по коллекции.

for (item in items) {
    println(item)
}

Проверка, содержит ли коллекция данный объект, с помощью оператора in.

val items = setOf("яблоко", "банан", "киви")
when {
    "апельсин" in items -> println("сочно")
    "apple" in items -> println("яблоко тоже подойдет")
}

Использование лямбда-выражения для фильтрации и модификации коллекции.

val fruits = listOf("банан", "авокадо", "яблоко", "киви")
fruits
    .filter { it.startsWith("а") }
    .sortedBy { it }
    .map { it.uppercase() }
    .forEach { println(it) }

См. Коллекции.

Nullable-значения и проверка на null

Ссылка должна быть явно объявлена как nullable (символ ? в конце имени), когда она может принимать значение null.

Возвращает null, если str не содержит числа.

fun parseInt(str: String): Int? {
  // ...
}

Использование функции, возвращающей null.

fun printProduct(arg1: String, arg2: String) {
    val x = parseInt(arg1)
    val y = parseInt(arg2)
    
    // Использование `x * y` приведет к ошибке, потому что они могут содержать null
    if (x != null && y != null) {
        // x и y автоматически приведены к не-nullable после проверки на null
    print(x * y)
    }
    else {
        println("'$arg1' или '$arg2' не число")
    }
}

или

// ...
if (x == null) {
    print("Неверный формат числа arg1: '$arg1'")
    return
}
if (y == null) {
    print("Неверный формат числа arg2: '$arg2'")
    return
}
// x и y автоматически приведены к не-nullable после проверки на null
  print(x * y)

См. Null-безопасность.

Проверка типа и автоматическое приведение типов

Оператор is проверяет, является ли выражение экземпляром заданного типа.
Если неизменяемая локальная переменная или свойство уже проверены на определенный тип, то в дальнейшем нет необходимости
явно приводить к этому типу:

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // в этом блоке `obj` автоматически преобразован в `String`
        return obj.length
    }
    // `obj` имеет тип `Any` вне блока проверки типа
    return null
}

или

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null
    // в этом блоке `obj` автоматически преобразован в `String`
    return obj.length
}

или даже

fun getStringLength(obj: Any): Int? {
    // `obj` автоматически преобразован в `String` справа от оператора `&&`
    if (obj is String && obj.length > 0) {
        return obj.length
    }
    return null
}

См. Классы и Приведение типов.

Классы — Kotlin

Классы в Kotlin объявляются с помощью использования ключевого слова class.

class Person { /*. ..*/ }

Объявление класса состоит из имени класса, заголовка (указания типов его параметров, основного конструктора и т.п) и тела класса,
заключённого в фигурные скобки. И заголовок, и тело класса являются необязательными составляющими. Если у класса нет тела, фигурные скобки могут быть опущены.

class Empty

Конструкторы

Класс в Kotlin может иметь основной конструктор (primary constructor) и один или более дополнительных конструкторов (secondary constructors).
Основной конструктор является частью заголовка класса, его объявление идёт сразу после имени класса (и необязательных параметров).

class Person constructor(firstName: String) { /*...*/ }

Если у основного конструктора нет аннотаций и модификаторов видимости, ключевое слово constructor может быть опущено.

class Person(firstName: String) { /*...*/ }

Основной конструктор не может содержать в себе исполняемого кода. Инициализирующий код может быть помещён в соответствующие блоки (initializers blocks),
которые помечаются словом init.

При создании экземпляра класса блоки инициализации выполняются в том порядке, в котором они идут в теле класса, чередуясь с инициализацией свойств.

class InitOrderDemo(name: String) {
    val firstProperty = "Первое свойство: $name".also(::println)
    
    init {
        println("Первый блок инициализации: ${name}")
    }
    
    val secondProperty = "Второе свойство: ${name.length}".also(::println)
    
    init {
        println("Второй блок инициализации: ${name.length}")
    }
}

Обратите внимание, что параметры основного конструктора могут быть использованы в инициализирующем блоке.
Они также могут быть использованы при инициализации свойств в теле класса.

class Customer(name: String) {
    val customerKey = name.uppercase()
}

Для объявления и инициализации свойств основного конструктора в Kotlin есть лаконичное синтаксическое решение:

class Person(val firstName: String, val lastName: String, var age: Int)

Такие объявления также могут включать в себя значения свойств класса по умолчанию.

class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)

Вы можете использовать завершающую запятую при объявлении свойств класса.

class Person(
    val firstName: String,
    val lastName: String,
    var age: Int, // завершающая запятая
) { /*...*/ }

Свойства, объявленные в основном конструкторе, могут быть изменяемые (var) и неизменяемые (val).

Если у конструктора есть аннотации или модификаторы видимости, ключевое слово constructor обязательно, и модификаторы используются перед ним.

class Customer public @Inject constructor(name: String) { /*...*/ }

Для более подробной информации см. «Модификаторы доступа».

Дополнительные конструкторы

В классах также могут быть объявлены дополнительные конструкторы (secondary constructors), перед которыми используется ключевое слово constructor.

class Person(val pets: MutableList<Pet> = mutableListOf())
class Pet {
    constructor(owner: Person) {
        owner. pets.add(this) // добавляет этого питомца в список домашних животных своего владельца
    }
}

Если у класса есть основной конструктор, каждый дополнительный конструктор должен прямо или косвенно ссылаться (через другой(ие) конструктор(ы)) на основной.
Осуществляется это при помощи ключевого слова this.

class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

Обратите внимание, что код в блоках инициализации фактически становится частью основного конструктора.
Дополнительный конструктор ссылается на основной при помощи своего первого оператора, поэтому код во всех блоках инициализации,
а также инициализация свойств выполняется перед выполнением кода в теле дополнительного конструктора.

Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также.

class Constructors {
    init {
        println("Блок инициализации")
    }
    constructor(i: Int) {
        println("Constructor $i")
    }
}

Если в абстрактном классе не объявлено никаких конструкторов (основного или дополнительных), у этого класса автоматически сгенерируется пустой конструктор без параметров.
Видимость этого конструктора будет public.

Если вы не желаете иметь класс с открытым public конструктором, вам необходимо объявить пустой конструктор с соответствующим модификатором видимости.

class DontCreateMe private constructor () { /*...*/ }

В JVM компилятор генерирует дополнительный конструктор без параметров в случае, если все параметры основного конструктора имеют значения по умолчанию. Это делает использование таких библиотек, как Jackson и JPA, более простым с Kotlin, так как они используют пустые конструкторы при создании экземпляров классов.

class Customer(val customerName: String = "")

Создание экземпляров классов

Для создания экземпляра класса конструктор вызывается так, как если бы он был обычной функцией.

val invoice = Invoice()
val customer = Customer("Joe Smith")

В Kotlin нет ключевого слова new.

Создание экземпляров вложенных, внутренних и анонимных внутренних классов описано в разделе Вложенные классы.

Члены класса

Классы могут содержать в себе:

  • Конструкторы и инициализирующие блоки
  • Функции
  • Свойства
  • Вложенные классы
  • Объявления объектов

Наследование

Классы могут быть производными друг от друга и формировать иерархии наследования.
Узнайте больше о наследовании в Котлине.

Абстрактные классы

Класс может быть объявлен как abstract со всеми или некоторыми его членами. Абстрактный член не имеет реализации в своём классе.
Обратите внимание, что нам не надо аннотировать абстрактный класс или функцию словом open — это и так подразумевается.

abstract class Polygon {
    abstract fun draw()
}
class Rectangle : Polygon() {
    override fun draw() {
        // рисование прямоугольника
    }
}

Можно переопределить неабстрактный open член абстрактным.

open class Polygon {
    open fun draw() {
        // некоторый метод рисования полигонов по умолчанию
    }
}
abstract class WildShape : Polygon() {
    // Классы, которые наследуют WildShape, должны предоставлять свой собственный
    // метод рисования вместо использования по умолчанию для полигона
    abstract override fun draw()
}

Вспомогательные объекты

Если вам нужно написать функцию, которая может быть использована без создания экземпляра класса, имеющую доступ к данным внутри этого класса
(к примеру, фабричный метод), вы можете написать её как член объявления объекта внутри этого класса.

В частности, если вы объявляете вспомогательный объект в своём классе,
у вас появляется возможность обращаться к членам класса, используя только название класса в качестве классификатора.

Язык программирования Котлин

Начать

Разработано JetBrains
и участники с открытым исходным кодом

Мультиплатформенный

Делитесь кодом на своих условиях и для разных платформ

На стороне сервера

Современный опыт разработки с использованием знакомой технологии JVM

Мультиплатформенные библиотеки

Создайте библиотеку, которая работает на нескольких платформах

Андроид

Рекомендуется Google для создания приложений для Android

Наука о данных

Простой

Асинхронный

Объектно-ориентированный

Функциональный

Идеально подходит для тестов

 5">
веселая главная () {
    val name = "stranger" // Объявите вашу первую переменную
    println("Привет, $name!") // ...и пользуйся!
    print("Текущее количество:")
    for (i in 0..10) { // Цикл в диапазоне от 0 до 10
        распечатать("$я")
    }
} 
импортировать kotlinx.coroutines.*
suspend fun main() { // Функция, которую можно приостановить и возобновить позже
    val start = System.currentTimeMillis()
    coroutineScope { // Создаем область для запуска сопрограмм
        для (я в 1..10) {
            launch { // Запустить 10 одновременных задач
                delay(3000L - i * 300) // Приостановить их выполнение
                log(начало, "Обратный отсчет: $i")
            }
        }
    }
    // Выполнение продолжается после завершения всех сопрограмм в области видимости
    log(начало, "Взлет!")
}
журнал развлечений (начало: длинное, сообщение: строка) {
    println("$msg" +
            "(на ${Thread. currentThread().name}) " +
            "после ${(System.currentTimeMillis() - start)/1000F} с")
} 
абстрактный класс Person (имя val: String) {
    абстрактное веселое приветствие ()
}
интерфейс FoodConsumer {
    весело есть ()
    fun pay(amount: Int) = println("Вкусно! Вот $amount баксов!")
}
класс RestaurantCustomer (имя: строка, значение блюда: строка): человек (имя), FoodConsumer {
    fun order() = println("$dish, пожалуйста!")
    переопределить fun eat() = println("*Ест $блюдо*")
    переопределить fungreet() = println("Это я, $name.")
}
веселая главная () {
    val sam = RestaurantCustomer("Сэм", "Салат")
    sam.greet() // Реализация абстрактной функции
    sam.order() // Функция-член
    sam.eat() // Реализация функции интерфейса
    sam.pay(10) // Реализация по умолчанию в интерфейсе
} 
 5">
веселая главная () {
    // Кто отправил больше всего сообщений?
    val FreightSender = сообщения
        .groupBy(Сообщение::отправитель)
        .maxByOrNull { (_, сообщения) -> messages.size }
        ?.key // Получить их имена
    println(frequentSender) // [Ма]
    // Кто отправители?
    val отправители = сообщения
        .asSequence() // Делаем операции ленивыми (для длинной цепочки вызовов)
        .filter { it.body.isNotBlank() && !it.isRead } // Использовать лямбда-выражения...
        .map(Message::sender) // ...или ссылки на элементы
        .отчетливый()
        .сортировано()
        .toList() // Преобразование последовательности обратно в список для получения результата
    println(senders) // [Адам, Ма]
}
Сообщение класса данных( // Создаем класс данных
    val отправитель: строка,
    тело val: Строка,
    val isRead: Boolean = false, // Предоставляем значение по умолчанию для аргумента
)
val messages = listOf( // Создаем список
    Сообщение("Ма", "Эй! Где ты?"),
    Message("Адам", "Сегодня все идет по плану?"),
    Сообщение("Ма", "Пожалуйста, ответьте.  Я вас потеряла!"),
) 
// Тесты
// Следующий пример работает только для JVM
импортировать org.junit.Test
импортировать kotlin.test.*
класс SampleTest {
    @Тест
    fun `test sum`() { // Пишем имена тестов с пробелами в обратных кавычках
        значение а = 1
        знач б = 41
        assertEquals(42, sum(a, b), "Неверный результат для суммы($a, $b)")
    }
    @Тест
    весело `проверить вычисление`() {
        assertTrue("Ошибка вычисления") {
            setup() // Используем лямбду, возвращающую испытуемый
            вычислить()
        }
    }
}
// Источники
забавная сумма (a: Int, b: Int) = a + b
забавная установка () {}
весело вычислить () = правда 

Начать ↗

Краткий

Выразительный

Асинхронный

Совместимый

 5">
// Более чем на 30% меньше строк кода по сравнению с Java
// (на основе опыта Duolingo и других компаний)
класс данных Книга (
    val title: String, // + автоматически генерируется equals(),
    val year: Int // hashCode(), toString() и copy()
)
fun Century(year: Int) = (year - 1) / 100 + 1 // Функция верхнего уровня,
                                               // тело одного выражения
веселая главная () {
    val books = listOf( // Создаем список
        Book("Дон Кихот", 1605), // Нет ключевого слова `new`
        Книга("Властелин колец", 1955)
    )
    val classics = books.filter { век(it.year)
             
// Приложения, созданные с помощью Kotlin, на 20% реже вылетают
// (на основе внутренних данных Google)
fun printMessagesUppercased(messages: List) { // Элементы списка могут быть пустыми
    // messages.add(Message("Java")) // ОШИБКА: Список доступен только для чтения
    messages. onEachIndexed {индекс, сообщение ->
        print("\nСообщение #$index: ")
        // print(msg.uppercase()) // ОШИБКА: `msg` может быть нулевым
        msg?.let { // Печатать, только если `msg` не равно null
            print(it.uppercase()) // ОК, это строка
        }
    }
}
веселая главная () {
    val messages = mutableListOf («привет», null, «мир»)
    // messages = mutableListOf("!!!") // ОШИБКА: невозможно переназначить значение
    messages.add("Kotlin") // ОК: список можно изменять
    printMessagesUppercased(messages) // Передать список только для чтения
} 
импортировать kotlin.math.absoluteValue
веселая главная () {
// начало выборки
    val dates = listOf (от 1 до «января», от 13 до «мая», от 22 до «сентября», от 23 до «декабря»)
    date.forEach { (день, месяц) -> // Проходим список пар с завершающей лямбдой
        println("${day.ordinal()} of $month") // Использовать функцию расширения Int. ordinal()
    }
    создатьпустое окно()
        .apply { // Настраиваем свойства объекта
            ширина = 300
            высота = 200
            Видимый = Истина
        }.also { w -> // Выполняем дополнительную операцию над цепочкой вызовов
            показатьокно(ж)
        }
    issueById["13456"]
        ?.takeIf { it.status == Status.FIXED } // Использовать значение, только если условие истинно
        ?.let { // Делаем что-то, только если значение не равно null
            println("Мы исправили это: $it")
        }
//конец выборки
}
// Функция расширения
fun Int.ordinal() = this.absoluteValue.let { iAbs ->
    суффикс val = if (iAbs % 100 в 11..13) "th" else
        когда (iAbs % 10) {
            1 -> "ст"
            2 -> «й»
            3 -> "й"
            иначе -> "й"
        }
    "$этот$суффикс"
}
Окно класса данных (ширина переменной: Int, высота переменной: Int, переменная isVisible: логическое значение)
весело createEmptyWindow() = Окно (0, 0, ложь)
весело showWindow(окно: Окно) {
    println("Показано $окно")
}
Статус класса enum {OPEN, FIXED, IN_PROGRESS}
Проблема класса данных (val status: Status)
val issueById = mutableMapOf(
    "13456" для выдачи (Статус. ИСПРАВЛЕНО)
) 
импортировать kotlinx.coroutines.*
импортировать kotlinx.coroutines.flow.*
// начало выборки
// Более 50% профессиональных разработчиков используют сопрограммы
// сообщаем о повышении производительности
// (на основе внутренних данных Google)
весело main () = runBlocking {
    val start = System.currentTimeMillis()
    coroutineScope { // Создаем область для сопрограмм
        val waitJob = launch { // Запуск сопрограммы
            ожидание(начало, 150)
        }
        countdownSignals(10, 300).collect { значение -> // Сбор элементов потока
            log(начало, "Обратный отсчет: $значение")
        }
        waitJob.cancel() // Отмена сопрограммы
    }
    log(start, "Liftoff!") // Выполнение продолжается, когда все
} // сопрограммы завершили работу
//конец выборки
fun countdownSignals(n: Int, delayMillis: Long): Flow = поток { // Построитель потока
    for (i in (1. .n).reversed()) {
        delay(delayMillis) // Задержка в выдаче сигналов
        emit(i) // Выдать элемент потока
    }
}
// Функция, которую можно приостановить и возобновить позже
приостановить ожидание удовольствия (начало: долго, delayMillis: долго) {
    while (currentCoroutineContext().isActive) { // Проверяем контекст сопрограммы
        log(начало, "Ожидание...")
        delay(delayMillis) // Одновременное ожидание
    }
}
журнал развлечений (начало: длинное, сообщение: строка) {
    println("$msg после ${(System.currentTimeMillis() - start)/1000F}s")
} 
// Используйте любую существующую библиотеку или фреймворк JVM
// Вызов кода Kotlin из Java без проблем
@SpringBootApplication
демонстрационное приложение класса
весело main(аргументы: Array) {
    runApplication(*args)
}
@RestController
класс MessageResource {
    @GetMapping
    забавный индекс(): Список<Сообщение> = listOf(
        Сообщение("1", "Привет!"),
        Сообщение("2", "Добрый день!"),
        Сообщение("3", "Привет!"),
    )
}
Сообщение класса данных (действительный идентификатор: строка?, допустимый текст: строка) 

Начать ↗

Делитесь кодом на своих условиях и для разных платформ

Упростите разработку кроссплатформенных проектов с помощью Kotlin Multiplatform. Это сокращает время, затрачиваемое на написание и сопровождение одного и того же кода для разных платформ, сохраняя при этом гибкость и преимущества нативного программирования.
Приложения Kotlin будут работать на разных операционных системах, таких как iOS, Android, macOS, Windows, Linux, watchOS и других.

Узнать о мультиплатформе Kotlin →
Узнать больше →

Большое, дружелюбное и полезное

сообщество

У Kotlin отличная поддержка и множество участников в быстрорастущем глобальном сообществе. Воспользуйтесь преимуществами богатой экосистемы с широким спектром библиотек сообщества. Помощь всегда рядом — обратитесь к многочисленным ресурсам сообщества или напрямую обратитесь к команде Kotlin.

Присоединяйтесь к сообществу →

Gradle представляет Kotlin как язык для написания скриптов сборки

Corda — это платформа распределенного реестра с открытым исходным кодом, поддерживаемая крупными банками и полностью построенная на Kotlin

.

Evernote недавно интегрировала Kotlin в свой Android-клиент

Android-приложение Coursera частично написано на Kotlin

Spring использует языковые функции Kotlin, чтобы предлагать более лаконичные API

Весь новый код в Android-приложении Trello написан на языке Kotlin

Хотите попробовать?

Начало работы

Базовый синтаксис | Kotlin Documentation

Это набор основных элементов синтаксиса с примерами. В конце каждого раздела вы найдете ссылку на подробное описание соответствующей темы.

Вы также можете изучить все основы Kotlin с помощью бесплатного курса Kotlin Basics в Академии JetBrains.

Определение пакета и импорт

Спецификация пакета должна быть в начале исходного файла.

пакет my.demo
импортировать kotlin.text.*
// …

Не требуется совпадение каталогов и пакетов: исходные файлы могут располагаться в файловой системе произвольно.

См. пакеты.

Точка входа программы

Точкой входа приложения Kotlin является основная функция .

весело main() {
println(«Привет, мир!»)
}

Другая форма main принимает переменное количество аргументов String .

весело main(args: Array) {
println(args.contentToString())
}

Вывести на стандартный вывод

напечатать вывести свой аргумент на стандартный вывод.

весело main() {
// начало выборки
распечатать(«Здравствуйте»)
распечатать(«мир!»)
//конец выборки
}

println выводит свои аргументы и добавляет разрыв строки, так что следующее печатаемое вами сообщение появляется на следующей строке.

весело main() {
// начало выборки
println(«Привет, мир!»)
распечатать(42)
//конец выборки
}

Функции

Функция с двумя Параметры Int и тип возвращаемого значения Int .

// начало выборки
забавная сумма (a: Int, b: Int): Int {
вернуть а + б
}
//конец выборки
веселая главная () {
print(«Сумма 3 и 5 равна»)
println (сумма (3, 5))
}

Тело функции может быть выражением. Его возвращаемый тип выводится.

// начало выборки
забавная сумма (a: Int, b: Int) = a + b
//конец выборки
веселая главная () {
println(«Сумма 19 и 23 равна ${сумма(19, 23)}»)
}

Функция, которая не возвращает значимого значения.

// начало выборки
fun printSum(a: Int, b: Int): Unit {
println(«Сумма $a и $b равна ${a + b}»)
}
//конец выборки
веселая главная () {
printSum(-1, 8)
}

Тип возвращаемого значения Unit может быть опущен.

// начало выборки
fun printSum(a: Int, b: Int) {
println(«Сумма $a и $b равна ${a + b}»)
}
//конец выборки
веселая главная () {
printSum(-1, 8)
}

См. Функции.

Переменные

Локальные переменные только для чтения определяются с помощью ключевого слова val . Им можно присвоить значение только один раз.

весело main() {
// начало выборки
val a: Int = 1 // немедленное присвоение
val b = 2 // выводится тип `Int`
val c: Int // Требуется тип, если инициализатор не указан
c = 3 // отложенное присваивание
//конец выборки
println(«а = $а, б = $b, с = $с»)
}

Переменные, которые можно переназначать, используют ключевое слово var .

весело main() {
// начало выборки
var x = 5 // выводится тип `Int`
х += 1
//конец выборки
println(«х = $х»)
}

Вы можете объявлять переменные на верхнем уровне.

//начало выборки
знач PI = 3,14
переменная х = 0
весело приращениеX () {
х += 1
}
//конец выборки
веселая главная () {
println(«x = $x; PI = $PI»)
приращениеX()
println(«приращениеX()»)
println(«x = $x; PI = $PI»)
}

См. также Свойства.

Создание классов и экземпляров

Чтобы определить класс, используйте ключевое слово class .

class Shape

Свойства класса могут быть перечислены в его объявлении или теле.

класс Прямоугольник (высота переменной: Double, длина переменной: Double) {
var периметр = (высота + длина) * 2
}

Конструктор по умолчанию с параметрами, указанными в объявлении класса, доступен автоматически.

класс Прямоугольник (высота переменной: Double, длина переменной: Double) {
var периметр = (высота + длина) * 2
}
веселая главная () {
// начало выборки
val прямоугольник = прямоугольник (5.0, 2.0)
println(«Периметр ${rectangle.perimeter}»)
//конец выборки
}

Наследование между классами объявляется двоеточием ( : ). Классы являются окончательными по умолчанию; чтобы сделать класс наследуемым, пометьте его как открыть .

открытый класс Форма
класс Rectangle (высота var: Double, длина var: Double): Shape () {
var периметр = (высота + длина) * 2
}

См. классы, объекты и экземпляры.

Как и большинство современных языков, Kotlin поддерживает однострочные (или end-of-line ) и многострочные ( block ) комментарии.

// Это комментарий в конце строки
/* Это блочный комментарий
на несколько строк. */

Блочные комментарии в Kotlin могут быть вложенными.

/* Здесь начинается комментарий
/* содержит вложенный комментарий *⁠/
и заканчивается здесь. */

См. Документирование кода Kotlin для получения информации о синтаксисе комментариев к документации.

Строковые шаблоны

fun main() {
// начало выборки
вар а = 1
// простое имя в шаблоне:
val s1 = «а есть $а»

а = 2
// произвольное выражение в шаблоне:
val s2 = «${s1.replace(«is», «was»)}, но теперь это $a»
//конец выборки
println(s2)
}

Дополнительные сведения см. в разделе Строковые шаблоны.

Условные выражения

//sampleStart
удовольствие maxOf(a: Int, b: Int): Int {
если (а > б) {
вернуть
} еще {
вернуть б
}
}
//конец выборки
веселая главная () {
println(«максимум 0 и 42 равно ${maxOf(0, 42)}»)
}

В Kotlin if также можно использовать как выражение.

// начало выборки
fun maxOf(a: Int, b: Int) = if (a > b) a else b
//конец выборки
веселая главная () {
println(«максимум 0 и 42 равно ${maxOf(0, 42)}»)
}

См. , если -выражения.

for loop

fun main() {
// начало выборки
val items = listOf(«яблоко», «банан», «киви»)
для (пункт в пунктах) {
println(элемент)
}
//конец выборки
}

или

fun main() {
// начало выборки
val items = listOf(«яблоко», «банан», «киви»)
for (индекс в items.indices) {
println(«элемент $index равен ${items[index]}»)
}
//конец выборки
}

См. Цикл.

цикл while

весело main() {
// начало выборки
val items = listOf(«яблоко», «банан», «киви»)
переменный индекс = 0
в то время как (индекс

См. цикл while.

когда выражение

//sampleStart
забавное описание (obj: Any): String =
когда (объект) {
1 -> «Один»
«Здравствуйте» -> «Приветствие»
длинный -> «длинный»
!is String -> «Не строка»
еще -> «Неизвестно»
}
//конец выборки
веселая главная () {
println(описать(1))
println(описать(«Привет»))
println(описать(1000L))
println(описать(2))
println(описать(«другое»))
}

Посмотреть, когда выражение.

Диапазоны

Проверить, находится ли число в диапазоне, используя оператор в .

весело main() {
// начало выборки
значение х = 10
значение у = 9
если (х в 1..у+1) {
println(«входит в диапазон»)
}
//конец выборки
}

Проверить, не выходит ли число за допустимые пределы.

весело main() {
// начало выборки
val list = listOf («а», «б», «в»)

если (-1 !in 0..list.lastIndex) {
println(«-1 вне допустимого диапазона»)
}
если (list.size ! в list.indices) {
println(«Размер списка также выходит за допустимый диапазон индексов списка»)
}
//конец выборки
}

Повторить диапазон.

весело main() {
// начало выборки
для (х в 1..5) {
печать (х)
}
//конец выборки
}

Или в последовательности.

весело main() {
// начало выборки
для (x в 1..10 шаге 2) {
печать (х)
}
println()
for (x in 9 downTo 0 step 3) {
печать (х)
}
//конец выборки
}

См. Диапазоны и последовательности.

Коллекции

Перебор коллекции.

весело main() {
val items = listOf(«яблоко», «банан», «киви»)
// начало выборки
для (пункт в пунктах) {
println(элемент)
}
//конец выборки
}

Проверить, содержит ли коллекция объект, используя оператор в .

весело main() {
val items = setOf(«яблоко», «банан», «киви»)
// начало выборки
когда {
«оранжевый» в пунктах -> println («сочный»)
«яблоко» в пунктах -> println («яблоко тоже подойдет»)
}
//конец выборки
}

Использование лямбда-выражений для фильтрации и сопоставления коллекций:

fun main() {
// начало выборки
val fruit = listOf(«банан», «авокадо», «яблоко», «киви»)
фрукты
.filter { it.startsWith («а») }
.sortedBy {это}
.map { it.uppercase() }
.forEach {println(это)}
//конец выборки
}

См. Обзор коллекций.

Значения, допускающие значение NULL, и проверки на значение NULL

Ссылка должна быть явно помечена как допускающая значение NULL, если возможно значение null . Имена типов, допускающие значение NULL, имеют ? в конце.

Вернуть null , если str не содержит целое число:

fun parseInt(str: String): Int? {
// …
}

Использовать функцию, возвращающую нулевое значение:

fun parseInt(str: String): Int? {
вернуть str.toIntOrNull()
}
// начало выборки
fun printProduct(arg1: строка, arg2: строка) {
val x = parseInt (arg1)
val y = parseInt (arg2)
// Использование `x * y` приводит к ошибке, поскольку они могут содержать нули.
если (х != null && y != null) {
// x и y автоматически приводятся к ненулевым значениям после нулевой проверки
println(х * у)
}
еще {
println(«‘$arg1’ или ‘$arg2’ не является числом»)
}
}
//конец выборки
веселая главная () {
печататьПродукт(«6», «7»)
printProduct(«а», «7»)
printProduct(«а», «б»)
}

или

fun parseInt(str: String): Int? {
вернуть str.toIntOrNull()
}
fun printProduct(arg1: строка, arg2: строка) {
val x = parseInt (arg1)
val y = parseInt (arg2)

// начало выборки
// . ..
если (х == ноль) {
println(«Неверный числовой формат в arg1: ‘$arg1′»)
возвращаться
}
если (у == ноль) {
println(«Неверный числовой формат в arg2: ‘$arg2′»)
возвращаться
}
// x и y автоматически приводятся к ненулевым значениям после нулевой проверки
println(х * у)
//конец выборки
}
веселая главная () {
печататьПродукт(«6», «7»)
printProduct(«а», «7»)
printProduct(«99», «б»)
}

См. Null-безопасность.

Проверка типов и автоматическое приведение типов

Оператор is проверяет, является ли выражение экземпляром типа. Если неизменяемая локальная переменная или свойство проверяется для определенного типа, нет необходимости приводить ее явно:

//sampleStart
fun getStringLength(obj: Any): Int? {
если (объект является строкой) {
// `obj` автоматически приводится к `String` в этой ветке
вернуть obj.length
}
// `obj` по-прежнему имеет тип `Any` за пределами ветки с проверкой типов
вернуть ноль
}
//конец выборки
веселая главная () {
весело printLength(объект: любой) {
println(«Получение длины ‘$obj’. Результат: ${getStringLength(obj) ?: «Ошибка: объект не является строкой»} «)
}
printLength(«Непонятности»)
длина печати(1000)
длина печати (список (любой ()))
}

или

// начало выборки
fun getStringLength(obj: Any): Int? {
if (obj !is String) возвращает null
// `obj` автоматически приводится к `String` в этой ветке
вернуть obj.length
}
//конец выборки
веселая главная () {
весело printLength(объект: любой) {
println(«Получение длины ‘$obj’. Результат: ${getStringLength(obj) ?: «Ошибка: объект не является строкой»} «)
}
printLength(«Непонятности»)
длина печати(1000)
длина печати (список (любой ()))
}

или даже

//начало выборки
fun getStringLength(obj: Any): Int? {
// `obj` автоматически преобразуется в `String` справа от `&&`
если (obj является строкой && obj.length > 0) {
вернуть obj.length
}
вернуть ноль
}
//конец выборки
веселая главная () {
весело printLength(объект: любой) {
println(«Получение длины ‘$obj’.

This entry was posted in Популярное