Kotlin Developer
6.22K subscribers
256 photos
8 videos
351 links
Самый топовый канал по Kotlin

По вопросам сотрудничества и рекламы: @NadikaKir

Мы на бирже: https://telega.in/c/KotlinSenior
Download Telegram
Давно в IT и хотите сменить профиль? Стремитесь попробовать что-то новое? Испытайте себя в роли Java-разработчика на практике! Популярность языка Java растёт, востребованность разработчиков — тоже. А возрастных ограничений для специалистов нет.

Бесплатный онлайн интенсив пройдёт с 31 июля по 2 августа в 19:00 по московскому времени.

Регистрация: 👉https://epic.st/sd-oR-

Сразу после регистрации пришлём гайд по профессии Java-разработчика 😉

За 3 вечера вы узнаете, в чем различия Python и Java, какие навыки нужны для трудоустройства Java-разработчику, и попробуете написать Telegram-бота на Java. В него будет «зашит» тест по Java от реального работодателя, но вы сможете модифицировать его и реализовать собственную логику.

🔝Знаниями поделится Даниил Пилипенко — основатель и директор центра подбора IT-специалистов SymbioWay.

🎁 Тем, кто будет онлайн, подарим сертификаты на скидку 10 000 рублей для обучения на образовательной платформе Skillbox. А всем, кто дойдёт до конца интенсива, — электронную книгу издательства МИФ.

Реклама. ЧОУ ДПО «Образовательные технологии «Скилбокс (Коробка навыков)», ИНН: 9704088880
Все, что вам нужно знать о Kotlin Multiplatform

Kotlin Multiplatform — это набор для разработки программного обеспечения, который значительно лучше других вариантов разработки кроссплатформенных приложений. В этом руководстве по Kotlin multiplatform вы подробно узнаете все об этом наборе.

Читать статью
List

Список — это упорядоченная коллекция. Каждое значение, помещённое в List, называется элементом, к которому можно обращаться по индексу. Индексы начинаются с "0" и заканчиваются индексом последнего элемента в списке — (list.size - 1). Список может содержать сколько угодно одинаковых элементов — дублей (в том числе null).

val trees = listOf("Сосна", "Берёза", "Дуб") // неизменяемый список
trees.add("Ясень") // ошибка

val mutableTrees = mutableListOf("Сосна", "Берёза", "Дуб") // изменяемый список
mutableTrees.add("Ясень") // всё ок

По умолчанию в Kotlin реализацией List является ArrayList, его можно создать напрямую:

val mutableTrees = ArrayList<String>()
mutableTrees.add("Ясень")
Set

Множество — это коллекция уникальных элементов.
Это означает, что Set не может содержать дублей. Обратите внимание, что null — это тоже уникальный элемент.

val trees = setOf("Сосна", "Берёза", "Дуб") // неизменяемый сет
trees.add("Ясень") // ошибка

val mutableTrees = mutableSetOf("Сосна", "Берёза", "Дуб") // изменяемый сет
mutableTrees.add("Сосна") // проигнорируется

В отличие от списка, множество не заботится о порядке элементов. Это означает, что при использовании функций, зависящих от порядка элементов, вы можете получить непредсказуемый результат. Но это зависит от реализации сета. Например, по умолчанию реализацией Set является LinkedHashSet, который сохраняет порядок вставки элементов.

val numbers = setOf(1, 2, 3, 4)  // по умолчанию LinkedHashSet
val numbersBackwards = setOf(4, 3, 2, 1)

println(numbers.first() == numbersBackwards.first()) // false
println(numbers.first() == numbersBackwards.last()) // true

Но также существует HashSet, который не сохраняет порядок вставки элементов. И LinkedHashSet, и HashSet можно создать напрямую.

val linkedHashSet = LinkedHashSet<String>()
linkedHashSet.add("Дуб")

val hashSet = HashSet<String>()
hashSet.add("Ясень")
Курс «Английский для разработчиков» Яндекс Практикума

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

Обучение построено не вокруг абстрактной теории, а вокруг рабочих ситуаций и полезных для карьеры навыков:

Стендапы. Подготовитесь обсуждать задачи, задавать вопросы и просить о помощи.
👨‍💻 Работа с заказчиками. Научитесь презентовать решения, говорить про баги и фичи.
📣 Митапы. Сможете понимать на слух доклады и выступать сами.
😎 Собеседования. Научитесь рассказывать про свой опыт, понимать вопросы и тактично переспрашивать.
👯 Неформальное общение с коллегами. Сможете рассказать о своих интересах, опыте, планах на будущее.
💻 Код-ревью. Сможете описать сделанное, дать обратную связь, тактично отстоять своё мнение.

Запишитесь на бесплатную консультацию. Кураторы определят ваш уровень языка и расскажут подробнее про обучение.
Map

Ассоциативные списки с уникальными ключами и любыми значениями (дубликаты ключей не допускаются, значения могут быть одинаковыми). Связь между ключами и значениями происходит через специальную форму вызова метода (инфиксный вызов) to.

// числа - это ключи, деревья - значения
val map = mapOf(1 to "Сосна", 2 to "Берёза", 3 to "Дуб") // неизменяемая мапа
map.put(4, "Ясень") // ошибка

val mutableMap = mutableMapOf(1 to "Сосна", 2 to "Берёза", 3 to "Дуб") // изменяемая мапа
mutableMap.put(4, "Ясень")

По умолчанию реализацией мапы является LinkedHashMap, который сохраняет порядок вставки записей. Есть ещё HashMap, которая не сохраняет порядок вставки записей. Обе реализации можно создать напрямую.

val linkedHashMap = LinkedHashMap<Int, String>()
linkedHashMap.put(1, "Дуб")

val hashMap = HashMap<Int, String>()
hashMap.put(1, "Ясень")
Промежуточные (intermediate) и терминальные (terminal) операции в Sequences

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

Операции над Sequence можно разделить на две категории: промежуточные (intermediate) и терминальные (terminal).

Промежуточные операции (intermediate) — это операции, которые возвращают новую Sequence.

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

filter(predicate: (T) -> Boolean): фильтрует элементы по заданному условию и возвращает новую Sequence

map(transform: (T) -> R): преобразует каждый элемент в новый элемент типа R и возвращает новую Sequence

sortedBy(selector: (T) -> R?): сортирует элементы по заданному ключу и возвращает новую Sequence

Терминальные операции (terminal) — это операции, которые выполняются немедленно и возвращают результат (не Sequence).

Терминальные операции могут быть вызваны только после всех промежуточных операций, так как они завершают последовательность и начинают вычисление результатов на основе всей последовательности, полученной после выполнения всех промежуточных операций. Если же терминальная операция вызывается до выполнения всех промежуточных операций, то она не будет иметь доступа к полной последовательности и вернет неполный результат. Примеры:

toList(): преобразует Sequence в список

toSet(): преобразует Sequence в множество

count(): возвращает количество элементов в Sequence

forEach(action: (T) -> Unit): выполняет действие для каждого элемента Sequence

ВАЖНО: вычисления запускаются только при вызове терминальной функции (до этого момента никаких вычислений не производится).
Kotlin: взгляд изнутри — преимущества, недостатки и особенности

Всем привет! На связи Сергей Керенцев, Android-разработчик Студии Олега Чулакова на проектах Сбера.

В данной статье мы углубимся в мир Kotlin, рассмотрим его основные преимущества, недостатки и особенности. Мы обойдем такие важные аспекты, как безопасность работы с null-значениями, гибкость типизации с помощью Generics, возможности расширения функциональности с помощью extension-функций, inline-функции, а также многое другое.

Давайте начнем наше увлекательное путешествие в мир Kotlin и раскроем его потенциал!

Читать статью
Extensions (расширения) — что это и для чего нужны?

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

Kotlin предлагает нам концепцию «Extension Function»: мы можем к любому существующему типу добавить функцию-расширение, которая будет доступна через все объекты этого типа (но будет иметь доступ только к публичным частям).

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

• Для расширения nullable типов.
• Для расширения companion object.
• Для расширения свойств существующих классов.
• Для конвертации моделей из одной в другую.
•Для расширения функционала дженериков.
Как функции расширения работают под капотом?

По своей задумке, функция расширения Kotlin — это дополнительный метод для любого объекта, даже для потенциально несуществующего (нуллабельного). Этот инструмент является прямой реализацией переопределения методов паттерна проектирования Декоратор.

ВАЖНО: В функциях расширения мы можем обращаться к любым общедоступным свойствам и методам объекта, однако не можем обращаться к свойствам и методам с модификаторами private и protected.

ВАЖНО: Функции расширения не переопределяют функции, которые уже определены в классе. Если функция расширения имеет ту же сигнатуру, что и уже имеющаяся функция класса, то компилятор просто будет игнорировать подобную функцию расширения.

Определение аналогично определению обычной функции за тем исключением, что после слова fun идет название типа, для которого определяется функция, и через точку название функции. Определим пару функций расширения к типам Int и String:

fun main() {

val hello: String = "hello world"
println(hello.wordCount('l')) // 3
println(hello.wordCount('o')) // 2
println(4.square()) // 16
println(6.square()) // 36
}

fun String.wordCount(c: Char) : Int {
var count = 0
for(n in this) {
if(n == c) count++
}
return count
}
fun Int.square(): Int {
return this * this
}

Для типа Int определена функция возведения в квадрат. В каждой функции расширения через ключевое слово this мы можем ссылаться на текущий объект того типа, для которого создается функция. Например, в функции:

fun Int.square(): Int {
return this * this
}

Через this обращаемся к тому объекту, для которого будет вызываться функция. И затем вы можем вызвать ее следующим образом:

4.square()      // 16

Для типа String определена функция wordCount, которая подсчитывает, сколько встречается определенный символ в строке.
Как extensions выглядят в Java?

Функции расширения для JVM являются static final методами.

Extensions в Kotlin не имеют прямого эквивалента в Java. Однако, компилятор Kotlin генерирует соответствующий статический метод в классе-компаньоне, который может быть вызван из Java кода в качестве статического метода этого класса.
Kotlin Multiplatform в ОС Аврора

В данной статье описана работа ОС Аврора с технологией Kotlin Multiplatform. Рассматривается метод подключения модуля Kotlin Multiplatform к приложению на Qt/QML. Для демонстрации было портировано уже существующие демо приложение "KMM RSS Reader". Проведены тесты производительности.

Читать статью
Разница между лямбда-выражением и анонимной функцией

1. Синтаксис

Лямбда-выражения определяются заключением их в фигурные скобки в виде { параметры -> тело }.
Анонимные функции определяются через ключевое слово fun как обычные функции, хотя не имеют имени.

2. Поведение оператора return без метки

• В лямбда-выражении использование оператора return без метки приводит к возврату из обрамляющей (внешней) функции, а не из самого лямбда-выражения (т.е. полностью завершает работу этой функции и код, указанный после оператора return никогда не выполнится). Это называется нелокальным возвратом (non-local return), и может иметь неожиданное поведение и привести к ошибкам. В лямбда-выражениях рекомендуется использовать метки для явного указания точки возврата.

• В анонимной функции return без метки приводит к возврату только из самой анонимной функции (а не из внешней функции), продолжая выполнение кода после вызова анонимной функции в обрамляющей функции. Анонимные функции ведут себя как ожидается для классических функций с явным оператором return.

Таким образом, поведение оператора return без метки различается только в том, как завершается выполнение функции, в которой вызывается выражение. В лямбда-выражении, вызывающая функция завершается полностью, а в анонимной функции только сама анонимная функция.

3. Поведение оператора return с меткой

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

• Если использовать return@label в лямбда-выражении, то возврат будет осуществляться из конкретной лямбды, к которой применена метка. Вместо нелокального возврата, который происходит при использовании return без метки, return с меткой завершит только ту лямбду, которая соответствует указанной метке, и выполнение кода продолжится после этой лямбды во внешней функции. Метка позволяет читать и понимать код проще, так как явно указывает, откуда происходит возврат.

• В анонимных функциях return без метки уже осуществляет возврат из самой анонимной функции. Однако, при использовании метки return@label вы также можете контролировать возврат из анонимной функции в сложных сценариях (например, при работе с несколькими вложенными функциями).

В обоих случаях использование оператора return с меткой показывает точку возврата и делает код более явным и контролируемым.
Ну, Тинькофф как всегда: предлагает крутые условия для опытных ИТ-специалистов. Будут масштабные финтех-задачи, продукты для 30 млн пользователей и команда единомышленников.

А работать можно прямо там, где живете: у Тинькофф 25 ИТ-хабов в городах России, в Армении, Беларуси и Казахстане. Детали тут
Что такое функциональный тип, какие у него ограничения?

Язык Kotlin допускает объявлять тип анонимных функций или лямбда выражений — функциональный.

Функциональный тип — это тип данных, который позволяет работать с функциями как с обычными объектами, передавать функции в качестве аргументов и возвращать их из функций. Синтаксис функционального типа в Котлин представлен списком типов параметров, разделенных запятой, затем оператором -> и типом возвращаемого значения функции.

Пример функционального типа: (a: Int, b: Int) -> Int
Здесь функциональный тип описывает функцию с двумя параметрами типа Int и возвращаемым значением типа Int.

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

Ограничения функционального типа:

• Тип передаваемой функции должен быть определен явно, чтобы компилятор мог проверить типы аргументов и возвращаемых значений.
• Функциональный тип может содержать только один тип возвращаемого значения.
• Функциональный тип не может содержать более 22 параметров из-за ограничения JVM.
• Функциональный тип не поддерживает неявные преобразования типов.

Несмотря на эти ограничения, функциональные типы позволяют обрабатывать функции как объекты, что повышает гибкость и выразительность кода. Пример определения функционального типа:

// определение функционального типа
typealias Operation = (Int, Int) -> Int

// использование функционального типа
fun calculate(op: Operation, a: Int, b: Int): Int {
return op(a, b)
}

// пример вызова функции calculate
val sum: Operation = { x, y -> x + y }
calculate(sum, 10, 5) // результат: 15

Код из примера определяет функциональный тип Operation, который представляет собой функцию, принимающую два аргумента типа Int и возвращающую значение типа Int. Затем создается функция calculate, которая принимает три параметра: функцию op типа Operation и два аргумента типа Int. Внутри функции calculate вызывается переданная функция op с переданными аргументами a и b, и результат возвращается из функции calculate. В конце кода создается переменная sum, которая содержит лямбда-выражение, реализующее операцию сложения. Далее вызывается функция calculate с параметрами sum, 10 и 5, что приводит к вызову функции sum с аргументами 10 и 5, и результатом является число 15.
Как работают SAM-conversions?

Single Abstract Method (SAM) интерфейсы — это интерфейсы только с одним абстрактным методом (функциональные интерфейсы). Kotlin поддерживает соглашение SAM — автоматическую конвертацию функций и lambda между Kotlin и Java.

SAM-conversions позволяют использовать Java-интерфейсы с единственным абстрактным методом в Kotlin, как если бы это были функциональные типы. В Kotlin вы можете использовать такие интерфейсы для создания лямбда-выражений без явного определения функционального типа.

При использовании интерфейса с единственным абстрактным методом в качестве функционального интерфейса в Java, вы можете передавать его экземпляры вместо лямбда-выражений. Это тоже возможно в Kotlin, но на самом деле Kotlin предоставляет более простой синтаксис для этого. Когда вам нужно использовать функциональный интерфейс в Kotlin, вы можете передать lambda-выражение, которое соответствует сигнатуре единственного метода интерфейса, вместо экземпляра интерфейса. Компилятор сам преобразует лямбда-выражение в экземпляр интерфейса, используя функцию-расширение метода invoke интерфейса. Пример:

interface OnClickListener {
fun onClick(view: View)
}

class Button {
fun setOnClickListener(listener: OnClickListener) {
// ...
}
}

val button = Button()
button.setOnClickListener { view ->
// обработка нажатия кнопки
}

В этом примере мы определяем интерфейс OnClickListener с единственным абстрактным методом onClick. Затем мы создаем класс Button, который может иметь слушатель, реализующий данный интерфейс. После этого мы создаем экземпляр Button и передаем лямбда-выражение с соответствующей сигнатурой в качестве слушателя. Компилятор автоматически преобразует это лямбда-выражение в экземпляр интерфейса OnClickListener, используя функцию-расширение invoke интерфейса.
Koin: Простой и легковесный фреймворк для внедрения зависимостей

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

В то же время, хотя Spring Framework и является широко распространенным выбором, он далеко не всем подходит. Некоторым было бы предпочтительнее использовать более простые и легковесные фреймворки с продвинутой поддержкой асинхронных операций ввода-вывода. Другие были бы признательны за статическое разрешение зависимостей для более быстрого запуска приложения.

Читать статью
Указатели на функции (Function references, Bound callable references)

В языке Kotlin есть возможность работать с функциями как с объектами. Функции можно сохранять в переменные, передавать как аргументы и возвращать из других функций. Для этого можно использовать функциональные ссылки (Function references), которые представляют собой указатель на функцию.

1. Function references

Синтаксис функциональной ссылки имеет следующий вид:
::function_name. Указатели на функции представляют собой сокращенную форму записи вызова функции. Вместо того, чтобы объявлять лямбда-выражение и передавать его как аргумент функции, можно использовать ссылку на существующий метод. Например, у нас есть класс Person с методом getName():

class Person(val name: String) {
fun getName(): String = name
}

Тогда мы можем использовать указатель на метод getName() вместо лямбда-выражения:

val persons = listOf(Person("Alice"), Person("Bob"))
val names = persons.map(Person::getName)

2. Bound callable references

Bound callable references (привязанные ссылки)
— это то же самое понятие, что и указатели на методы, но в случае, когда метод вызывается на экземпляре класса. В этом варианте мы можем использовать ссылку на метод, связанную с конкретным экземпляром класса. Для создания привязанной ссылки на метод используется следующий синтаксис: <object_name>::<method_name>.

Допустим, что у нас есть экземпляр класса person типа Person. Тогда мы можем использовать ссылку на метод getName() для получения его имени:

val person = Person("Alice")
val name = person::getName

Здесь name будет ссылаться на метод getName() объекта person.
Получи преимущество на рынке, программируя Event Sourcing системы, особенно популярные на западе

Целишься на позиции senior или architect? Хочешь знать больше, чем твои коллеги? Этот курс для тебя!

Разберешься в event-driven системах
Узнаешь Event sourcing, паттерн CQRS лучше, чем 99.9% разработчиков
Пройдешь путь создания Kafka, узнаешь best practice для RabbitMQ и как масштабировать свои системы
Твердо поймешь основы Domain-driven design, как его применять
Глубоко залезешь в паттерн Saga и реализуешь распределенную транзакцию, все это на Kotlin
Будешь уверен в себе на system design интервью

Авторский курс от преподавателя Санкт-Петербургского университета ИТМО и разработчика высоконагруженных систем Андрея Суховицкого.

Твоими менторами станут ребята из Амазон, Револют, Яндекс или Озон.

❗️Старт курса - 11 сентября, лекции и задания доступны уже сейчас! ❗️
Промокод KOTLIN -10% до 11 сентября

Переходи по ссылке и начинай учиться!
Задачи про PEG-парсеры

Когда-то я хотел сделать контест по парсингу для Codeforces. Придумал задания двух типов:

1. Дается неформальное описание языка, по которому нужно создать грамматику (например, "язык с правильными скобочными последовательностями")

2. Даны примеры строк в языке, по которым нужно восстановить грамматику

У обоих типов заданий есть свои проблемы, так что контест я не сделал.

В итоге я сделал игру программу, в которой можно решать задания второго типа, при этом проверять строки на принадлежность угадываемому языку.

Читать статью