Kotlin
https://kotlinlang.org/docs/home.html |
https://kotlinlang.org/docs/basic-syntax.html |
https://kotlinlang.org/docs/properties.html |
09.03.2024 | https://youtu.be/62XBpj2hrQw |
20.01.2024 | https://youtu.be/9SHvJy5Sghg |
https://typealias.com/start/ |
Keywords
package
import
class
interface
fun
object
val
var
typealias
constructor
init
companion
override
abstract
final
open
enum
sealed
data
inner
tailrec
operator
infix
external
inline
noinline
crossinline
const
suspend
lateinit
if
else
when
try
catch
finally
for
do
while
throw
return
break
continue
public
private
protected
internal
is
in
as
out
reified
dynamic
null
true
false
annotation
actual
expect
this
super
by
get
set
Null Safety
https://kotlinlang.org/docs/null-safety.html |
Система предотвращения ошибок связанных с null. По умолчанию типы в Kotlin не могут быть null
, что делает код безопаснее. Null safety в Kotlin минимизирует риск возникновения ошибок NullPointerException
, делая код более надежным.
?.
Оператор безопасного вызова. Bспользуется для безопасного обращения к свойствам или методам nullable-объектов. Если объект равен null, дальнейшая цепочка вызовов будет проигнорирована.
val length = name?.length // Возвращает длину строки или null
?:
Оператор Элвиса. Позволяет указать значение по умолчанию, если переменная равна null
.
val length = name?.length ?: 0 // Если name == null, возвращается 0
!!
Оператор небезопасного приведения. Используется, когда уверены, что значение не null
. Но если оно окажется null
, выбросится исключение NullPointerException
.
val length = name!!.length // NullPointerException, если name == null
Packages and imports
https://kotlinlang.org/docs/packages.html |
package
Используется для организации кода в модули и пространственные области, чтобы избежать конфликтов имен и упростить управление кодом. Пакеты помогают структурировать проект и указывают, где находится файл. Пакет указывается в начале файла и должен соответствовать структуре папок, в которой расположен файл.
// Файл расположен в папке src/main/kotlin/com/example/myapp
package com.example.myapp
class MyClass {
fun greet() {
println("Hello from MyClass!")
}
}
fun main() {
val myClass = MyClass()
myClass.greet()
}
import
Используется для импорта классов, функций, объектов или других элементов из различных пакетов, чтобы иметь возможность использовать их в файле без необходимости указывать полный путь.
• Импорт класса
import java.util.Date
fun main() {
val currentDate = Date()
println(currentDate)
}
• Импорт функции
import kotlin.math.sqrt
fun main() {
val number = 16.0
val result = sqrt(number)
println("Square root of $number is $result")
}
• Импорт с использованием псевдонимов
import kotlin.collections.HashMap as MapAlias
fun main() {
val map = MapAlias<String, Int>()
map["one"] = 1
map["two"] = 2
println(map)
}
• Импорт всех элементов пакета
import kotlin.math.*
fun main() {
val number = 25.0
println("Square root: ${sqrt(number)}")
println("Cosine: ${cos(number)}")
}
typealias
https://kotlinlang.org/docs/type-aliases.html |
Позволяет создать псевдоним для существующего типа. Это полезно, когда нужно упростить длинные или сложные типы, сделать код более читаемым или создать понятный псевдоним для конкретного контекста. typealias
не создает новый тип, он просто указывает на уже существующий, и компилятор видит эти типы как идентичные. Поддерживают модификаторы доступа. Не могут быть вложенными и локальными.
• Упрощение сложного типа функции
typealias StringTransformer = (String) -> String
fun greet(transformer: StringTransformer) {
println(transformer("Hello, World!"))
}
fun main() {
val upperCaseTransformer: StringTransformer = { it.uppercase() }
greet(upperCaseTransformer) // Выведет: HELLO, WORLD!
}
• Сокращение сложного типа коллекции
typealias UserMap = Map<String, List<Int>>
fun main() {
val userScores: UserMap = mapOf(
"Alice" to listOf(85, 90, 92),
"Bob" to listOf(78, 80, 88)
)
println(userScores)
}
• Упрощение вложенных классов
class Outer {
class Inner
}
typealias InnerClass = Outer.Inner
fun main() {
val instance: InnerClass = InnerClass()
println(instance)
}
• Улучшение читаемости в коде с использованием специальных типов
typealias Meter = Double
typealias Kilogram = Double
fun calculateBMI(height: Meter, weight: Kilogram): Double {
return weight / (height * height)
}
fun main() {
val height: Meter = 1.75
val weight: Kilogram = 70.0
println("BMI: ${calculateBMI(height, weight)}") // Выведет: BMI: 22.857142857142858
}
• Псевдоним для интерфейса
interface ClickListener {
fun onClick()
}
typealias ButtonClickListener = ClickListener
class Button(private val listener: ButtonClickListener) {
fun click() {
listener.onClick()
}
}
fun main() {
val button = Button(object : ButtonClickListener {
override fun onClick() {
println("Button clicked!")
}
})
button.click() // Выведет: Button clicked!
}
abstract
Используется для объявления абстрактных классов и абстрактных методов (или свойств). Абстрактный класс или метод представляет собой незавершенную реализацию, которую должны дополнять или переопределять классы-наследники.
• Абстрактный класс и метод
abstract class Animal {
abstract fun makeSound()
fun eat() {
println("Eating food")
}
}
class Dog: Animal() {
override fun makeSound() {
println("Bark")
}
}
class Cat: Animal() {
override fun makeSound() {
println("Meow")
}
}
fun main() {
val dog = Dog()
dog.makeSound() // Выведет: Bark
dog.eat() // Выведет: Eating food
val cat = Cat()
cat.makeSound() // Выведет: Meow
cat.eat() // Выведет: Eating food
}
• Абстрактные свойства
abstract class Vehicle {
abstract val maxSpeed: Int
abstract fun startEngine()
fun showMaxSpeed() {
println("Max speed is $maxSpeed km/h")
}
}
class Car: Vehicle() {
override val maxSpeed = 220
override fun startEngine() {
println("Car engine started")
}
}
class Bike: Vehicle() {
override val maxSpeed = 180
override fun startEngine() {
println("Bike engine started")
}
}
fun main() {
val car = Car()
car.showMaxSpeed() // Выведет: Max speed is 220 km/h
car.startEngine() // Выведет: Car engine started
val bike = Bike()
bike.showMaxSpeed() // Выведет: Max speed is 180 km/h
bike.startEngine() // Выведет: Bike engine started
}
• Использование абстрактного класса в наследовании
abstract class Employee(val name: String) {
abstract fun calculateSalary(): Double
fun showDetails() {
println("Employee: $name, Salary: ${calculateSalary()}")
}
}
class Developer(
name: String,
private val hoursWorked: Int,
private val ratePerHour: Double
): Employee(name) {
override fun calculateSalary(): Double {
return hoursWorked * ratePerHour
}
}
class Manager(
name: String,
private val baseSalary: Double,
private val bonus: Double
): Employee(name) {
override fun calculateSalary(): Double {
return baseSalary + bonus
}
}
fun main() {
val dev = Developer("Alice", 160, 50.0)
dev.showDetails() // Выведет: Employee: Alice, Salary: 8000.0
val mgr = Manager("Bob", 5000.0, 2000.0)
mgr.showDetails() // Выведет: Employee: Bob, Salary: 7000.0
}
Modifiers
https://kotlinlang.org/docs/visibility-modifiers.html |
public
Указывает, что класс, функция, свойство или объект будут доступны везде в коде, без ограничений. В Kotlin public является модификатором по умолчанию, если явно не указан другой модификатор.
public class Person(val name: String) {
public fun greet() {
println("Hello, my name is $name")
}
}
fun main() {
val person = Person("Alice")
person.greet() // Выведет: Hello, my name is Alice
}
protected
Ограничивает доступ к элементам (свойствам, методам или конструкторам) только в пределах класса и его подклассов. Применяется только к классам и интерфейсам.
open class Animal {
protected fun makeSound() {
println("Animal sound")
}
}
class Dog: Animal() {
fun bark() {
makeSound() // Доступно, так как это подкласс
println("Dog barks")
}
}
fun main() {
val dog = Dog()
dog.bark() // Выведет: Animal sound, Dog barks
// dog.makeSound() // Ошибка: makeSound() недоступен вне класса и его подклассов
}
private
Используется для ограничения доступа к классам, свойствам, методам и конструкторам. Он делает элемент доступным только в пределах того файла или класса, в котором он объявлен.
class Person(private val name: String) {
private fun showName() {
println("Name is $name")
}
fun introduce() {
showName() // Можно вызвать внутри класса
}
}
fun main() {
val person = Person("Alice")
person.introduce() // Выведет: Name is Alice
// person.showName() // Ошибка: showName() недоступен вне класса
}
internal
Используется для ограничения доступа к элементам (классам, функциям, свойствам и конструкторам) в пределах одного модуля. Модуль — это набор файлов, которые компилируются вместе, например, проект, библиотека или единица сборки Gradle. В Java нет модификатора internal
, при компиляции класс станет общедоступным public
. Имена internal
-методов будут искажены, чтобы их было сложнее случайно вызвать.
• На класс и его методы
internal class Car(val model: String) {
internal fun drive() {
println("$model is driving")
}
}
fun main() {
val car = Car("Toyota")
car.drive() // Доступно, так как находится в том же модуле
}
• На уровне файла
internal fun calculateSum(a: Int, b: Int): Int {
return a + b
}
fun main() {
println(calculateSum(5, 10)) // Доступно, так как находится в том же модуле
}
• Конструктор
class User internal constructor(val name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main() {
val user = User("Alice") // Доступно, так как находится в том же модуле
user.greet()
}
open
Используется для того, чтобы разрешить классу, методу или свойству быть унаследованными или переопределенными. В Kotlin по умолчанию все классы и методы являются закрытыми (final
). Это сделано для повышения безопасности и предсказуемости кода.
• open
класс и наследование
open class Animal {
open fun sound() {
println("Some generic animal sound")
}
}
class Dog: Animal() {
override fun sound() {
println("Bark!")
}
}
fun main() {
val dog = Dog()
dog.sound() // Выведет: Bark!
}
• open
свойства
open class Car {
open val maxSpeed: Int = 180
open fun drive() {
println("Driving at speed: $maxSpeed")
}
}
class SportsCar: Car() {
override val maxSpeed: Int = 300
override fun drive() {
println("Driving at high speed: $maxSpeed")
}
}
fun main() {
val car = SportsCar()
car.drive() // Выведет: Driving at high speed: 300
}
• open
и protected
open class Vehicle {
protected open fun startEngine() {
println("Engine started")
}
}
class Truck: Vehicle() {
override fun startEngine() {
println("Truck engine started")
}
fun start() {
startEngine() // Доступен, так как он protected и переопределен
}
}
fun main() {
val truck = Truck()
truck.start() // Выведет: Truck engine started
}
final
Используется для того, чтобы запретить повторное наследование или переопределение класса, метода или свойства.
open class GrandPa {
open fun smoke() {
println("Дед курит сигары")
}
}
open class Dad: GrandPa() {
final override fun smoke() {
println("Батя унаследовал от деда привычку курить сигары, но запретил ее передачу сыну")
}
}
class Son: Dad() {
// Сын не может наследовать метод курить, потому что он final
}
Basic Types
Any
Базовый класс для всех типов (аналог Object
в Java). Все классы Kotlin по умолчанию наследуют Any
. Он включает 3 метода: equals()
, hashCode()
, и toString()
.
fun printInfo(obj: Any) {
println("Class: ${obj::class.simpleName}")
}
fun main() {
printInfo("Hello, Kotlin!")
printInfo(123)
}
Unit
Тип, который указывает, что функция не возвращает значимого значения (аналогичен void
в Java). Unit
является полноценным типом и может использоваться как значение. Функция с типом Unit
возвращает единственный объект Unit
.
fun printMessage(): Unit {
println("Hello, Kotlin!")
}
val printHello: () -> Unit = { println("Hello, World!") }
fun main() {
printHello()
}
Notning
Обозначает тип, который никогда не имеет значений и указывает на отсутствие завершения (например, выброс исключения). Обычно применяется, чтобы сообщить компилятору, что функция никогда не возвращается.
fun fail(): Nothing {
throw IllegalArgumentException("Ошибка")
}
fun main() {
fail() // Программа завершится с исключением
}
sealed class Result<out A: Any, out B: Any> {
data class Success<A: Any>(val value: A): Result<A, Nothing>()
data class Failure<B: Any>(val error: B): Result<Nothing, B>()
}
class
https://kotlinlang.org/docs/classes.html |
https://kotlinlang.org/docs/inheritance.html |
Ключево слово для определения класса в Kotlin. Классы содержат свойства и функции, а также поддерживают наследование и инкапсуляцию.
class Car(val make: String, var model: String) {
fun display() {
println("Car make: $make, model: $model")
}
}
val car = Car("Toyota", "Camry")
car.display() // Вывод: Car make: Toyota, model: Camry
constructor
Конструкция constructor
используется для объявления конструкторов классов. В языке есть два вида конструкторов: primary constructor (главный конструктор) и secondary constructors (вторичные конструкторы). Главный конструктор задается сразу при объявлении класса и является его основной точкой инициализации. Он может быть пустым или содержать параметры для передачи значений.
class Person(val name: String, var age: Int) {
init {
println("Создан объект с именем: $name и возрастом: $age")
}
}
• Можно задать видимость конструктора с помощью модификаторов доступа private
, protected
и internal
.
class Restricted private constructor(val name: String) {
// В этом классе никто не сможет создать экземпляр через обычный конструктор
}
• Можно задавать значения по умолчанию для параметров конструктора.
class Employee(val name: String, val position: String = "Инженер")
Secondary Constructor
Вторичные конструкторы используются, когда нужно предоставить дополнительные варианты инициализации объекта. Они определяются внутри тела класса с использованием ключевого слова constructor
.
• Может быть несколько вторичных конструкторов в одном классе.
• Вторичные конструкторы обязаны вызывать либо главный конструктор, либо другие вторичные конструкторы.
class Book {
var title: String
var author: String
// Главный конструктор
constructor(title: String) {
this.title = title
this.author = "Неизвестный автор"
}
// Вторичный конструктор, вызывает другой конструктор
constructor(title: String, author: String): this(title) {
this.author = author
}
}
class Animal(val species: String) {
var age: Int = 0
// Вторичный конструктор
constructor(species: String, age: Int): this(species) {
this.age = age
}
}
== vs ===
Оператор ==
сравнивает значения, а ===
ссылки на переменные.
val number = Integer(1)
val anotherNumber = Integer(1)
number == anotherNumber // true (structural equality)
number === anotherNumber // false (referential equality)
interface
https://kotlinlang.org/docs/interfaces.html |
https://kotlinlang.org/docs/fun-interfaces.html |
Используется для определения интерфейса. Интерфейс — это тип, который может содержать объявления абстрактных методов и свойств, а также предоставлять реализацию по умолчанию для методов.
• Базовый интерфейс с абстрактными методами
interface Animal {
fun makeSound() // Абстрактный метод
}
class Dog: Animal {
override fun makeSound() {
println("Woof!")
}
}
fun main() {
val dog = Dog()
dog.makeSound() // Выведет: Woof!
}
• Интерфейс с реализацией метода
interface Vehicle {
fun startEngine() {
println("Engine started")
}
fun stopEngine()
}
class Car: Vehicle {
override fun stopEngine() {
println("Engine stopped")
}
}
fun main() {
val car = Car()
car.startEngine() // Выведет: Engine started
car.stopEngine() // Выведет: Engine stopped
}
• Интерфейс со свойствами
interface Shape {
val name: String // Абстрактное свойство
fun area(): Double
}
class Circle(private val radius: Double) : Shape {
override val name: String
get() = "Circle"
override fun area(): Double = Math.PI * radius * radius
}
fun main() {
val circle = Circle(5.0)
println("Shape: ${circle.name}, Area: ${circle.area()}")
// Выведет: Shape: Circle, Area: 78.53981633974483
}
• Множественная реализация интерфейсов
interface Drivable {
fun drive()
}
interface Flyable {
fun fly()
}
class FlyingCar: Drivable, Flyable {
override fun drive() {
println("Driving on the road")
}
override fun fly() {
println("Flying in the sky")
}
}
fun main() {
val flyingCar = FlyingCar()
flyingCar.drive() // Выведет: Driving on the road
flyingCar.fly() // Выведет: Flying in the sky
}
• Разрешение конфликта при множественной реализации
interface Clickable {
fun click() = println("Clickable clicked")
}
interface Focusable {
fun click() = println("Focusable clicked")
}
class Button: Clickable, Focusable {
override fun click() {
super<Clickable>.click() // Указываем, какую реализацию использовать
super<Focusable>.click()
}
}
fun main() {
val button = Button()
button.click()
// Выведет:
// Clickable clicked
// Focusable clicked
}
fun interface
Cпециальный тип интерфейсов, предназначенный для функциональных интерфейсов (также известных как Single Abstract Method (SAM) интерфейсы). Это интерфейсы, которые содержат только один абстрактный метод, и они могут быть использованы для создания лямбд и сокращенного синтаксиса функциональных объектов.
fun interface Calculator {
fun calculate(a: Int, b: Int): Int
}
fun main() {
// Используем лямбду вместо создания объекта
val sum: Calculator = Calculator { a, b -> a + b }
println(sum.calculate(5, 3)) // Выведет: 8
val multiply: Calculator = Calculator { a, b -> a * b }
println(multiply.calculate(5, 3)) // Выведет: 15
}
init
Инициализационный блок, который выполняется каждый раз, когда создается объект класса. Он используется для выполнения дополнительных операций, которые нельзя поместить непосредственно в главный конструктор, таких как инициализация полей, валидация входных параметров или выполнение сторонних операций, которые требуют предварительного вызова.
• Запускается после того, как главный конструктор класса завершил инициализацию его параметров.
class Rectangle(val width: Int, val height: Int) {
val area: Int
init {
// Инициализация свойства площади
area = width * height
println("Создан прямоугольник с шириной $width и высотой $height. Площадь: $area")
}
}
• Может быть несколько блоков init
: если в классе объявлено несколько блоков init
, они выполняются в порядке их объявления сверху вниз.
class User(val name: String) {
init {
println("Первый блок инициализации: Привет, $name")
}
init {
println("Второй блок инициализации: Добро пожаловать!")
}
}
• Срабатывает перед вторичными конструкторами: даже если используются вторичные конструкторы, блоки init
будут вызваны до того, как произойдет выполнение кода в этих конструкторах.
class Book(val title: String) {
var author: String = "Неизвестный"
init {
println("Создана книга: $title")
}
constructor(title: String, author: String): this(title) {
this.author = author
println("Автор книги: $author")
}
}
val book1 = Book("Книга без автора")
// Вывод:
// Создана книга: Книга без автора
val book2 = Book("Книга с автором", "Иван Иванов")
// Вывод:
// Создана книга: Книга с автором
// Автор книги: Иван Иванов
• В случае наследования блоки init
также могут использоваться, но сначала будет выполнен блок init
базового класса, а затем блоки наследника.
open class Animal(val species: String) {
init {
println("Создано животное вида $species")
}
}
class Dog: Animal("Собака") {
init {
println("Создана собака")
}
}
val dog = Dog()
// Вывод:
// Создано животное вида Собака
// Создана собака
• Блок init
не запрещено использовать внутри object
.
object MySingleton {
val data: String
init {
data = "Initialized"
}
// Код инициализации можно написать напрямую
val anotherData = "Direct Initialization"
}
enum
https://kotlinlang.org/docs/enum-classes.html |
Cпециальный тип класса, который используется для представления набора фиксированных значений.
• Пустой enum
-класс
enum class EmptyEnum
• enum
-класс с фиксированными значениями
enum class Direction {
NORTH, EAST, SOUTH, WEST
}
• enum
-класс с целочисленными значениями
enum class Month(val number: Int) {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12)
}
• enum
-класс со строковыми значениями
enum class Color(val hexCode: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
• enum
-класс с несколькими параметрами
enum class Planet(val mass: Double, val radius: Double) {
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6),
MARS(6.421e+23, 3.3972e6)
}
• enum
-класс с методом
enum class TrafficLight(val duration: Int) {
RED(30),
YELLOW(5),
GREEN(25);
fun printDuration() {
println("The duration of $name is $duration seconds")
}
}
fun main() {
val light = TrafficLight.RED
light.printDuration() // Выведет: The duration of RED is 30 seconds
}
• enum
-класс с переопределенными методами
enum class Operation {
ADD {
override fun apply(x: Int, y: Int) = x + y
},
SUBTRACT {
override fun apply(x: Int, y: Int) = x - y
},
MULTIPLY {
override fun apply(x: Int, y: Int) = x * y
},
DIVIDE {
override fun apply(x: Int, y: Int) = x / y
};
abstract fun apply(x: Int, y: Int): Int
}
fun main() {
val result = Operation.ADD.apply(5, 3)
println(result) // Выведет: 8
}
• enum
-класс, реализующий интерфейс
interface Drawable {
fun draw()
}
enum class Shape: Drawable {
CIRCLE {
override fun draw() {
println("Drawing a circle")
}
},
SQUARE {
override fun draw() {
println("Drawing a square")
}
},
TRIANGLE {
override fun draw() {
println("Drawing a triangle")
}
}
}
fun main() {
val shape = Shape.CIRCLE
shape.draw() // Выведет: Drawing a circle
}
• enum
-класс с вложенными объектами (анонимные классы)
enum class Animal {
DOG {
override fun sound() = "Bark"
},
CAT {
override fun sound() = "Meow"
},
COW {
override fun sound() = "Moo"
};
abstract fun sound(): String
}
fun main() {
val dogSound = Animal.DOG.sound()
println(dogSound) // Выведет: Bark
}
• enum
-класс с лямбдами
enum class Operation(val perform: (Int, Int) -> Int) {
ADD({ a, b -> a + b }),
SUBTRACT({ a, b -> a - b }),
MULTIPLY({ a, b -> a * b }),
DIVIDE({ a, b -> a / b });
fun apply(x: Int, y: Int): Int = perform(x, y)
}
fun main() {
val result = Operation.ADD.apply(10, 5)
println(result) // Выведет: 15
val multiplication = Operation.MULTIPLY.apply(3, 4)
println(multiplication) // Выведет: 12
}
• enum
-класс с объектами другого enum
-класса
enum class Size {
SMALL, MEDIUM, LARGE
}
enum class Drink(val size: Size) {
COFFEE(Size.MEDIUM),
TEA(Size.SMALL),
SODA(Size.LARGE);
}
fun main() {
val coffeeSize = Drink.COFFEE.size
println("The size of coffee is: $coffeeSize") // Выведет: The size of coffee is: MEDIUM
}
• enum
-класс с другим классом
open class Animal(val sound: String) {
fun makeSound() = println("This animal makes a $sound sound.")
}
enum class ZooAnimal(val animal: Animal) {
LION(Animal("roar")),
CAT(Animal("meow")),
DOG(Animal("bark"));
fun showSound() {
animal.makeSound()
}
}
fun main() {
val lion = ZooAnimal.LION
lion.showSound() // Выведет: This animal makes a roar sound.
}
• enum
-класс c data
-классом
data class Car(val brand: String, val model: String, val year: Int)
enum class CarType(val car: Car) {
SEDAN(Car("Toyota", "Camry", 2020)),
SUV(Car("Ford", "Explorer", 2021)),
TRUCK(Car("Chevrolet", "Silverado", 2019));
fun printCarInfo() {
println("Car Info: ${car.brand} ${car.model}, Year: ${car.year}")
}
}
fun main() {
val sedan = CarType.SEDAN
sedan.printCarInfo() // Выведет: Car Info: Toyota Camry, Year: 2020
}
• enum
-класс c sealed
-интерфейсом
sealed interface AnimalSound {
fun makeSound(): String
data object Bark : AnimalSound {
override fun makeSound() = "Bark"
}
data object Meow : AnimalSound {
override fun makeSound() = "Meow"
}
data object Moo : AnimalSound {
override fun makeSound() = "Moo"
}
}
enum class Animal(val sound: AnimalSound) {
DOG(AnimalSound.Bark),
CAT(AnimalSound.Meow),
COW(AnimalSound.Moo);
fun showSound() {
println("The animal makes a sound: ${sound.makeSound()}")
}
}
fun main() {
val dog = Animal.DOG
dog.showSound() // Выведет: The animal makes a sound: Bark
val cat = Animal.CAT
cat.showSound() // Выведет: The animal makes a sound: Meow
val cow = Animal.COW
cow.showSound() // Выведет: The animal makes a sound: Moo
}
• enum
-класс c sealed
-классом
sealed class TransportMode(val speed: Int) {
data object Car : TransportMode(120)
data object Bicycle : TransportMode(20)
data object Train : TransportMode(180)
}
enum class Transport(val mode: TransportMode) {
CAR(TransportMode.Car),
BICYCLE(TransportMode.Bicycle),
TRAIN(TransportMode.Train);
fun describe() {
when (mode) {
is TransportMode.Car -> println("Car can go up to ${mode.speed} km/h")
is TransportMode.Bicycle -> println("Bicycle can go up to ${mode.speed} km/h")
is TransportMode.Train -> println("Train can go up to ${mode.speed} km/h")
}
}
}
fun main() {
val car = Transport.CAR
car.describe() // Выведет: Car can go up to 120 km/h
val bicycle = Transport.BICYCLE
bicycle.describe() // Выведет: Bicycle can go up to 20 km/h
}
name
Возвращает имя элемента enum
-класса в виде строки.
enum class Color {
RED, GREEN, BLUE
}
fun main() {
val color = Color.RED
println(color.name) // Выведет: RED
}
ordinal
Возвращает порядковый номер (индекс) элемента в enum
-классе.
enum class Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
fun main() {
val day = Day.WEDNESDAY
println("The ordinal of $day is ${day.ordinal}") // Выведет: The ordinal of WEDNESDAY is 2
}
entries
Возвращает список (List
) всех элементов enum
-класса.
enum class Direction {
NORTH, EAST, SOUTH, WEST
}
fun main() {
val directions: List<Direction> = Direction.entries
directions.forEach { direction ->
println(direction) // listOf(NORTH, EAST, SOUTH, WEST)
}
}
values()
Возвращает массив (Array
) всех элементов enum
-класса. Метод устарел, рекомендуется заменить на Enum.entries
начиная с версии Kotlin 1.9.
enum class Direction {
NORTH, EAST, SOUTH, WEST
}
fun main() {
val directions: Array<Direction> = Direction.values()
directions.forEach { direction ->
println(direction) // [NORTH, EAST, SOUTH, WEST]
}
}
Variables
https://kotlinlang.org/docs/properties.html |
val
Используется для объявления неизменяемых переменных, которые нельзя изменить после инициализации.
• Переменные, объявленные с помощью val
, инициализируются значением, и это значение не может быть изменено в дальнейшем.
• Тип переменной определяется автоматически на основе присвоенного значения, но также может быть указан явно.
• val
требует немедленной инициализации при объявлении или инициализации в блоке инициализации.
• val
может быть объявлен на уровне класса, функции или блока, что определяет его область видимости.
val pi = 3.14
val name: String = "Alice"
class Example {
val a: Int
init {
a = 0
}
}
var
Используется для объявления изменяемых переменных, которые могут изменять свое значение в процессе выполнения программы.
• Переменные, объявленные с помощью var
, могут быть инициализированы значением и изменены позже.
• Тип переменной определяется автоматически на основе присвоенного значения или может быть указан явно.
• var
может быть объявлен на уровне класса, функции или блока, определяя его область видимости.
var count = 0
count += 1 // Теперь count равно 1
var name: String = "Alice"
const
Используется для определения компилируемого константного значения. Позволяет создать константы, которые будут доступны в любом месте вашего кода, улучшая его качество и производительность.
• Можно использовать только с переменными, которые имеют тип String
или примитивные типы.
• Значения, объявленные с помощью const
, должны быть известны на этапе компиляции и не могут быть изменены. Они не могут быть инициализированы с помощью выражений, которые зависят от выполнения кода.
• Делает код более читаемым и оптимизированным, так как компилятор заменяет все вхождения const
переменной ее значением на этапе компиляции.
• Переменные должны быть объявлены на верхнем уровне файла или в именованном объекте или в companion object
. Их нельзя объявить внутри функции или класса напрямую.
const val CONST_ON_TOP_LEVEL = "Name"
object MyObject {
const val CONST_IN_NAMED_OBJECT = 10
}
class MyClass {
companion object {
const val CONST_IN_COMPANION_OBJECT = 'A'
}
}
vararg
Механизм, позволяющий передавать переменное количество аргументов в функцию. Он упрощает написание функций, которые могут принимать не фиксированное количество параметров одного типа. Вместо того чтобы определять множество перегруженных методов, вы можете использовать vararg
для обработки нескольких значений.
• В одной функции может быть только один параметр, объявленный как vararg
.
• Параметр vararg
должен быть последним в списке параметров функции.
• Можно комбинировать vararg
с другими параметрами, но vararg
всегда должен быть в конце.
fun printNumbers(vararg numbers: Int) {
for (number in numbers) {
println(number)
}
}
printNumbers(1, 2, 3, 4, 5) // Передача отдельных значений
val nums = intArrayOf(6, 7, 8)
printNumbers(*nums) // Передача массива
lateinit
Модификатор, который используется для объявления переменных, которые будут инициализированы позже, но перед использованием. Он позволяет избежать необходимости присваивать значение переменной в момент её объявления, что особенно полезно для не-nullable переменных, которые не могут иметь значение по умолчанию.
• lateinit
можно использовать только с переменными, которые являются изменяемыми (var
).
• lateinit
не могут иметь тип, основанный на примитивных типах (например, Int
Long
Float
Double
Boolean
Char
).
• lateinit
применим только к переменным класса, а не к локальным переменным в методах.
• Если попытаться получить доступ к lateinit-переменной до инициализации, будет выброшено исключение UninitializedPropertyAccessException
.
class Example {
lateinit var name: String
}
fun main() {
val example = Example()
example.name = "Kotlin" // Инициализация
println(example.name) // Вывод: Kotlin
}
fun main() {
val example = Example()
if (::example.name.isInitialized) {
println(example.name)
} else {
println("name не инициализирована")
}
}
Functions
https://kotlinlang.org/docs/functions.html |
fun
Используется для объявления функций.
fun sum(a: Int, b: Int): Int {
return a + b
}
• Функции поддерживают значения по умолчанию.
fun greet(name: String = "Гость") {
println("Привет, $name!")
}
// Вызывается как greet() или greet("Иван")
• Функции поддерживают переменное количество аргументов.
fun printNumbers(vararg numbers: Int) {
for (number in numbers) {
println(number)
}
}
// Пример вызова: printNumbers(1, 2, 3, 4)
tailrec
Используется для оптимизации хвостовой рекурсии. Если функция является хвосторекурсивной (рекурсивный вызов является последней операцией в функции), компилятор может преобразовать её в цикл, что предотвращает переполнение стека вызовов. Это позволяет использовать рекурсию в ситуациях, где обычно использовался бы цикл, сохраняя читаемость кода.
// Здесь функция factorial вычисляет факториал числа.
// Благодаря ключевому слову tailrec, компилятор оптимизирует рекурсивные вызовы в цикл, что предотвращает переполнение стека при больших значениях n.
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n <= 1) acc else factorial(n - 1, acc * n)
}
fun main() {
println(factorial(5)) // Вывод: 120
}
operator
https://kotlinlang.org/docs/operator-overloading.html |
Используется для перегрузки операторов. Оно позволяет разработчикам определять, как стандартные операторы (например, +, -, *, ==, [], и т. д.) должны вести себя при применении к объектам определенного класса. Это делает код более читаемым и интуитивно понятным, позволяя использовать синтаксис, похожий на встроенные типы данных, для своих собственных классов.
• Перегрузка оператора сложения +
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main() {
val p1 = Point(3, 4)
val p2 = Point(1, 2)
val p3 = p1 + p2 // Используется перегруженный оператор +
println(p3) // Выведет: Point(x=4, y=6)
}
• Перегрузка оператора вычитания -
data class Point(val x: Int, val y: Int) {
operator fun minus(other: Point): Point {
return Point(x - other.x, y - other.y)
}
}
fun main() {
val p1 = Point(5, 7)
val p2 = Point(2, 3)
println(p1 - p2) // Выведет: Point(x=3, y=4)
}
• Перегрузка оператора умножения *
data class Vector(val x: Int, val y: Int) {
operator fun times(scale: Int): Vector {
return Vector(x * scale, y * scale)
}
}
fun main() {
val v = Vector(2, 3)
val scaled = v * 5 // Используется перегруженный оператор *
println(scaled) // Выведет: Vector(x=10, y=15)
}
• Перегрузка оператора деления /
data class Fraction(val numerator: Int, val denominator: Int) {
operator fun div(other: Fraction): Fraction {
return Fraction(
numerator * other.denominator,
denominator * other.numerator
)
}
}
fun main() {
val f1 = Fraction(3, 4)
val f2 = Fraction(2, 5)
println(f1 / f2) // Выведет: Fraction(numerator=15, denominator=8)
}
• Перегрузка оператора остатка от деления %
data class ModNumber(val value: Int) {
operator fun rem(other: Int): Int {
return value % other
}
}
fun main() {
val num = ModNumber(10)
println(num % 3) // Выведет: 1
}
• Перегрузка операторов равенства ==
≠
data class Person(val name: String, val age: Int) {
operator fun equals(other: Person): Boolean {
return this.name == other.name && this.age == other.age
}
}
fun main() {
val person1 = Person("Alice", 25)
val person2 = Person("Alice", 25)
println(person1 == person2) // Выведет: true
println(person1 != person2) // Выведет: false
}
• Перегрузка операторов сравнения >
<
>=
<=
data class Box(val weight: Int) : Comparable<Box> {
override operator fun compareTo(other: Box): Int {
return this.weight - other.weight
}
}
fun main() {
val box1 = Box(5)
val box2 = Box(10)
println(box1 < box2) // Выведет: true
println(box1 > box2) // Выведет: false
println(box1 <= box2) // Выведет: true
println(box1 >= box2) // Выведет: false
}
• Перегрузка операторов инкремента и декремента ++
--
data class Counter(var value: Int) {
operator fun inc(): Counter {
value++
return this
}
operator fun dec(): Counter {
value--
return this
}
}
fun main() {
var counter = Counter(5)
counter++
println(counter.value) // Выведет: 6
counter--
println(counter.value) // Выведет: 5
}
• Перегрузка оператора логического отрицания !
data class Light(val isOn: Boolean) {
operator fun not() = Light(!isOn)
}
fun main() {
val light = Light(true)
println(!light) // Выведет: Light(isOn=false)
}
• Перегрузка оператора доступа к элементу get
class Matrix(private val data: Array<Array<Int>>) {
operator fun get(row: Int, col: Int): Int {
return data[row][col]
}
}
fun main() {
val matrix = Matrix(arrayOf(arrayOf(1, 2), arrayOf(3, 4)))
println(matrix[0, 1]) // Используется перегруженный оператор []
// Выведет: 2
}
• Перегрузка оператора установки значения set
class MutableListWrapper<T>(private val list: MutableList<T>) {
operator fun set(index: Int, value: T) {
list[index] = value
}
override fun toString(): String {
return list.toString()
}
}
fun main() {
val list = MutableListWrapper(mutableListOf(1, 2, 3))
list[1] = 10
println(list) // Выведет: [1, 10, 3]
}
• Перегрузка оператора вызова функции ()
class Greeter(val greeting: String) {
operator fun invoke(name: String) {
println("$greeting, $name!")
}
}
fun main() {
val greeter = Greeter("Hello")
greeter("Kotlin") // Выведет: Hello, Kotlin!
}
it
Имя параметра по умолчанию для одиночного параметра в лямбда-выражении. Когда лямбда принимает только один аргумент, его можно не называть явно, и тогда к нему можно обращаться через it
.
val numbers = listOf(1, 2, 3, 4, 5)
val doubledNumbers = numbers.map { it * 2 }
println(doubledNumbers) // Выведет: [2, 4, 6, 8, 10]
::
Оператор ссылки на функцию (function reference). Он позволяет передавать функции или свойства в качестве параметров другим функциям или сохранять их в переменные. Этот оператор может ссылаться как на функции, так и на свойства класса.
• Ссылка на функцию
fun greet() {
println("Hello!")
}
val greetFunction = ::greet
greetFunction() // Вызовет функцию greet, результат: "Hello!"
• Ссылка на методы класса
class Greeter {
fun sayHello() {
println("Hello from the Greeter!")
}
}
val greeter = Greeter()
val helloFunction = greeter::sayHello
helloFunction() // Выведет: "Hello from the Greeter!"
• Ссылка на конструктор
class Person(val name: String, val age: Int)
val createPerson = ::Person
val person = createPerson("John", 25)
println(person.name) // Выведет: "John"
• Ссылка на свойства
val x = 42
val propertyRef = ::x
println(propertyRef.get()) // Выведет: 42
• Использование в лямбдах
val numbers = listOf(1, 2, 3, 4, 5)
val squares = numbers.map(Int::toString)
println(squares) // Выведет: ["1", "2", "3", "4", "5"]
• C функциями расширениями:
fun String.printWithExclamation() {
println(this + "!")
}
val printExclaim = String::printWithExclamation
printExclaim("Hello") // Выведет: "Hello!"
Local Functions
Локальные функции — это функции, определенные внутри другой функции. Они могут быть вызваны только изнутри той функции, где они объявлены. Локальные функции полезны для структурирования кода и уменьшения дублирования, а также для повышения его читабельности.
fun main() {
fun outerFunction(number: Int): Int {
// Локальная функция
fun double(x: Int) = x * 2
// Использование локальной функции
return double(number) + double(number)
}
val result = outerFunction(5)
println(result) // Выведет: 20
}
High Order Functions
https://kotlinlang.org/docs/lambdas.html |
Функция высшего порядка — это функция, которая принимает другую функцию в качестве аргумента или возвращает функцию в качестве результата.
// Эта функция принимает значение value и функцию func, которая преобразует value.
// Она применяет функцию дважды и возвращает результат.
fun <T> applyTwice(value: T, func: (T) -> T): T {
return func(func(value))
}
// Функция createMultiplier возвращает функцию, которая умножает входное число на заданный коэффициент.
fun createMultiplier(factor: Int): (Int) -> Int {
return { number -> number * factor }
}
Anonymous Functions
Анонимная функция — это функция без имени, которую можно объявить и использовать в месте, где она нужна. В Kotlin они могут быть созданы с помощью ключевого слова fun
. Обычно используются для кратковременных операций и передаются как аргументы в другие функции. Это похоже на лямбда-выражения, но с явным определением типов и тела функции.
val sum = fun(a: Int, b: Int): Int {
return a + b
}
super
Используется для вызова методов или свойств родительского класса или интерфейса из класса-потомка.
• Вызов метода суперкласса
open class Animal {
open fun makeSound() {
println("Animal makes a sound")
}
}
class Dog: Animal() {
override fun makeSound() {
super.makeSound() // Вызов метода суперкласса Animal
println("Dog barks")
}
}
fun main() {
val dog = Dog()
dog.makeSound()
// Выведет:
// Animal makes a sound
// Dog barks
}
• Доступ к свойству суперкласса
open class Person {
open val name: String = "Unknown"
}
class Employee: Person() {
override val name: String = "John Doe"
fun printNames() {
println("Employee name: $name")
println("Person name: ${super.name}")
}
}
fun main() {
val employee = Employee()
employee.printNames()
// Выведет:
// Employee name: John Doe
// Person name: Unknown
}
• Вызов метода из конкретного интерфейса
interface A {
fun show() {
println("Interface A")
}
}
interface B {
fun show() {
println("Interface B")
}
}
class C: A, B {
override fun show() {
super<A>.show() // Вызов метода из интерфейса A
super<B>.show() // Вызов метода из интерфейса B
println("Class C")
}
}
fun main() {
val c = C()
c.show()
// Выведет:
// Interface A
// Interface B
// Class C
}
if
- else
Используется как оператор для выполнения условного кода и как выражение, которое может возвращать значение.
• if
в Kotlin всегда должен иметь ветку else
, если используется как выражение.
• Он может быть многострочным, если нужно выполнить несколько инструкций в каждой ветке.
// Пример как оператора:
val x = 10
if (x > 5) {
println("x больше 5")
} else {
println("x меньше или равно 5")
}
// Пример как выражения:
val max = if (a > b) a else b
Loops
while
Выполняет блок кода, пока условие истинно.
var i = 0
while (i < 5) {
println(i)
i++
}
do
- while
Cначала выполняет блок кода, а затем проверяет условие. Он гарантирует хотя бы одно выполнение.
var i = 0
do {
println(i)
i++
} while (i < 5)
for
Используется для итерации по коллекциям, диапазонам и последовательностям.
• Итерация по диапазону. Используется оператор in
. Итерация проходит от начального до конечного значения (включительно):
for (i in 1..5) {
println(i) // Выведет: 1, 2, 3, 4, 5
}
• until
— итерация до верхней границы (исключительно):
for (i in 1 until 5) {
println(i) // Выведет: 1, 2, 3, 4 (5 не включено)
}
• downTo
— итерация в обратном порядке:
for (i in 5 downTo 1) {
println(i) // Выведет: 5, 4, 3, 2, 1
}
• step
— позволяет задать шаг для увеличения или уменьшения значений:
for (i in 1..10 step 2) {
println(i) // Выведет: 1, 3, 5, 7, 9
}
for (i in 10 downTo 1 step 3) {
println(i) // Выведет: 10, 7, 4, 1
}
• Итерация по коллекции. Итерация по элементам коллекции (например, список или массив):
val items = listOf("a", "b", "c")
for (item in items) {
println(item) // Выведет: a, b, c
}
• Итерация по индексам коллекции. Можно итерироваться по индексам коллекции с использованием .indices
:
val items = listOf("a", "b", "c")
for (i in items.indices) {
println("Index $i contains ${items[i]}")
}
• Итерация по паре индекс-значение. Используя функцию .withIndex()
, можно получать одновременно и индекс, и элемент:
val items = listOf("a", "b", "c")
for ((index, value) in items.withIndex()) {
println("Item at index $index is $value")
}
break
Досрочно завершает выполнение цикла. После его вызова цикл немедленно прерывается.
val items = listOf("a", "b", "c", "d")
for (item in items) {
if (item == "c") break // Прерываем цикл, когда находим "c"
println(item) // выведет: a, b
}
continue
Пропускает оставшуюся часть текущей итерации и переходит к следующей.
val items = listOf("a", "b", "c", "d")
for (item in items) {
if (item == "c") continue // Пропускаем "c" и продолжаем с "d"
println(item) // выведет: a, b, d
}
Labels
Позволяют управлять потоком выполнения кода, особенно когда нужно прервать цикл, вернуть значение или продолжить выполнение из вложенного блока. Создаются с помощью символа @
, и их можно использовать вместе с return
, break
, и continue
для указания конкретного уровня блока или лямбды, к которому нужно вернуться.
break@label
Позволяет прервать вложенный цикл, чтобы выйти сразу из нескольких уровней. break@outerLoop
прерывает выполнение не только внутреннего, но и внешнего цикла, помеченного outerLoop@
.
fun findNumber(matrix: List<List<Int>>, target: Int) {
outerLoop@ for (row in matrix) { // Лейбл для внешнего цикла
for (cell in row) {
if (cell == target) {
println("Found $target")
break@outerLoop // Выход из внешнего цикла
}
}
}
println("Search complete")
}
continue@label
Пропускает текущую итерацию определённого цикла, полезно при работе с вложенными циклами. Если cell == 0
, continue@outerLoop
пропустит текущую итерацию внешнего цикла outerLoop
, переходя сразу к следующей строке matrix
.
fun processMatrix(matrix: List<List<Int>>) {
outerLoop@ for (row in matrix) { // Лейбл для внешнего цикла
for (cell in row) {
if (cell == 0) continue@outerLoop // Переход к следующей итерации внешнего цикла
println(cell)
}
}
}
return@label
return
в лямбде по умолчанию используется для завершения выполнения всей функции, содержащей эту лямбду. Чтобы завершить только саму лямбду, используют лейбл. Например, return@forEach
завершает только текущую итерацию лямбды forEach
, не прерывая выполнение всей функции processNumbers
.
fun processNumbers(numbers: List<Int>) {
numbers.forEach {
if (it == 0) return@forEach // Завершает только текущую итерацию в лямбде
println(it)
}
println("Completed processing")
}
Types
https://kotlinlang.org/docs/arrays.html |
В Kotlin нет примитивных типов данных, все является объектами. Byte
Short
Int
Double
Char
Float
Long
Boolean
- все это объекты, наследуются от Any
и переопределяют его методы.
val a: Byte = -10
val b: Short = 45
val c: Int = -250
val d: Long = 30000L
val a: UByte = 10U
val b: UShort = 45U
val c: UInt = 250U
val d: ULong = 30000U
val a: Int = 0x0A1 // 161
val a: Int = 0b0101 // 5
val b: Int = 0b1011 // 11
val height: Double = 1.78
val pi: Float = 3.14F
val d: Double = 23e3 // 23 000
val g: Double = 23e-3 // 0.023
val a: Boolean = true
val b: Boolean = false
val a: Char = 'A'
val b: Char = 'B'
val c: Char = 'T'
val t: Char = '\t' // табуляция
val n: Char = '\n' // перевод строки
val r: Char = '\r' // возврат каретки
val a: Char = '\'' // одинарная кавычка
val b: Char = '\"' // двойная кавычка
val c: Char = '\\' // обратный слеш
val name: String = "Michael"
val name: String = """Michael"""
val range: IntRange = 1..5 // [1, 2, 3, 4, 5]
val range: CharRange = 'a'..'d'
val range: LongRange = 0L..1L
val range: ClosedRange<String> = "a".."d"
val range: IntProgression = 5 downTo 1 // 5 4 3 2 1
val range: IntProgression = 1..10 step 2 // 1 3 5 7 9
val range: IntProgression = 10 downTo 1 step 3 // 10 7 4 1
val range: IntProgression = 1 until 9 // 1 2 3 4 5 6 7 8
val range: IntProgression = 1 until 9 step 2 // 1 3 5 7
val isInRange: Boolean = 5 in 1..5
val isNotInRange: Boolean = 5 !in 1..5
val numbers: Array<Int> = arrayOf(1, 2, 3, 4, 5)
val numbers = Array(3) { 5 } // [5, 5, 5]
var i = 1
val numbers = Array(3) { i++ * 2 } // [2, 4, 6]
val numbers: IntArray = intArrayOf(1, 2, 3, 4, 5)
val doubles: DoubleArray = doubleArrayOf(2.4, 4.5, 1.2)
val numbers: IntArray = IntArray(3) { 5 }
val doubles: DoubleArray = DoubleArray(3) { 1.5 }
val table: Array<Array<Int>> = Array(3) { Array(5) { 0 } } // двумерный массив
fun changeNumbers(vararg values: Int, koef: Int) {}
val numbers: IntArray = intArrayOf(1, 2, 3, 4)
changeNumbers(*numbers, koef = 2)
Integer types
https://kotlinlang.org/docs/numbers.html |
Type | Size | Min value | Max value |
---|---|---|---|
Byte | 8 bits (1 byte) | -128 | 127 |
Short | 16 bits (2 byte) | -32 768 | 32 767 |
Int | 32 bits (4 byte) | -2 147 483 648 (-2 в 31 степени) | 2 147 483 647 (2 в 31 степени - 1) |
Long | 64 bits (8 byte) | -9 223 372 036 854 775 808 (-2 в 63) | 9 223 372 036 854 775 807 (2 в 63 - 1) |
Unsigned Integer types
https://kotlinlang.org/docs/unsigned-integer-types.html |
Type | Size | Min value | Max value |
---|---|---|---|
UByte | 8 bits (1 byte) | 0 | 255 |
UShort | 16 bits (2 byte) | 0 | 65 535 |
UInt | 32 bits (4 byte) | 0 | 4 294 967 295 (2 в 32 - 1) |
ULong | 64 bits (8 byte) | 0 | 18 446 744 073 709 551 615 (2 в 64 - 1) |
Floating-point types
Type | Size | Decimal digits |
---|---|---|
Float | 32 bits (4 byte) | 6-7 |
Double | 64 bits (8 byte) | 15-16 |
Boolean
https://kotlinlang.org/docs/booleans.html |
Тип данных для представления логических значений, который может содержать одно из двух значений: true
или false
. Boolean
используется в условных операторах, циклах и для хранения результатов логических выражений.
val isAdult: Boolean = true
Char
https://kotlinlang.org/docs/characters.html |
Тип данных, представляющий символ. Символ — это отдельный элемент текста, который может быть буквой, цифрой, специальным символом или любым другим символом из набора Unicode. В отличие от строк, Char
представляет один символ и обозначается в коде одиночными кавычками.
val letter: Char = 'A'
val smiley: Char = '\u263A' // Unicode для ☺
code
Возвращает числовое значение (код) символа.
val char: Char = 'A'
val charCode: Int = char.code // Преобразование Char в Int
println("Code of '$char': $charCode") // Выведет: Code of 'A': 65
toChar
Преобразует число обратно в символ.
val newChar: Char = charCode.toChar() // Преобразование Int в Char
println("Char from code $charCode: $newChar") // Выведет: Char from code 65: A
isDigit
Проверяет, является ли символ цифрой (0-9).
fun main() {
val char1: Char = '5'
val char2: Char = 'a'
println("Is '$char1' a digit? ${char1.isDigit()}") // Выведет: Is '5' a digit? true
println("Is '$char2' a digit? ${char2.isDigit()}") // Выведет: Is 'a' a digit? false
}
isLetter
Проверяет, является ли символ буквой (любая буква алфавита, включая латиницу и другие символы Unicode).
fun main() {
val char1: Char = 'K'
val char2: Char = '3'
println("Is '$char1' a letter? ${char1.isLetter()}") // Выведет: Is 'K' a letter? true
println("Is '$char2' a letter? ${char2.isLetter()}") // Выведет: Is '3' a letter? false
}
isWhitespace
Проверяет, является ли символ пробелом или другим разделителем (например, пробел, табуляция, перевод строки и другие символы-разделители).
fun main() {
val char1: Char = ' '
val char2: Char = '\n'
val char3: Char = 'a'
println("Is ' ' a whitespace? ${char1.isWhitespace()}") // Выведет: Is ' ' a whitespace? true
println("Is '\\n' a whitespace? ${char2.isWhitespace()}") // Выведет: Is '\n' a whitespace? true
println("Is 'a' a whitespace? ${char3.isWhitespace()}") // Выведет: Is 'a' a whitespace? false
}
isUpperCase
Проверяет, является ли символ заглавной буквой.
fun main() {
val char1: Char = 'G'
val char2: Char = 'g'
println("Is '$char1' uppercase? ${char1.isUpperCase()}") // Выведет: Is 'G' uppercase? true
println("Is '$char2' uppercase? ${char2.isUpperCase()}") // Выведет: Is 'g' uppercase? false
}
isLowerCase
Проверяет, является ли символ строчной буквой.
fun main() {
val char1: Char = 'm'
val char2: Char = 'M'
println("Is '$char1' lowercase? ${char1.isLowerCase()}") // Выведет: Is 'm' lowercase? true
println("Is '$char2' lowercase? ${char2.isLowerCase()}") // Выведет: Is 'M' lowercase? false
}
Таблица Unicode
Символ | Unicode | Описание | Пример на Kotlin |
---|---|---|---|
' ' | '\u0020' | Пробел (Space) | val char = '\\u0020' |
'\\n' | '\u000A' | Перенос строки (Line Feed) | val char = '\\u000A' |
'\\r' | '\u000D' | Возврат каретки (Carriage Return) | val char = '\\u000D' |
'\\t' | '\u0009' | Табуляция (Tab) | val char = '\\u0009' |
',' | '\u002C' | Запятая (Comma) | val char = '\\u002C' |
'.' | '\u002E' | Точка (Period) | val char = '\\u002E' |
';' | '\u003B' | Точка с запятой (Semicolon) | val char = '\\u003B' |
':' | '\u003A' | Двоеточие (Colon) | val char = '\\u003A' |
'!' | '\u0021' | Восклицательный знак (Exclamation Mark) | val char = '\\u0021' |
'?' | '\u003F' | Вопросительный знак (Question Mark) | val char = '\\u003F' |
'"' | '\u0022' | Кавычка (Double Quote) | val char = '\\u0022' |
'\\'' | '\u0027' | Одинарная кавычка (Single Quote) | val char = '\\u0027' |
'-' | '\u002D' | Дефис (Hyphen) | val char = '\\u002D' |
'_' | '\u005F' | Подчеркивание (Underscore) | val char = '\\u005F' |
'(' | '\u0028' | Открывающая скобка (Left Parenthesis) | val char = '\\u0028' |
')' | '\u0029' | Закрывающая скобка (Right Parenthesis) | val char = '\\u0029' |
'[' | '\u005B' | Открывающая квадратная скобка (Left Square Bracket) | val char = '\\u005B' |
']' | '\u005D' | Закрывающая квадратная скобка (Right Square Bracket) | val char = '\\u005D' |
'{' | '\u007B' | Открывающая фигурная скобка (Left Curly Brace) | val char = '\\u007B' |
'}' | '\u007D' | Закрывающая фигурная скобка (Right Curly Brace) | val char = '\\u007D' |
'☺' | '\u263A' | Улыбка (Smiley Face) | val char = '\\u263A' |
'©' | '\u00A9' | Знак авторского права (Copyright Symbol) | val char = '\\u00A9' |
'®' | '\u00AE' | Знак зарегистрированной торговой марки (Registered Trademark) | val char = '\\u00AE' |
'✓' | '\u2713' | Галочка (Check Mark) | val char = '\\u2713' |
'✗' | '\u2717' | Крестик (Cross Mark) | val char = '\\u2717' |
'←' | '\u2190' | Стрелка влево (Left Arrow) | val char = '\\u2190' |
'→' | '\u2192' | Стрелка вправо (Right Arrow) | val char = '\\u2192' |
'♥' | '\u2665' | Сердце (Heart) | val char = '\\u2665' |
CharSequence
Представляет собой последовательность символов. Он является более общим типом, чем String
, и предоставляет методы для работы с текстом. Это интерфейс, который не хранит данные сам по себе, а описывает поведение объектов, содержащих текст.
val text: CharSequence = "Kotlin is great!"
length
Возвращает длину последовательности символов.
val text: CharSequence = "Hello, Kotlin"
println("Length: ${text.length}") // Выведет: Length: 13
get(index: Int)
Возвращает символ по заданному индексу.
val text: CharSequence = "Hello"
println("Character at index 1: ${text.get(1)}") // Выведет: Character at index 1: e
subSequence(startIndex: Int, endIndex: Int)
Возвращает подстроку с указанного начального индекса до конечного (не включая его).
val text: CharSequence = "Kotlin Programming"
val subText = text.subSequence(0, 6)
println("Subsequence: $subText") // Выведет: Subsequence: Kotlin
toString
Преобразует CharSequence
в строку String
.
val text: CharSequence = StringBuilder("Mutable text")
val str: String = text.toString()
println("String representation: $str") // Выведет: String representation: Mutable text
String
https://kotlinlang.org/docs/strings.html |
Класс для работы со строками.
val name = "Котлин"
println("Привет, $name!") // Привет, Котлин
Number
базовый класс для числовых типов.
Math
Open-ended ranges ..<
Оператор для выражения диапазона значений. Работает как until
. Дает понять, что верхняя граница не включена
when (value) {
in 0.0..<0.25 -> // first quarter
in 0.25..<0.5 -> // second quarter
in 0.5..<0.75 -> // third quarter
in 0.75..1.0 -> // last quarter <- note closed range here
}
Операция по модулю
val a: Int = 65 % 10 // 5. Возвращает остаток от целочисленного деления двух чисел
// 65 / 10 = 6.5. Результатом будет число после запятой
// 10 % 3 = 1
// 10 / 20 = 0.5. Если целых до запятой нет то результатом будет числитель.
Операторы расширенного присвоения
val a: Int = 10
a += 2 // 12. Эквивалентно a = a + 2
a -= 2 // 8. Эквивалентно а = а - 2
a *= 2 // 20. Эквивалентно а = а * 2
a /= 2 // 5. Эквивалентно а = а / 2
a %= 3 // 1.
Операторы увеличения и уменьшения
var x: Int = 5
val y = ++x // Префиксный инкремент увеличивает значение на единицу. x должен быть var
printLn(x) // 6
printLn(y) // 6
var x = 5
val y = --x // Префиксный декремент уменьшает значение на единицу. x должен быть var
println(x) // x = 4
println(y) // y = 4
var x = 5
val y = x++ // Постфиксный инкремент возвращает значение до увеличения на единицу. x должен быть var
println(x) // x = 6
println(y) // y = 5
var x = 5
val y = x-- // Постфиксный декремент возвращает значение до уменьшения на единицу. x должен быть var
println(x) // x = 4
println(y) // y = 5
Округлить число
val value: Double = 3.14123456789
val roundedValue: Double = value
.toBigDecimal()
.setScale(2, RoundingMode.HALF_EVEN)
.toDouble() // 3.14
Logical Bit Operations
and
Используется для побитового логического И между двумя значениями типа Int
, Long
, или других целочисленных типов. Оператор and
выполняет побитовое сравнение двух чисел. Он сравнивает каждую пару битов двух чисел, и если оба бита равны 1, результатом будет 1, иначе — 0.
val a = 5 // в двоичной системе 0101
val b = 3 // в двоичной системе 0011
val result = a and b // результат 1, в двоичной системе 0001
println(result) // Выведет 1
or
Используется для выполнения побитовой операции «логическое ИЛИ» (bitwise OR) между целочисленными значениями (например, Int
, Long
). Оператор or выполняет побитовое сравнение двух чисел. Если хотя бы один бит из двух операндов равен 1, результатом для этого бита будет 1. Если оба бита равны 0, результатом будет 0.
val a = 5 // в двоичной системе: 0101
val b = 3 // в двоичной системе: 0011
val result = a or b // результат: 7 (в двоичной: 0111)
println(result) // Выведет 7
xor
Используется для выполнения побитовой операции «исключающее ИЛИ» между двумя целыми числами. Оператор обозначается символом ^
.
// a xor b возвращает 1, если биты a и b различны (один из них 1, а другой 0).
// Возвращает 0, если биты a и b одинаковы (оба 0 или оба 1).
fun main() {
val a = 5 // Бинарное представление: 0101
val b = 3 // Бинарное представление: 0011
val result = a xor b // 0110 (6 в десятичной системе)
println(result) // Выведет: 6
}
Bit Shift Operations
Kotlin позволяет сдвигать биты целого числа влево или вправо (умножение или деления целого числа на степень двойки.).
shl
Оператор сдвига влево. Сдвигает биты целого числа влево на указанное количество позиций, сохраняя исходный знак целого числа. Младшие значащие биты с правой стороны значения заполняются нулями. Операция эквивалентна умножению целого числа на n^2.
@Test
fun `shifts bits left in an integer`() {
assertEquals(512, 128 shl 2)
}
shr
Оператор сдвига вправо. Сдвигает биты целого числа вправо на указанное количество позиций. Эта операция эквивалентна делению целого числа на n^2, сохраняя знак исходного целого числа.
@Test
fun `shifts bits right in a positive integer`() {
assertEquals(32, 128 shr 2)
}
@Test
fun `shifts bits right in a negative integer`() {
val a = -128 // 11111111111111111111111110000000
val expected = -32 // 11111111111111111111111111100000
assertEquals(expected, a shr 2)
}
ushr
Оператор сдвига вправо без знака. Сдвигает биты целого числа вправо на указанное количество позиций. Избыточные биты, сдвинутые вправо, отбрасываются, а нулевые биты всегда сдвигаются слева. В отличие от оператора shr
оператор ushr
не сохраняет знак исходного целого числа.
@Test
fun `shifts unsigned bits right in an integer`() {
val a = -128 // 11111111111111111111111110000000
val expected = 1073741792 // 00111111111111111111111111100000
assertEquals(expected, a ushr 2)
}
Bit Methods
inv
Используется для выполнения побитовой операции «NOT» (инверсия) над целым числом. Этот оператор инвертирует все биты числа, превращая 0 в 1 и 1 в 0. Для целых чисел, таких как Int или Long, операция inv меняет каждый бит числа на противоположный. Например, если в двоичном представлении число было 0101, то после применения inv получится 1010.
fun main() {
val a = 5 // Бинарное представление: 0000 0101
val result = a.inv() // Инвертирует все биты: 1111 1010
println(result) // Выведет: -6
}
countOneBits
Используется для подсчета количества единичных битов (1) в бинарном представлении числа.
fun main() {
val a = 5 // Бинарное представление: 0101
val count = a.countOneBits() // 2 (два единичных бита)
println(count) // Выведет: 2
}
countLeadingZeroBits
Используется для подсчета количества ведущих нулевых битов в бинарном представлении целого числа. Возвращает количество нулевых битов, расположенных в начале (слева) бинарного представления числа, до первого встреченного бита 1.
fun main() {
val a = 5 // Бинарное представление: 0000 0101
val count = a.countLeadingZeroBits() // 4
println(count) // Выведет: 4
}
countTrailingZeroBits
Используется для подсчета количества завершающих нулевых битов в бинарном представлении целого числа. Возвращает количество нулевых битов, расположенных в конце (справа) бинарного представления числа, после последнего встреченного бита 1.
fun main() {
val a = 40 // Бинарное представление: 0010 1000
val count = a.countTrailingZeroBits() // 3
println(count) // Выведет: 3
}
takeHighestOneBit
Возвращает значение, которое содержит только самый старший бит в бинарном представлении целого числа, установленных в 1. Все остальные биты устанавливаются в 0. Находит самый старший бит, установленный в 1, и устанавливает только этот бит в результирующее значение, в то время как все остальные биты устанавливаются в 0.
fun main() {
val a = 42 // Бинарное представление: 0010 1010
val result = a.takeHighestOneBit() // 0010 0000 (32 в десятичной системе)
println(result) // Выведет: 32
}
takeLowestOneBit
Возвращает значение, в котором установлен только самый младший бит, установленный в 1 в бинарном представлении числа. Все остальные биты устанавливаются в 0. Находит самый младший бит, установленный в 1, и устанавливает только этот бит в результирующее значение, в то время как все остальные биты устанавливаются в 0.
fun main() {
val a = 42 // Бинарное представление: 0010 1010
val result = a.takeLowestOneBit() // 0000 0010 (2 в десятичной системе)
println(result) // Выведет: 2
}
rotateLeft
Используется для побитового циклического сдвига (или вращения) чисел влево. Это означает, что биты числа сдвигаются влево на указанное количество позиций, а биты, вышедшие за пределы числа, переносятся в начало. Сдвигает биты числа влево на указанное количество позиций. Биты, которые “выпадают” за левую границу, возвращаются в начало числа с правой стороны.
fun main() {
val a = 9 // Бинарное представление: 0000 1001
val result = a.rotateLeft(2) // 0010 0100 (36 в десятичной системе)
println(result) // Выведет: 36
}
rotateRight
Используется для побитового циклического сдвига (или вращения) чисел вправо. Это означает, что биты числа сдвигаются вправо на указанное количество позиций, а биты, которые «выпадают» с правой стороны, переносятся в начало числа. Сдвигает биты числа вправо на указанное количество позиций. Биты, которые «выпадают» за правую границу, возвращаются в начало числа с левой стороны.
fun main() {
val a = 36 // Бинарное представление: 0010 0100
val result = a.rotateRight(2) // 0000 1001 (9 в десятичной системе)
println(result) // Выведет: 9
}
Properties
get
Метод, который вызывается при доступе к свойству для его чтения. В Kotlin геттеры создаются автоматически для всех свойств, но их можно переопределить, чтобы выполнить дополнительные действия или вернуть другое значение.
• Добавить логику при доступе к переменной
var age: Int = 25
get() {
println("Геттер вызывается")
return field
}
• Использовать field
var name: String = "John"
get() {
return field.toUpperCase()
}
• Свойства val
val birthYear: Int = 1990
get() = field + 1
• Переопределение геттера в производном классе
open class User {
open val age: Int = 25
}
class AdultUser : User() {
override val age: Int
get() = super.age + 10
}
• Определение геттера в интерфейсе
interface Person {
val age: Int
get() = 18
}
• Применение геттера с Backing Field
// Применение геттеров с Backing Field
class User {
private var _name: String? = null
val name: String
get() = _name ?: "No name provided"
}
set
Метод, который вызывается при присваивании значения свойству. В Kotlin сеттеры создаются по умолчанию только для изменяемых свойств (var
).
• Логика при присвоении значения переменной
var age: Int = 25
set(value) {
println("Сеттер вызывается")
field = value
}
• Использование field
var age: Int = 25
set(value) {
field = value // `field` ссылается на свойство `age`
}
• Валидация данных в сеттере
var age: Int = 25
set(value) {
if (value >= 0) {
field = value
} else {
println("Возраст не может быть отрицательным")
}
}
• Переопределение сеттера в производном классе
open class User {
open var age: Int = 25
}
class AdultUser: User() {
override var age: Int = 30
set(value) {
if (value >= 18) {
field = value
} else {
println("Возраст должен быть не меньше 18")
}
}
}
• Определение сеттера в интерфейсе
interface Person {
var age: Int
set(value) // Интерфейс определяет, что должно быть свойство с сеттером
}
class User : Person {
override var age: Int = 18
set(value) {
field = value
}
}
• Применение сеттеров с Backing Field
class User {
private var _name: String = "John"
var name: String
get() = _name
set(value) {
_name = value.trim() // Устанавливаем значение, предварительно удалив лишние пробелы
}
}
field
Используется внутри геттеров и сеттеров для обращения к свойству класса. Оно представляет бэкэнд-поле свойства, которое используется для хранения значения.
class Product {
var price: Double = 0.0
get() = field * 1.2 // Добавляем наценку 20%
}
fun main() {
val product = Product()
product.price = 100.0
println(product.price) // Выведет: 120.0
}
Exceptions
https://kotlinlang.org/docs/exceptions.html |
Исключения — это события, которые нарушают нормальное выполнение программы, возникающие в результате ошибок во время выполнения, таких как деление на ноль, выход за пределы массива или работа с недоступными ресурсами. Kotlin использует механизмы обработки исключений, такие как try
, catch
и finally
, для управления этими ситуациями и обеспечения устойчивости приложения.
try {
// Код, генерирующий исключение
} catch (e: Exception) {
// Обработка исключения
} finally {
// Постобработка
}
Throwable
Корневой класс всех исключений и ошибок в Kotlin (как и в Java). Он разделяется на два подкласса: Error
и Exception
.
Throwable
├── Error
│ ├── StackOverflowError
│ └── OutOfMemoryError
│
└── Exception
├── RuntimeException
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ └── IndexOutOfBoundsException
├── IOException
└── SQLException
Error
Этот класс представляет собой серьезные ошибки, от которых, как правило, нельзя восстановиться. Ошибки этого типа не нужно обрабатывать в коде, так как они указывают на проблемы на уровне JVM или системы.
• OutOfMemoryError
– возникает, когда программа превышает допустимый объём памяти.
• StackOverflowError
– возникает, когда программа уходит в бесконечную рекурсию.
Exception
Этот класс представляет собой обычные исключения, которые могут возникнуть в ходе выполнения программы. Эти исключения можно обрабатывать с помощью блоков try
-catch
.
• RuntimeException
- Представляет исключения времени выполнения, которые возникают из-за ошибок логики программы.
• NullPointerException
- Возникает при попытке обращения к объекту с null значением.
• IndexOutOfBoundsException
- Выбрасывается при доступе к элементам коллекции или массива за пределами их границ.
• IllegalArgumentException
- Возникает, когда методу передан аргумент, не соответствующий его ожиданиям.
• IOException
- Представляет исключения, которые связаны с ошибками ввода/вывода (например, при работе с файлами или сетевыми соединениями).
• SQLException
- Указывает на ошибки, связанные с работой с базами данных.
• FileNotFoundException
- Возникает, когда указанный файл не может быть найден.
try
Используется для оборачивания кода, который может выбросить исключение. Это блок, в котором выполняется потенциально «опасный» код.
• Требует обязательного наличия блоков catch
или finally
.
try {
val result = 10 / 0
} catch (e: ArithmeticException) {
println("Ошибка деления на ноль")
} finally {
println("Завершение операции")
}
try {
val result = 10 / 0
} catch (e: ArithmeticException) {
println("Ошибка деления на ноль")
}
try {
val result = 10 / 5
} finally {
println("Завершение операции")
}
catch
Перехватывает исключения, возникшие внутри try
. В catch можно указать конкретный тип исключения, который нужно обработать, или использовать общий тип Exception
.
• Можно указать сколько угодно блоков catch
.
try {
val numbers = listOf(1, 2, 3)
println(numbers[5]) // IndexOutOfBoundsException
val result = 10 / 0 // ArithmeticException
} catch (e: ArithmeticException) {
println("Ошибка: деление на ноль.")
} catch (e: IndexOutOfBoundsException) {
println("Ошибка: индекс вне границ списка.")
} catch (e: Exception) {
println("Произошло общее исключение: ${e.message}")
}
finally
Этот блок выполняется всегда, независимо от того, было исключение или нет. Обычно его используют для освобождения ресурсов, закрытия файлов или завершения каких-то операций.
var inputStream: FileInputStream? = null
try {
inputStream = FileInputStream(fileName)
inputStream.readBytes()
} catch (e: IOException) {
println("Ошибка")
} finally {
inputStream?.close()
}
throw
Используется для явного выбрасывания исключения. Оно позволяет создать и бросить экземпляр класса исключения, что приводит к немедленному прерыванию выполнения текущего блока кода и передаче управления ближайшему блоку catch
, если он существует. Имеет тип Nothing
.
fun validateAge(age: Int) {
if (age < 18) {
throw IllegalArgumentException("Возраст должен быть не меньше 18")
}
}
@Throws
Используется для явного указания на то, какие исключения могут быть выброшены из функции. Это особенно полезно при работе с Java-кодом, поскольку в Java существуют проверяемые исключения, которые требуют обработки или явного указания в сигнатуре метода. Можно указать одно или несколько исключений, которые могут возникнуть при выполнении функции. Когда такая функция вызывается из Java-кода, IDE и компиляторы будут предупреждать, если это исключение не обрабатывается. Аннотация @Throws
не является обязательной в Kotlin, так как языковые конструкции уже позволяют обрабатывать исключения. Однако она полезна для обеспечения совместимости с Java.
@Throws(IllegalArgumentException::class)
fun validateAge(age: Int) {
if (age < 18) {
throw IllegalArgumentException("Возраст должен быть не меньше 18")
}
}
Smart casts
https://kotlinlang.org/docs/typecasts.html |
is
Используется для проверки типа объекта во время выполнения. Позволяет проверить, соответствует ли объект определенному типу.
if (obj is SomeType) {
// Действия, если obj является экземпляром SomeType
}
!is
Противоположный оператор, который проверяет, не является ли объект указанным типом.
fun checkNotString(obj: Any) {
if (obj !is String) {
println("This is not a String")
} else {
println("This is a String of length ${obj.length}")
}
}
fun main() {
checkNotString(100) // Выведет: This is not a String
checkNotString("Hello") // Выведет: This is a String of length 5
}
as
Используется для приведения типов. Позволяет явно преобразовать объект к определенному типу. Если объект не может быть приведен к указанному типу, оператор as выбрасывает исключение ClassCastException
.
val obj: Any = "Hello, Kotlin"
val str: String = obj as String // Приведение типа к String
as?
Безопасное приведение типа.
val obj: Any = 123
val str: String? = obj as? String // Попытка приведения типа к String
Annotations
Эти аннотации помогают управлять тем, как Kotlin-код будет компилироваться и взаимодействовать с Java-кодом, обеспечивая гибкость и совместимость.
annotation
Используется для создания аннотаций, которые могут добавлять метаданные к классам, функциям, параметрам и другим элементам кода. Аннотации помогают компилятору или сторонним инструментам обрабатывать код.
annotation class Info(val author: String, val version: Int)
@Info(author = "John", version = 1)
class Example
@JvmStatic
Делает методы или свойства компаньон-объекта или объекта синглтона доступными как статические методы или свойства в Java.
// В Java это будет доступно как MyClass.staticMethod().
class MyClass {
companion object {
@JvmStatic
fun staticMethod() {
// ...
}
}
}
@JvmOverloads
Генерирует перегруженные версии функции с параметрами по умолчанию для Java. Это позволяет вызывать функцию из Java без указания всех аргументов, которые имеют значения по умолчанию.
// В Java будут созданы перегруженные методы greet(String) и greet(String, int).
class MyClass {
@JvmOverloads
fun greet(message: String, times: Int = 1) {
// ...
}
}
@JvmField
Делает свойство публичным полем в Java, а не геттером/сеттером.
// В Java это будет доступно как myClass.someValue вместо myClass.getSomeValue().
class MyClass {
@JvmField
val someValue: Int = 42
}
@JvmInline
Используется для объявлений класса, который будет компилироваться как «вложенный» inline
-класс. Это позволяет уменьшить накладные расходы на создание экземпляров классов и улучшить производительность.
// В Java это будет представлено как обычный тип (например, int), что может улучшить производительность за счет уменьшения накладных расходов.
@JvmInline
value class MyValue(val value: Int)
@JvmName
Позволяет изменить имя класса, метода или свойства, видимое в Java. Это полезно для избежания конфликтов имен.
// В Java это будет доступно как renamedFunction().
@JvmName("renamedFunction")
fun myFunction() {
// ...
}
@JvmSynthetic
Прячет методы или свойства от Java-кода. Это позволяет скрыть их от Java, делая их доступными только в Kotlin.
class MyClass {
@JvmSynthetic
fun hiddenMethod() {
// ...
}
}
@JvmDefault
Позволяет задать реализацию по умолчанию для методов интерфейса в Java. Эта аннотация используется для обеспечения совместимости между Kotlin и Java, когда интерфейсы имеют методы по умолчанию.
interface MyInterface {
@JvmDefault
fun defaultMethod() {
// ...
}
}
Concurrent
thread
Используется для создания и запуска нового потока. Он является удобной оберткой над классом Thread
, позволяя вам легко создавать потоки с минимальными усилиями. Метод позволяет задавать параметры, такие как name
, start
, daemon
, и contextClassLoader
, чтобы настроить новый поток.
fun main() {
thread {
// Код выполняется в новом потоке
println("Running in a separate thread")
}
println("Running in the main thread")
}
@Synchronized
Используется для обозначения методов или блоков кода, которые должны быть выполнены синхронно, что предотвращает одновременное выполнение нескольких потоков и защищает от возможных гонок данных. Когда метод помечен этой аннотацией, он автоматически синхронизируется на объекте, на котором он вызывается, что позволяет обеспечивать потокобезопасность для доступа к разделяемым ресурсам.
// В этом примере методы increment и getCount синхронизированы, что обеспечивает корректное обновление и чтение значения count из разных потоков.
class Counter {
private var count = 0
@Synchronized
fun increment() {
count++
}
@Synchronized
fun getCount(): Int {
return count
}
}
var value: Int = 0
@Synchronized get() = 5
@Synchronized set(value) { field = value }
Preconditions
require
Функция для проверки аргументов.
Принимает параметры: value
и lazyMessage
.
Если value
= false, то функция вернет IllegalArgumentException
с сообщением lazyMessage
.
Можно передать только value
тогда текст ошибки будет стандартным: Failed requirement.
fun printPositiveNumber(num: Int) {
require(num > 0)
println(num)
}
fun printPositiveNumber(num: Int) {
require(num > 0) { "Number must be positive" }
println(num)
}
requireNotNull
Функция для проверки аргумента на null.
Принимает параметры: value
и lazyMessage
.
Если value = null, то функция вернет IllegalArgumentException
с сообщением lazyMessage
.
Можно передать только value
тогда текст ошибки будет стандартным: Required value was null.
private val _numberFlow = MutableStateFlow(0)
val numberFlow = _numberFlow.asStateFlow()
val number = requireNotNull(numberFlow.value)
check
Функция для проверки состояния.
Принимает параметры: value
и lazyMessage
.
Если value
= false, то функция вернет IllegalStateException
с сообщением переданным в lazyMessage
.
Можно передать только value
тогда текст ошибки будет стандартным: Check failed.
var someState: String = ""
val state = check(someState) { "State must be not empty" }
checkNotNull
Функция для проверки состояние на null.
Принимает параметры: value
и lazyMessage
.
Если value = null, то функция вернет IllegalStateException
с сообщением lazyMessage
.
Можно передать только value
тогда текст ошибки будет стандартным: Required value was null.
var someState: String? = null
val state = checkNotNull(someState) { "State must be not null" }
error
Функция для намеренного вызова исключения в коде. Выдаст IllegalStateException
.
fun divide(a: Int, b: Int): Int {
if (b == 0) {
error("Cannot divide by zero")
}
return a / b
}
Contracts
Позволяет уточнять контрактные соглашения о поведении функций, которые могут быть использованы для оптимизации компилятора и упрощения анализа кода. Контракты помогают компилятору Kotlin лучше понимать намерения разработчика и улучшать поведение кода за счет более точного анализа. Они могут быть полезны в ряде случаев, например, для корректной работы с условными выражениями, которые зависят от результата вызова функции, или для уточнения условий, которые должны выполняться для корректной работы кода.
• Контракт для проверки null
// В этом примере contract указывает, что если функция requireNotNull не выбрасывает исключение, то её результат не может быть null.
// Это помогает компилятору понимать, что после вызова requireNotNull(x) переменная y не может быть null.
fun requireNotNull(value: Any?): Any {
contract {
returnsNotNull() implies (value != null)
}
if (value == null) {
throw IllegalArgumentException("Value cannot be null")
}
return value
}
fun example() {
val x: String? = "Hello"
val y: String = requireNotNull(x) // После вызова, y не может быть null
}
• Контракт для условного выражения
// В этом примере контракт сообщает компилятору, что если функция isEmptyOrNull возвращает true, то list либо пустой, либо null.
// Это позволяет компилятору понять, что если isEmptyOrNull возвращает true, то переменная list будет либо пустой, либо null.
fun <T> List<T>.isEmptyOrNull(): Boolean {
contract {
returns(true) implies (this@isEmptyOrNull == null || this@isEmptyOrNull.isEmpty())
}
return this == null || this.isEmpty()
}
fun example(list: List<String>?) {
if (list.isEmptyOrNull()) {
// list гарантированно пустой или null
}
}
DSL
https://kotlinlang.org/docs/type-safe-builders.html |
Kotlin DSL (Domain-Specific Language) — это способ создания мини-языков, ориентированных на конкретную предметную область, с помощью синтаксических возможностей Kotlin. DSL позволяет сделать код более читаемым и интуитивно понятным для разработчиков, предоставляя специфичный синтаксис для определённых задач. Примеры: Gradle Kotlin DSL и Jetpack Compose.
class Pizza {
var dough: String = ""
var sauce: String = ""
var toppings: MutableList<String> = mutableListOf()
fun topping(name: String) {
toppings.add(name)
}
override fun toString(): String {
return "Pizza with $dough dough, $sauce sauce, and toppings: $toppings"
}
}
fun pizza(init: Pizza.() -> Unit): Pizza {
val pizza = Pizza()
pizza.init()
return pizza
}
val myPizza = pizza {
dough = "Thin crust"
sauce = "Tomato"
topping("Cheese")
topping("Pepperoni")
}
println(myPizza)
Расширение синтаксиса
Kotlin предоставляет мощные функции, такие как функции расширения, лямбды с приемником, именованные аргументы, что делает возможным создание удобных DSL.
fun String.hasLength(length: Int) = this.length == length
Лямбды с приемником
Используются для создания контекста, в котором вызываются методы и свойства объекта без необходимости постоянно указывать его. Это позволяет строить цепочки вызовов.
class HtmlTag(val name: String) {
fun render() = "<$name></$name>"
}
fun html(init: HtmlTag.() -> Unit): HtmlTag {
val tag = HtmlTag("html")
tag.init()
return tag
}
val result = html {
// Внутри этой лямбды мы можем вызывать методы HtmlTag напрямую
}
Именованные аргументы и параметры по умолчанию
Именованные аргументы позволяют писать более читаемый код в DSL, задавая значение параметров прямо в вызове функции.
fun buildHouse(floors: Int = 2, color: String = "White") {
println("House with $floors floors and $color color")
}
buildHouse(floors = 3, color = "Blue")
Вопросы на собесе (82)
Variables (14)
- Разница между val и var?
В Kotlin
val
используется для создания неизменяемых переменных, значение которых можно установить только один раз, тогда какvar
создаёт изменяемые переменные, значение которых можно изменять.
- Разница между val и const val?
val
инициализируется во время выполнения, аconst val
— на этапе компиляции.const val
поддерживает только ограниченный набор типов, и может быть объявлен только в корне пакета или объекте, тогда какval
может находиться в любом классе или функции.
- В чем преимущество использования val вместо var?
Использование
val
вместоvar
обеспечивает неизменяемость, что делает код безопаснее и облегчает его понимание. Это помогает избежать непреднамеренных изменений данных, снижая вероятность ошибок.
- Что будет если вызвать lateinit var до объявления?
Произойдет
UninitializedPropertyAccessException
.
- Всегда ли val возвращает одно и тоже значение?
Не всегда. Если добавить
get()
- значение будет возвращаться каждый раз при обращении к свойству, и оно может изменяться в зависимости от логики геттера.
- Для чего используется const?
Для объявления неизменяемых констант, которые известны на этапе компиляции. Могут быть использованы в качестве значений по умолчанию и в аннотациях.
- Какие типы данных поддерживает const?
Int
Long
Double
Float
Boolean
Char
String
- Можно ли в const добавить List?
Нет. Тип должен быть известен на этапе компиляции и быть неизменяемым.
- Где можно объявлять константу?
• На уровне файла (top-level) вне классов.
• Внутри
object
.
- Где хранятся переменные const val?
На уровне байткода Java как
static final
поля в сгенерированном классе. Они компилируются как статические константы и хранятся в метаспейс (метаданные) JVM, что позволяет к ним быстро обращаться без создания экземпляра класса. Благодаря этому значениюconst val
можно обращаться напрямую по имени класса, так как они статические и глобально доступны.
- Есть ли в Kotlin возможность объявить поле, чтобы оно скомпилировалось в Java-примитив?
Да. Для этого нужно использовать типы
Int
,Long
,Double
,Float
,Char
,Boolean
,Byte
, иShort
— Kotlin автоматически скомпилирует их в соответствующие Java-примитивы, если это возможно. Например,val number: Int = 10
скомпилируется вint
в байткоде Java. Однако если поле nullable (например,Int?
), оно будет скомпилировано какInteger
в Java, чтобы поддерживать значениеnull
.
- Каким правилам должна соответствовать переменная, чтобы стать Java-примитивом?
Для компиляции переменной в Java-примитив она должна быть ненуллабельной и одного из примитивных типов Kotlin, таких как
Int
,Boolean
,Double
и т.д. При этом переменная не должна использоваться в контексте, где требуется объектный тип, например, при использованииInt?
.
- Можно ли изменить переменную типа val?
Переменную типа
val
нельзя изменить после инициализации. Она является read-only, что означает, что после присвоения значения вы не можете присвоить новое значение этой переменной. Однако если переменнаяval
содержит изменяемый объект, например, список или массив, вы можете изменять содержимое этого объекта, но не можете переназначить саму переменную.val myList = mutableListOf(1, 2, 3) myList.add(4) // Допустимо, изменяем содержимое списка
- Где неверно создана переменная?
•
val str: String = null
•
var num = 50;
•
var number: Float = 45.001f
•
var isGet: Boolean? = null
•
var char = 'S'
- Разница между val и var?
Types (3)
- Какие типы есть в Kotlin?
• Числовые:
Int
Long
Double
Float
Byte
Short
• Символы:
Char
• Логический:
Boolean
• Строки:
String
• Специальные:
Any
Unit
Nothing
- Почему в Kotlin нет примитивных типов?
Потому что язык использует объекты для представления всех типов данных, включая те, которые в Java являются примитивами. Это упрощает работу с типами, делает язык более однородным, позволяет избежать проблем с боксингом/анбоксингом и различиями между примитивами и объектами, позволяет использовать типы в extension-функциях.
- Какого типа данных не существует в Kotlin?
•
Array
•
Object
•
Int
•
List
• Все перечисленные существуют
- Какие типы есть в Kotlin?
Basic Types (8)
- Что такое Any?
Базовый тип для всех классов в Kotlin.
- Какие методы есть у класса Any?
equals()
hashCode()
toString()
- Разница между Any в Kotlin и Object в Java?
Any
содержит только 3 метода:toString
,equals
, иhashCode
. В отличие отObject
,Any
не включает методы, связанные с многопоточностью, такие какwait
,notify
, иnotifyAll
. В Kotlin такие методы предоставляются черезkotlin.concurrent
или в других библиотеках, что делаетAny
более легковесным и подходящим для Kotlin-экосистемы.
- Что такое Unit?
Тип для функций без возвращаемого значения, аналог
void
в Java.
- Для чего нужен Nothing?
Используется для обозначения типов, которые никогда не возвращают значения. Например, функции, которые всегда выбрасывают исключения или бесконечно выполняются. Nothing используется для улучшения типов и логики кода, указывая на неестественные или непредвиденные состояния.
- Как Nothing устроен в Kotlin?
Класс с приватным конструктором.
- Почему Unit в Kotlin существует в единственном экземпляре?
В Kotlin существует только один экземпляр класса
Unit
. Это Singleton, представляющий отсутствие возвращаемого значения в функциях или пустое значение. Все функции, которые не возвращают ничего, фактически возвращают этот единственный объектUnit
.
- Сколько экземпляров класса Unit может существовать?
• Ни одного.
• Три.
• Один.
• Сколько угодно.
- Что такое Any?
Modifiers (6)
- Какие есть модификаторы доступа в Kotlin? В чем отличие от Java?
public
private
protected
internal
- Разница между модификаторами в Kotlin и Java?
В Kotlin
internal
ограничивает доступ в пределах модуля, а в Java эквивалентный модификатор отсутствует.
- Для чего явно используется модификатор final в Kotlin?
Запрещает повторное переопределение метода в дочернем классе.
- Для чего используется ключевое слово open?
Используется для того, чтобы разрешить наследование от класса или переопределение метода/свойства, поскольку по умолчанию классы и их члены закрыты для наследования.
- Можно ли объявить конструкцию private open class Person?
Да.
- Какие существуют модификаторы доступа у элементов класса?
•
public
доступен из любого места. Это значение по умолчанию.•
private
доступен только внутри самого класса или файла (если используется на уровне файла).•
protected
доступен только внутри самого класса и его подклассов. Не может использоваться для элементов на уровне файла.•
internal
доступен внутри модуля, то есть коду, который компилируется вместе (например, в одном проекте или библиотеке).•
open
позволяет методу быть унаследованным или переопределенным. По умолчанию все классы и методы в Kotlin являютсяfinal
, если не указано иное.•
final
запрещает наследование класса или переопределение метода. Используется по умолчанию для классов и методов, если не указан модификаторopen
.
- Какие есть модификаторы доступа в Kotlin? В чем отличие от Java?
Exceptions (10)
- В чем отличие исключений в Kotlin и Java?
В Kotlin нет проверенных исключений, и вместо обязательной обработки ошибок можно использовать функциональные подходы с
Result
. Исключения генерируются и обрабатываются аналогично Java, но без требований к проверке исключений.
- Есть ли в Kotlin проверяемые исключения?
В Kotlin нет проверяемых исключений (checked exceptions), в отличие от Java. Все исключения являются непроверяемыми (unchecked), что означает, что компилятор не требует обработки или объявления исключений в сигнатуре функции. Это упрощает код и делает его более лаконичным, однако требует от разработчиков большей ответственности за обработку возможных ошибок.
- Как обрабатываются исключения в Kotlin?
Исключения в Kotlin обрабатываются с помощью блоков
try
-catch
-finally
. Внутри блокаtry
выполняется код, который может вызвать исключение. Если исключение возникает, управление передается в соответствующий блокcatch
. Блокfinally
выполняется в любом случае после завершенияtry
илиcatch
, и используется для освобождения ресурсов.
- Какая иерархия у исключений в Kotlin?
В Kotlin иерархия исключений основана на классе
Throwable
, который является корневым классом. Он делится на две основные категории:•
Error
- ошибки, указывающие на серьезные проблемы, с которыми приложение не может справиться (например,OutOfMemoryError
).•
Exception
- исключения, которые могут быть обработаны программой:RuntimeException
: Исключения времени выполнения, (например,NullPointerException
,IndexOutOfBoundsException
,IllegalArgumentException
) и Checked exceptions: Исключения, которые должны быть обработаны или объявлены в сигнатуре функции (например,IOException
).
- В чем принципиальная разница между Error и Exception?
Error
обозначает серьезные проблемы, обычно вне контроля приложения, тогда какException
относится к условиям, которые можно предсказать и обработать для повышения устойчивости.
- Приведи примеры исключений типа Error в Kotlin?
•
OutOfMemoryError
•
StackOverflowError
- Для чего в конструкции обработки исключений нужен блок finally?
Блок
finally
выполняется независимо от того, произошло ли исключение, что позволяет гарантировать выполнение кода, например, для освобождения ресурсов, таких как закрытие потоков или соединений.
- Может ли конструкция try-catch-finally не содержать блок try?
Нет.
- Может ли конструкция try-catch-finally не содержать блок catch?
Да.
- Сколько блоков catch может содержать конструкция try-catch-finally?
Сколько угодно.
- Переменной A присвоен блок try-finally, в try значение устанавливается = 10 в finally = 20, чему будет равно А?
Переменная A будет равна 10, поскольку значение в блоке try присваивается переменной до выполнения блока finally. Блок finally выполняется после, но он не изменяет значение переменной A.
- В чем отличие исключений в Kotlin и Java?
init (4)
- Сколько блоков init можно объявить в классе?
Сколько угодно.
- В каком порядке выполняются код в нескольких блоках init?
В порядке их объявления в классе.
- Где код выполнится быстрее, в блоке init или в secondary constructor?
Код выполнится быстрее в блоке
init
, так как он вызывается сразу после первичного конструктора.
- Можно ли объявить блок init внутри object?
Да, блок
init
можно объявить внутриobject
, и он выполнится при первой инициализации этого объекта.
- Сколько блоков init можно объявить в классе?
Class (5)
- Какие классы существуют в Kotlin?
•
class
•
data class
•
value class
•
sealed class
•
enum class
•
inner class
• nested class (просто класс внутри класса).
- Разница между class в Kotlin и Java?
• В Kotlin классы по умолчанию
final
, в Java -public
.• Kotlin поддерживает первичные и вторичные конструкторы; Java — только явные.
• В Kotlin есть
data class
с автоматической реализацией методов.
- Разница между == и ===?
•
==
сравнивает значения (эквивалентequals
в Java).•
===
сравнивает ссылки (проверяет, указывают ли обе ссылки на один и тот же объект).
- Как сравнить ссылки на классы?
С помощью оператора
===
. Этот оператор проверяет, ссылаются ли две переменные на один и тот же объект в памяти.
- Что выводит метод toString() у обычного класса?
По умолчанию метод
toString()
у обычного класса в Kotlin выводит строку, состоящую из имени класса и хеша объекта в формате, похожем на: ClassName@hashcode.
- Какие классы существуют в Kotlin?
Functions (7)
- Чем отличается передача параметров в функцию в Kotlin и в Java?
• Kotlin поддерживает значения по умолчанию для параметров, что позволяет вызывать функции без необходимости указывать все аргументы, в Java для этого требуется перегрузка методов.
• В Java можно работать с полями функции, присваивая им новые значения в теле функции.
- Как в Kotlin передаются параметры функции по ссылке или по значению?
По значению. Передаётся копия ссылки на объект, а не сам объект. Поэтому, если объект изменяемый (например,
MutableList
), то можно изменить его содержимое внутри функции. Но если изменить саму ссылку (например, присвоить новый объект), то это не отразится на исходной переменной.
- Если передать в функцию класс с var-полями можно ли будет поменять значение полей?
Да, если передать в функцию объект класса с
var
-полями, можно изменить значения этих полей, так как объект передается по ссылке.
- Что такое функция высшего порядка?
Это функция, которая принимает другую функцию в качестве аргумента или возвращает функцию в качестве результата.
- Что такое анонимная функция?
Анонимная функция — это функция без имени, используемая на месте для кратковременных операций.
- Напиши результат вывода на экран 5 закомментированных выражений, в случае любой ошибки в ответе напиши Exception?
fun transform(x: Int): Int = x * 2 fun main() { val numbers = listOf(1, 2, 3) // println("1: " + numbers.map(::transform)) // println("2: " + numbers.map { ::transform }) // println("3: " + numbers.map { transform(it) }) // println("4: " + numbers.map { _ -> ::transform }) // println("5: " + numbers.map { x -> transform(x) }) }
Ответы
1 -[2, 4, 6]
2 -[Function1, Function1, Function1]
3 -[2, 4, 6]
4 -[Function1, Function1, Function1]
5 -[2, 4, 6]
- Какой синтаксис используется для создания функции, которая может принимать неограниченное количество аргументов?
•
fun name(args: List<Any>)
•
fun name(vararg args: Any)
•
fun name(args: Any...)
•
fun name(args: Array<Any>)
- Чем отличается передача параметров в функцию в Kotlin и в Java?
Enum (1)
- Какие типы данных можно положить в enum?
•
Int
Long
Float
Double
Boolean
Char
и другие.•
String
.•
class
data class
.• Объекты другого
enum class
.• Lambdas.
- Какие типы данных можно положить в enum?
Lambdas (3)
- Что такое лямбда в Kotlin?
Это анонимная функция, которая может быть присвоена переменной или передана в качестве аргумента другой функции. Она может захватывать переменные из своего окружения и используется для функционального программирования. Лямбда-выражения позволяют писать более лаконичный и выразительный код.
- В каком из разделов памяти хранятся лямбды?
Лямбды в Kotlin хранятся в разделе памяти, называемом heap (куча). При создании лямбды для хранения ее состояния и захваченных переменных, создается объект, который размещается в куче.
- Есть ли equals и hashcode у лямбды в Kolin?
• Есть только
equals()
.• Есть обе функции.
• Нет ни одной.
- Что такое лямбда в Kotlin?
Анонимный класс (3)
- Что такое анонимный класс?
Это класс, который не имеет имени и создается на месте, обычно для реализации интерфейсов или абстрактных классов. Он позволяет создавать объекты с переопределенными методами без явного определения нового класса. Такой подход удобен для краткосрочного использования и уменьшает количество кода.
- Можно ли создать инстанс анонимного класса?
Да, можно создать инстанс анонимного класса, используя синтаксис, который реализует интерфейс или наследует класс на месте.
- Какой недостаток есть у анонимных классов с точки зрения памяти?
Создание дополнительных объектов со скрытой ссылкой на родительский класс, что может привести к утечкам памяти. Это увеличивает время жизни родительского объекта, даже если он больше не нужен, особенно в долгоживущих потоках.
- Что такое анонимный класс?
DSL (3)
- Что такое DSL в Kotlin?
Способ создания мини-языков, специфичных для определенной задачи. Он позволяет писать код, который выглядит как набор деклараций, что упрощает работу с библиотеками или фреймворками.
- Какие есть примеры использования DSL?
• Gradle Kotlin DSL для настройки проектов.
• Jetpack Compose.
• HTML Builders: Создание HTML-документов.
• Exposed: Работа с базами данных через декларативные SQL-запросы.
- Какие конструкции поддерживает DSL?
• Лямбды с приемником: позволяют вызывать функции объекта без явного указания его имени.
• Инфиксные функции: позволяют писать более читаемые выражения.
• Расширения: добавляют методы к существующим классам для улучшения API.
• Именованные аргументы: улучшают читаемость, особенно для сложных вызовов функций.
• Операторы перегрузки: позволяют использовать синтаксический сахар для улучшения выразительности.
- Что такое DSL в Kotlin?
Concurrent (2)
- Почему synchronized в Kotlin сделан через аннотацию @Synchronized?
@Synchronized
в Kotlin реализован как аннотация для упрощения синхронизации методов, делая код более читаемым. Это позволяет разработчикам легко добавлять потокобезопасность, избегая ручного управления синхронизацией и снижая вероятность ошибок.
- Будет ли данный код работать корректно?
@Synchronized fun fib(x: Int): Int = if (x < 2) x else fib(x - 1) + fib(x - 2)
Код будет работать корректно с точки зрения потокобезопасности благодаря аннотации
@Synchronized
. Однако, из-за рекурсивной реализации без мемоизации или оптимизаций, при больших значениях x он будет крайне неэффективен и может привести к переполнению стека.
- Почему synchronized в Kotlin сделан через аннотацию @Synchronized?
Другие (13)
- Чем Kotlin отличается от Java?
Kotlin предлагает лаконичный синтаксис, поддержку нулевой безопасности, корутины для асинхронного программирования и расширенные функции, такие как свойства и функции расширения, которые отсутствуют в Java. Кроме того, Kotlin полностью совместим с Java и предоставляет современную альтернативу с улучшенной выразительностью и безопасностью.
• Null-safety.
• Именованные аргументы.
• Выведение типов.
• Extension-функции.
• Локальные функции.
• Операторы перегрузки.
• Data-классы.
• Sealed-классы.
• DSL (Domain-Specific Languages).
- В чем преимущество Kotlin в Andrid-разработке? (Какие фичи нравятся)
Kotlin упрощает разработку Android-приложений благодаря своей лаконичности, современным языковым конструкциям и полной совместимости с Java. Также он предоставляет встроенную поддержку для безопасной работы с нулевыми значениями и упрощает работу с корутинами.
- Что не нравится в Kotlin?
• Высокая функциональность.
• Избыточное использование scope-функций.
• Более длительное время компиляции по сравнению с Java.
- Что такое null safety в Kotlin? Как осуществить проверку на null?
Null safety предотвращает ошибки с
null
. Проверку наnull
можно выполнить с помощью безопасного вызова?.
, оператора Элвиса?:
или оператором!!
для выброса исключения, если значениеnull
.
- Для чего используется аннотация @JvmOverloads?
Генерирует перегруженные версии функций или конструкторов с параметрами по умолчанию для улучшения совместимости с Java-кодом.
- Чем if в Kotlin отличается от if в Java?
В Kotlin
if
является выражением, возвращающим значение, что позволяет использовать его в присваивании.
- Зачем нужны Kotlin Contracts?
Kotlin contracts описывают поведение функций, уточняя их влияние на контроль потока и гарантии о параметрах или результатах, что позволяет компилятору лучше анализировать и оптимизировать код. Это помогает избежать ошибок и улучшить работу с nullable-типами.
- Как отработает функция main?
class A { lateinit var first: Model var second: Model? = null } fun main() { val a = A() a.first.get() a.second?.get() }
a.first.get()
вызовет исключениеUninitializedPropertyAccessException
, так как переменнаяfirst
имеет модификаторlateinit
, но не была инициализирована до обращения к ней.
- Что будет выведено в результате выполнения кода?
var num = 9 num++ num += 6 --num println(num)
15
- Компилируется ли этот код? Как исправить?
fun main() { foo1 { println("Hello World") return } } fun foo1(f: () -> Unit) { f() }
Код не компилируется из-за того, что оператор
return
в лямбда-функции пытается вернуть значение из функцииmain
, а не из лямбды. Чтобы исправить это, можно заменить return наreturn@foo1
, чтобы явно указать, что нужно выйти из функцииfoo1
.
- Компилируется ли этот код? Как исправить? Как исправить с помощью inline?
fun bar() { foo { println(1) return println(2) } println(3) } fun foo(fooLambda: () -> Unit) { fooLambda() }
Код не компилируется из-за того, что оператор
return
в лямбда-функции пытается вернуть значение из функцииbar
, а не изfoo
. Чтобы исправить это, нужно использоватьreturn@foo
вместо простоreturn
. Чтобы исправить код с использованиемinline
, можно сделать функциюfoo
инлайновой. Это позволит использоватьreturn
в лямбде для выхода из родительской функцииbar
. Код будет компилироваться и выполняться правильно, выводя 1 и 3, при этомreturn
в лямбде завершает выполнение функцииbar
.
- Каким будет результат функции main?
class SpecialFunction: () -> Unit { override fun invoke() { println("Invoked from an instance.") } } fun main() { try { SpecialFunction()() } catch (ex: Exception) { println("An error occurred") } }
Ответ: Invoked from an instance.
- На базе чего построен язык Kotlin?
• На базе Android Studio
• На базе языка Python
• На базе языка С++
• На базе платформы Intellij IDEA
• На базе Java Virtual Machine
- Чем Kotlin отличается от Java?