Кратко о Nothing
Nothing является типом, который полезен при объявлении функции, которая ничего не возвращает и не завершается.
Примеры:
• функция, которая выбрасывает exception или в которой запущен бесконечный цикл;
• функция TODO() — public inline fun TODO(): Nothing = throw NotImplementedError();
• в тестах есть функция с именем fail, которая выдает исключение с определенным сообщением:
Nothing является типом, который полезен при объявлении функции, которая ничего не возвращает и не завершается.
Примеры:
• функция, которая выбрасывает exception или в которой запущен бесконечный цикл;
• функция TODO() — public inline fun TODO(): Nothing = throw NotImplementedError();
• в тестах есть функция с именем fail, которая выдает исключение с определенным сообщением:
fun fail(message: String): Nothing {
throw IllegalStateException(message)
}
Назовите подтип всех типов в Kotlin
Nothing в Kotlin — это т.н. bottom type, то есть он является подтипом любого другого типа. Наличие Nothing в системе типов позволяет типизировано выражать то, что без него принципиально невозможно.
Bottom type — это тип, который не имеет значений и предназначен для обозначения невыполнимых ситуаций в программе.
Nothing в Kotlin — это т.н. bottom type, то есть он является подтипом любого другого типа. Наличие Nothing в системе типов позволяет типизировано выражать то, что без него принципиально невозможно.
Bottom type — это тип, который не имеет значений и предназначен для обозначения невыполнимых ситуаций в программе.
Сколько существует instance Nothing (0)?
Nothing — класс, который является наследником любого класса в Kotlin, даже класса с модификатором final. При этом Nothing нельзя создать — у него приватный конструктор. В коде он объявлен так:
Nothing — класс, который является наследником любого класса в Kotlin, даже класса с модификатором final. При этом Nothing нельзя создать — у него приватный конструктор. В коде он объявлен так:
public class Nothing private constructor()
Есть ли аналог Nothing в Java (нет)?
Тип Nothing является особенным, поскольку в Java ему нет аналогов.
Действительно, каждый ссылочный тип Java, включая java.lang.Void, принимает в качестве значения null, а Nothing не принимает даже этого. Таким образом, этот тип не может быть точно представлен в мире Java. Вот почему Kotlin генерирует необработанный тип, в котором используется аргумент типа Nothing:
Тип Nothing является особенным, поскольку в Java ему нет аналогов.
Действительно, каждый ссылочный тип Java, включая java.lang.Void, принимает в качестве значения null, а Nothing не принимает даже этого. Таким образом, этот тип не может быть точно представлен в мире Java. Вот почему Kotlin генерирует необработанный тип, в котором используется аргумент типа Nothing:
fun emptyList(): List<Nothing> = listOf()
// is translated to
// List emptyList() { ... }
Модификаторы доступа — private, protected, internal, public
Классы, объекты, интерфейсы, конструкторы, функции, свойства и их сеттеры могут иметь модификаторы доступа. Геттеры всегда имеют ту же видимость, что и свойства, к которым они относятся. Модификаторы доступа — это ключевые слова, с помощью которых можно задать область действия данных. Они позволяют регулировать уровень доступа к различным частям кода. Локальные переменные, функции и классы не могут иметь модификаторов доступа.
В Kotlin есть четыре модификатора доступа: private, protected, internal и public.
Если модификатор явно не указан, то присваивается значение по умолчанию — public.
Private — доступ к членам класса только в пределах самого класса. То есть, поля и методы с модификатором private недоступны из других классов и даже из наследников.
Protected — доступ к членам класса только в пределах класса и его наследников. То есть, поля и методы с модификатором protected доступны из класса и его наследников, но не из других классов.
Internal — доступ к членам модуля (module). Модуль — это набор файлов, компилирующихся вместе, поэтому все классы, объявленные внутри модуля, могут иметь доступ к членам с модификатором internal.
Public — не ограничивает доступ к членам класса. Поля и методы с модификатором public доступны из любого места программы, включая другие модули.
Классы, объекты, интерфейсы, конструкторы, функции, свойства и их сеттеры могут иметь модификаторы доступа. Геттеры всегда имеют ту же видимость, что и свойства, к которым они относятся. Модификаторы доступа — это ключевые слова, с помощью которых можно задать область действия данных. Они позволяют регулировать уровень доступа к различным частям кода. Локальные переменные, функции и классы не могут иметь модификаторов доступа.
В Kotlin есть четыре модификатора доступа: private, protected, internal и public.
Если модификатор явно не указан, то присваивается значение по умолчанию — public.
Private — доступ к членам класса только в пределах самого класса. То есть, поля и методы с модификатором private недоступны из других классов и даже из наследников.
Protected — доступ к членам класса только в пределах класса и его наследников. То есть, поля и методы с модификатором protected доступны из класса и его наследников, но не из других классов.
Internal — доступ к членам модуля (module). Модуль — это набор файлов, компилирующихся вместе, поэтому все классы, объявленные внутри модуля, могут иметь доступ к членам с модификатором internal.
Public — не ограничивает доступ к членам класса. Поля и методы с модификатором public доступны из любого места программы, включая другие модули.
Каким будет результат выполнения следующего кода?
Anonymous Quiz
17%
Улыбающееся лицо
36%
Грустное лицо
38%
Ошибка компиляции - нельзя перезаписывать встроенные функции (IllegalOverrideException)
9%
Ошибка выполнения - переполнение стека (StackOverflowException)
Разница между var, val, const val
1. var — это изменяемая переменная. После инициализации мы можем изменять данные, хранящиеся в переменной.
Переменные val и const val доступны только для чтения — это неизменяемые переменные.
2. val — константа времени выполнения, т.е. значение можно назначить во время выполнения программы.
3. const val — константа времени компиляции, т.к. значения константам присваивается при компиляции (в момент, когда программа компилируется).
В отличие от val, значение const val должно быть известно во время компиляции.
Особенности const val:
• могут получать значение только базовых типов: Int, Double, Float, Long, Short, Byte, Char, String, Boolean.
• объявляются в глобальной области видимости, то есть за пределами функции main() или любой другой функции.
• нет пользовательского геттера.
1. var — это изменяемая переменная. После инициализации мы можем изменять данные, хранящиеся в переменной.
Переменные val и const val доступны только для чтения — это неизменяемые переменные.
2. val — константа времени выполнения, т.е. значение можно назначить во время выполнения программы.
3. const val — константа времени компиляции, т.к. значения константам присваивается при компиляции (в момент, когда программа компилируется).
В отличие от val, значение const val должно быть известно во время компиляции.
Особенности const val:
• могут получать значение только базовых типов: Int, Double, Float, Long, Short, Byte, Char, String, Boolean.
• объявляются в глобальной области видимости, то есть за пределами функции main() или любой другой функции.
• нет пользовательского геттера.
Как стоит объявлять свои константы в Kotlin — при помощи companion object или вне класса?
На самом деле оба эти подхода приемлемы. Однако, использование companion object может быть излишним: компилятор Kotlin преобразует companion object во вложенный класс. Слишком много кода для простой константы.
Если вам не требуется поведение, специфичное для companion object, объявляйте константы вне класса, так как это будет способствовать более эффективному байт-коду. Да и сам синтаксис объявления констант вне класса более чистый и читабельный.
На самом деле оба эти подхода приемлемы. Однако, использование companion object может быть излишним: компилятор Kotlin преобразует companion object во вложенный класс. Слишком много кода для простой константы.
Если вам не требуется поведение, специфичное для companion object, объявляйте константы вне класса, так как это будет способствовать более эффективному байт-коду. Да и сам синтаксис объявления констант вне класса более чистый и читабельный.
Как создать анимированные шейдеры в Jetpack Compose
Jetpack Compose — молодой, но бурно развивающийся фреймворк для разработки под Android, который обладает множеством не всегда очевидных фичей. Сегодня я хотел бы описать одну из таких встроенных возможностей: речь идет об использовании OpenGL-шейдеров. Они позволяют делать красивые анимированные интерфейсы.
Читать статью
Jetpack Compose — молодой, но бурно развивающийся фреймворк для разработки под Android, который обладает множеством не всегда очевидных фичей. Сегодня я хотел бы описать одну из таких встроенных возможностей: речь идет об использовании OpenGL-шейдеров. Они позволяют делать красивые анимированные интерфейсы.
Читать статью
Свойства, методы get и set
Свойства класса — это переменные, которые хранят состояние объекта класса. Как и любая переменная, свойство может иметь тип, имя и значение.
В классе можно объявить свойства с помощью ключевого слова var или val. Свойства, объявленные с var, могут быть изменены после их инициализации, а свойства, объявленные с val, только для чтения.
Если get и set методы не были созданы вручную, то для таких свойств Kotlin незаметно сам их генерирует. При этом для свойства, объявленного с val, генерируется get-метод, а для свойства, объявленного с var — и get, и set методы.
Свойства класса — это переменные, которые хранят состояние объекта класса. Как и любая переменная, свойство может иметь тип, имя и значение.
В классе можно объявить свойства с помощью ключевого слова var или val. Свойства, объявленные с var, могут быть изменены после их инициализации, а свойства, объявленные с val, только для чтения.
class Person {При создании своего класса мы хотим сами управлять его свойствами, контролируя то, какие данные могут быть предоставлены или перезаписаны. С этой целью создаются get и set методы (геттеры и сеттеры). Цель get-метода — вернуть значение, а set-метода — записать полученное значение в свойство класса.
var name: String = ""
val age: Int = 0
}
var name: String = ""В данном примере свойство name имеет тип String и начальное значение пустой строки. Геттер возвращает значение свойства, преобразованное к верхнему регистру. Сеттер устанавливает значение свойства с добавлением префикса "Name: " перед переданным значением. Слово field используется для обращения к текущему значению свойства.
get() = field.toUpperCase()
set(value) {
field = "Name: $value"
}
Если get и set методы не были созданы вручную, то для таких свойств Kotlin незаметно сам их генерирует. При этом для свойства, объявленного с val, генерируется get-метод, а для свойства, объявленного с var — и get, и set методы.
В чем отличие field от property?
В Kotlin свойство (property) — это абстракция над полями (fields), которая позволяет обращаться к значению переменной через методы геттера и сеттера, вместо прямого доступа к полю.
Field — это переменная, которая содержит значение и может быть доступна напрямую или через геттер/сеттер.
Пример определения свойства с геттером и сеттером в классе:
В Kotlin свойство (property) — это абстракция над полями (fields), которая позволяет обращаться к значению переменной через методы геттера и сеттера, вместо прямого доступа к полю.
Field — это переменная, которая содержит значение и может быть доступна напрямую или через геттер/сеттер.
Пример определения свойства с геттером и сеттером в классе:
class Person {В данном примере свойство name содержит поле, которое может быть доступно напрямую только внутри класса, и методы геттера и сеттера, которые позволяют получать и изменять значение свойства через специальные методы.
var name: String = ""
get() = field.toUpperCase() // возвращает значение поля name в верхнем регистре
set(value) {
field = value.trim() // устанавливает значение поля name без начальных и конечных пробелов
}
}
Что такое делегированные свойства (Delegated properties)?
Делегированные свойства (Delegated properties) — это свойства, которые не хранят своё значение напрямую, а делегируют это значение другому объекту, который реализует интерфейс Delegate. При доступе к свойству, его значение запрашивается у делегата, который может выполнить какую-то дополнительную логику, а затем вернуть требуемое значение. Пример:
Синтаксис выглядит так: val/var <имя свойства>: <Тип> by <выражение>. Выражение после by — делегат, потому что обращения get(), set() к свойству будут делегированы его методам getValue() и setValue(). Делегат не обязан реализовывать какой-то интерфейс, достаточно, чтобы у него были метод getValue() (и setValue() для var'ов) с определённой сигнатурой.
В Kotlin существуют несколько встроенных делегатов для работы с делегированными свойствами:
• lazy() — позволяет создавать лениво инициализированные свойства
• observable() — позволяет реагировать на изменения свойства
• vetoable() — позволяет отклонять изменения значения свойства на основе заданного условия
• notNull() — гарантирует, что свойство не будет иметь значение null
• map() — позволяет хранить значения свойств в словаре (Map)
Кроме того, в Kotlin можно создавать свои собственные делегаты, реализуя интерфейс ReadOnlyProperty или ReadWriteProperty. Это дает возможность создавать кастомные поведения для свойств, например, кеширование значений или логирование операций чтения/записи.
Делегированные свойства (Delegated properties) — это свойства, которые не хранят своё значение напрямую, а делегируют это значение другому объекту, который реализует интерфейс Delegate. При доступе к свойству, его значение запрашивается у делегата, который может выполнить какую-то дополнительную логику, а затем вернуть требуемое значение. Пример:
class Example {Ключевое слово by используется для обозначения свойств, методы чтения и записи которых реализованы другим объектом, который называют делегатом.
var p: String by Delegate()
}
Синтаксис выглядит так: val/var <имя свойства>: <Тип> by <выражение>. Выражение после by — делегат, потому что обращения get(), set() к свойству будут делегированы его методам getValue() и setValue(). Делегат не обязан реализовывать какой-то интерфейс, достаточно, чтобы у него были метод getValue() (и setValue() для var'ов) с определённой сигнатурой.
В Kotlin существуют несколько встроенных делегатов для работы с делегированными свойствами:
• lazy() — позволяет создавать лениво инициализированные свойства
• observable() — позволяет реагировать на изменения свойства
• vetoable() — позволяет отклонять изменения значения свойства на основе заданного условия
• notNull() — гарантирует, что свойство не будет иметь значение null
• map() — позволяет хранить значения свойств в словаре (Map)
Кроме того, в Kotlin можно создавать свои собственные делегаты, реализуя интерфейс ReadOnlyProperty или ReadWriteProperty. Это дает возможность создавать кастомные поведения для свойств, например, кеширование значений или логирование операций чтения/записи.
Блок инициализации (init блок)
Основной конструктор не может в себе содержать какую-либо логику по инициализации свойств (исполняемый код). Он предназначен исключительно для объявления свойств и присвоения им полученных значений. Поэтому вся логика может быть помещена в блок инициализации — блок кода, обязательно выполняемый при создании объекта независимо от того, с помощью какого конструктора этот объект создаётся. Помечается он словом init.
Основной конструктор не может в себе содержать какую-либо логику по инициализации свойств (исполняемый код). Он предназначен исключительно для объявления свойств и присвоения им полученных значений. Поэтому вся логика может быть помещена в блок инициализации — блок кода, обязательно выполняемый при создании объекта независимо от того, с помощью какого конструктора этот объект создаётся. Помечается он словом init.
class Person(val name: String, var age: Int) {По сути блок инициализации — это способ настроить переменные или значения, а также проверить, что были переданы допустимые параметры. Код в блоке инициализации выполняется сразу после создания экземпляра класса, т.е. сразу после вызова основного конструктора. В классе может быть один или несколько блоков инициализации и выполняться они будут последовательно.
var id: Int = 0
// require выдает ошибку с указанным текстом, если условие в левой части false
init {
require(name.isNotBlank(), { "У человека должно быть имя!" })
require(age > -1, { "Возраст не может быть отрицательным." })
}
constructor(name: String, age: Int, id: Int) : this(name, age) {
if (id > 0) this.id = id * 2
}
}
class Person(val name: String, var age: Int) {
// сначала вызывается основной конструктор и создаются свойства класса
// далее вызывается первый блок инициализации
init {
...
}
// после первого вызывается второй блок инициализации
init {Блок инициализации может быть добавлен, даже если у класса нет основного конструктора. В этом случае его код будет выполнен раньше кода вторичных конструкторов.
...
}
// и т.д.
}
Расскажите о Data классах. Какие преимущества они имеют?
Data класс предназначен исключительно для хранения каких-либо данных.
Основное преимущество: для параметров, переданных в основном конструкторе автоматически будут переопределены методы toString(), equals(), hashCode(), copy().
Также для каждой переменной, объявленной в основном конструкторе, автоматически генерируются функции componentN(), где N — номер позиции переменной в конструкторе.
Благодаря наличию вышеперечисленных функций внутри data класса мы исключаем написание шаблонного кода.
Data класс предназначен исключительно для хранения каких-либо данных.
Основное преимущество: для параметров, переданных в основном конструкторе автоматически будут переопределены методы toString(), equals(), hashCode(), copy().
Также для каждой переменной, объявленной в основном конструкторе, автоматически генерируются функции componentN(), где N — номер позиции переменной в конструкторе.
Благодаря наличию вышеперечисленных функций внутри data класса мы исключаем написание шаблонного кода.
Пару слов о полях и свойствах в Kotlin
Терминология свойств и полей в Kotlin может немного сбивать с толку, потому что технически в Kotlin нет полей. Вы не можете объявить поле. Все — свойства! Однако, во избежании путаницы, я предпочитаю разделять определения полей и свойств на следующей основе — полями являются приватные переменные-члены класса. Это то, для чего выделена память. Свойствами являются публичные или защищенные (protected) функциями геттеры и сеттеры, которые позволяют вам получить доступ к приватным полям.
Я считаю хорошей идеей разграничивать эти понятия таким образом, потому что это способствует моему пониманию, а также упрощает объяснение связанных с этим вещей.
Читать статью
Терминология свойств и полей в Kotlin может немного сбивать с толку, потому что технически в Kotlin нет полей. Вы не можете объявить поле. Все — свойства! Однако, во избежании путаницы, я предпочитаю разделять определения полей и свойств на следующей основе — полями являются приватные переменные-члены класса. Это то, для чего выделена память. Свойствами являются публичные или защищенные (protected) функциями геттеры и сеттеры, которые позволяют вам получить доступ к приватным полям.
Я считаю хорошей идеей разграничивать эти понятия таким образом, потому что это способствует моему пониманию, а также упрощает объяснение связанных с этим вещей.
Читать статью
Teletype
Пару слов о полях и свойствах в Kotlin
Терминология свойств и полей в Kotlin может немного сбивать с толку, потому что технически в Kotlin нет полей. Вы не можете объявить...
Что такое мульти-декларации (destructuring declarations)?
Мульти-декларации (destructuring declarations или деструктуризирующее присваивание) — это способ извлечения значений из объекта и присвоения их сразу нескольким переменным. В Kotlin этот механизм поддерживается с помощью оператора распаковки (destructuring operator) — componentN(), где N — номер компонента.
При создании data класса Kotlin автоматически создает функции componentN() для каждого свойства класса, где N — номер позиции переменной в конструкторе. Функции componentN() возвращают значения свойств в порядке их объявления в конструкторе. Это позволяет использовать мульти-декларации для распаковки значений свойств и присваивания их отдельным переменным.
Например, если у нас есть data класс Person с двумя свойствами name и age, мы можем использовать мульти-декларации, чтобы извлечь эти свойства и присвоить их двум переменным:
Мульти-декларации (destructuring declarations или деструктуризирующее присваивание) — это способ извлечения значений из объекта и присвоения их сразу нескольким переменным. В Kotlin этот механизм поддерживается с помощью оператора распаковки (destructuring operator) — componentN(), где N — номер компонента.
При создании data класса Kotlin автоматически создает функции componentN() для каждого свойства класса, где N — номер позиции переменной в конструкторе. Функции componentN() возвращают значения свойств в порядке их объявления в конструкторе. Это позволяет использовать мульти-декларации для распаковки значений свойств и присваивания их отдельным переменным.
Например, если у нас есть data класс Person с двумя свойствами name и age, мы можем использовать мульти-декларации, чтобы извлечь эти свойства и присвоить их двум переменным:
data class Person(val name: String, val age: Int)Также можно использовать мульти-декларации в циклах, чтобы итерироваться по спискам объектов и распаковывать значения свойств:
val person = Person("Alice", 29)
val (name, age) = person
println(name) // Alice
println(age) // 29
val people = listOf(Person("Alice", 30), Person("Bob", 40))Мульти-декларации также могут быть использованы с массивами и другими коллекциями:
for ((name, age) in people) {
println("$name is $age years old")
}
// Alice is 30 years old
// Bob is 40 years old
val list = listOf("apple", "banana", "orange")
val (first, second, third) = list
println(first) // apple
println(second) // banana
println(third) // orange
Что делает функция componentN()?
Функция componentN() возвращает значение переменной и позволяет обращаться к свойствам объекта класса по их порядковому номеру. Генерируется автоматически только для data классов.
Также функцию componentN() можно создать самому для класса, который не является data классом.
Функция componentN() возвращает значение переменной и позволяет обращаться к свойствам объекта класса по их порядковому номеру. Генерируется автоматически только для data классов.
Также функцию componentN() можно создать самому для класса, который не является data классом.
class Person(val firstName: String, val lastName: String, val age: Int) {Теперь можно использовать мульти-декларации для класса Person:
operator fun component1() = firstName
operator fun component2() = lastName
operator fun component3() = age
}
val person = Person("John", "Doe", 30)В данном примере мы определили функции component1(), component2() и component3() как операторы с ключевым словом operator. Они возвращают значения свойств firstName, lastName и age соответственно. После этого мы можем использовать мульти-декларации для разбивки объекта Person на отдельные переменные.
val (firstName, lastName, age) = person
println("$firstName $lastName is $age years old.")
Какие требования должны быть соблюдены для создания data класса?
Класс должен иметь хотя бы одно свойство, объявленное в основном конструкторе.
Все параметры основного конструктора должны быть отмечены val или var (рекомендуется val).
Классы данных не могут быть abstract, open, sealed или inner.
Класс должен иметь хотя бы одно свойство, объявленное в основном конструкторе.
Все параметры основного конструктора должны быть отмечены val или var (рекомендуется val).
Классы данных не могут быть abstract, open, sealed или inner.
Kotlin вместо bash. Прокачиваем автоматизацию на сервере
Для решения задач автоматизации рутинных процессов для системных администраторов и DevOps чаще всего используются или bash-сценарии или python. Первое решение косвенно используется и в описании Dockerfile, поскольку сценарий исполняемых команд принципиально ничем не отличается от запуска скрипта в какой-либо shell, второй подход чаще ассоциируется с автоматизацией, связанных с взаимодействием с хранилищами данных. Но несправедливо было бы обойти стороной возможность создания исполняемых сценариев на языке Kotlin, которые могут стать полноценной заменой bash-сценариям.
В этой статье мы рассмотрим несколько примеров использования Kotlin Scripting (KTS) для автоматизации в распределенной системе, будем использовать долгоживущие скрипты с ожиданием заданий через RabbitMQ, а также поработаем с файловой системой, внешними сервисами, а также попробуем использовать KTS для сборки Docker-контейнеров.
Читать статью
Для решения задач автоматизации рутинных процессов для системных администраторов и DevOps чаще всего используются или bash-сценарии или python. Первое решение косвенно используется и в описании Dockerfile, поскольку сценарий исполняемых команд принципиально ничем не отличается от запуска скрипта в какой-либо shell, второй подход чаще ассоциируется с автоматизацией, связанных с взаимодействием с хранилищами данных. Но несправедливо было бы обойти стороной возможность создания исполняемых сценариев на языке Kotlin, которые могут стать полноценной заменой bash-сценариям.
В этой статье мы рассмотрим несколько примеров использования Kotlin Scripting (KTS) для автоматизации в распределенной системе, будем использовать долгоживущие скрипты с ожиданием заданий через RabbitMQ, а также поработаем с файловой системой, внешними сервисами, а также попробуем использовать KTS для сборки Docker-контейнеров.
Читать статью
Teletype
Kotlin вместо bash. Прокачиваем автоматизацию на сервере
Для решения задач автоматизации рутинных процессов для системных администраторов и DevOps (которые, кроме всего прочего, нередко...
Можно ли наследоваться от data класса?
От data класса нельзя наследоваться т.к. он является final классом, но он может наследоваться от других классов.
От data класса нельзя наследоваться т.к. он является final классом, но он может наследоваться от других классов.