Сравнение Java и Kotlin
Дальше: совместимость Kotlin и Java · ООП в Kotlin · корутины · Java — о разделе
Java и Kotlin — два объектно-ориентированных языка для виртуальной машины Java (JVM). Оба компилируются в байт-код (промежуточный код, который исполняет JVM), поэтому в одном Gradle- или Maven-проекте файлы .java и .kt собираются вместе, а классы вызывают друг друга напрямую. Подробнее о цепочке "исходник → байт-код → выполнение" — байт-код и JVM.
Java появился в 1995 году и десятилетиями оставался главным языком enterprise, Android и Big Data. Kotlin разрабатывает JetBrains с 2010 года; с 2017-го Google официально поддерживает его на Android, с 2019-го Kotlin — приоритетный язык для новых мобильных проектов. Обзор разделов — Java, Kotlin, хронология — история Kotlin.
Сначала: типизация и ООП — общая база без привязки к синтаксису. Вызовы между языками в одном проекте — совместимость на практике.
Словарь терминов
| Термин | Простыми словами |
|---|---|
| JVM | Программа, которая запускает скомпилированный Java/Kotlin-код на любой ОС. Один и тот же .class-файл работает на Windows, Linux и macOS. |
| Байт-код | Результат компиляции .java или .kt; JVM переводит его в машинные инструкции. См. выполнение кода. |
| JAR | Архив из .class-файлов и ресурсов; единица деплоя Java/Kotlin-приложения. |
| Gradle / Maven | Системы сборки: скачивают зависимости, компилируют, запускают тесты. Kotlin подключают в тот же проект, что и Java — первая программа. |
| POJO | Plain Old Java Object — обычный класс с полями, конструктором, геттерами; типичная "модель данных" в Java. |
| DTO | Data Transfer Object — объект для передачи данных между слоями (API, БД, UI); в Kotlin чаще data class. |
| Boilerplate | Повторяющийся шаблонный код (геттеры, equals, конструкторы), который не несёт бизнес-смысла. |
Null / null | Специальное значение "ссылка ни на что". Вызов метода у null даёт NPE (NullPointerException). |
| NPE | NullPointerException — падение программы при обращении к null. Теория — ошибки и исключения. |
| Null-safety | Правила языка, которые отсекают часть NPE ещё при компиляции. В Kotlin — через String и String?. |
| Interop | Совместная работа двух языков в одном проекте — статья. |
Platform type (T!) | Тип из Java без null-аннотаций; Kotlin не знает, может ли значение быть null. |
| Корутина | Легковесная задача в Kotlin; код с suspend выглядит последовательным — корутины. |
| Virtual thread | "Лёгкий" поток JVM с Java 21 (Project Loom); тысячи задач без ручного пула — асинхронность Java. |
| LTS | Long-Term Support — версия с долгой поддержкой для продакшена (Java 21, Java 25). |
| KMP | Kotlin Multiplatform — общий код для Android, iOS, Desktop — Compose Multiplatform. |
Общее наследие
| Java | Kotlin | |
|---|---|---|
| Первый публичный релиз | 1995 (JDK 1.0) | 2016 (Kotlin 1.0) |
| Создатель | Sun Microsystems (Джеймс Гослинг) | JetBrains (Андрей Бреслав) |
| Целевая платформа (изначально) | JVM | JVM; позже JavaScript, Native, WebAssembly |
| Лицензия | OpenJDK (GPL+Classpath) | Apache 2.0 |
| Именование | Остров Ява | Остров Котлин |
| Статус на Android | Поддерживается для старого кода | Приоритетный язык Google с 2019 года |
| Актуальные версии (2026) | Java 21 LTS в продакшене, Java 25 | Kotlin 2.x, компилятор K2 |
В enterprise-монолитах и Big Data по-прежнему доминирует Java. В новом коде Android и во многих JVM-сервисах чаще пишут на Kotlin. Типичная схема команды — оба языка в одном репозитории: legacy на Java, новые модули на Kotlin.
Как устроена связь на JVM
Исходники .java Исходники .kt
│ │
▼ ▼
javac (компилятор JDK) kotlinc (компилятор K2)
│ │
└──────────┬─────────────┘
▼
Байт-код (.class)
│
▼
JVM (HotSpot и др.)
│
┌──────────┴──────────┐
▼ ▼
Spring Boot, Spark Android, Compose
Ktor, Hibernate Room, KSP
Компилятор (javac или kotlinc) переводит текст программы в байт-код. JVM исполняет этот байт-код. Язык исходника для JVM не важен: Kotlin-класс и Java-класс в одном JAR вызывают друг друга как обычные методы.
| Утверждение | Верно? | Пояснение |
|---|---|---|
| Для Kotlin нужна отдельная виртуальная машина | Нет | Та же JVM, те же .class |
| Из Kotlin нельзя вызвать Java-библиотеку | Нет | Вызов прямой, без обёрток |
| Можно переводить проект по одному классу | Да | Миграция и interop |
| Kotlin всегда короче Java на 40% | Частично | На моделях данных и UI — да; на алгоритмах разница меньше |
| Kotlin-разработчику не нужна Java | Нет | Android SDK, Spring, JDBC — Java API; stack trace тоже на Java |
Сводное сравнение
| Критерий | Java | Kotlin |
|---|---|---|
| Синтаксис | Развёрнутый, в духе C; точка с запятой обязательна | Короче; точка с запятой чаще не нужна — синтаксис |
| Работа с null | Любая ссылка может быть null; защита через проверки и Optional | Типы String и String? различаются на этапе компиляции — типы |
| Шаблонный код | Геттеры, equals, конструкторы вручную или через Lombok | data class, свойства val/var — ООП |
| Расширение чужих классов | Статические утилиты (StringUtils.trim) | Extension-функции fun String.myExt() — встроенные функции |
| Коллекции | List из java.util часто изменяемый | List только для чтения, MutableList — для изменений — коллекции |
| Наследование | Класс и метод по умолчанию можно переопределить | Нужно явно пометить open, иначе класс закрыт — наследование в ООП |
| Статические члены | Ключевое слово static | companion object, аннотация @JvmStatic |
| Асинхронность | Потоки ОС, CompletableFuture, virtual threads (Java 21) | Корутины suspend, библиотека Flow — корутины, Flow |
| Модели данных | record (Java 16+), POJO, Lombok | data class с методом copy() |
| Закрытые иерархии типов | sealed (Java 17+) | sealed class, sealed interface |
| Скорость компиляции | javac обычно быстрее на огромных модулях | K2 заметно ускорил Kotlin; кэш Gradle помогает обоим |
| Скорость работы программы | Эталон для JVM-приложений | Сопоставима: тот же байт-код, те же библиотеки |
| IDE | IntelliJ IDEA, Eclipse, VS Code | IntelliJ IDEA, Android Studio |
| Типичные сферы | Enterprise, Big Data, банки, legacy Android | Новый Android, Ktor, KMP, Spring |
| Рынок специалистов | Очень широкий | Меньше в enterprise, стандарт в Android |
Возможности языка (подробнее)
| Возможность | Java | Kotlin |
|---|---|---|
| Лямбды (короткие функции) | С Java 8 — лямбды в Java | С первой версии — основы |
| Цепочки над коллекциями | Stream API stream().filter().map() | filter, map; ленивый Sequence — коллекции |
| Сопоставление с образцом | switch + instanceof (Java 21+) | when и smart cast — конструкции |
| Метод с телом в интерфейсе | default (Java 8+) | Тело метода в интерфейсе с Kotlin 1.0 |
| Наследование от двух классов | Нельзя | Нельзя |
| Перегрузка операторов | Только + для строк | operator fun |
| Делегирование поведения | Пишут вручную или через библиотеку | Ключевое слово by в классе |
| Функции вне класса | Только static внутри класса | Top-level fun в файле — fun main() |
| Подстановка в строку | Текстовые блоки """ (Java 15+) | "Привет, $name" и "${выражение}" |
Исчерпывающий when / switch | Pattern matching в switch (21+) | Компилятор требует все ветки для sealed |
| Лямбда вместо интерфейса с одним методом | SAM-конверсия (Java 8+) | То же; плюс fun interface |
| Кроссплатформенность | JVM, GraalVM Native Image | JVM, JS, Native, KMP |
| Проверяемые исключения (checked) | Обязательны в сигнатуре | Нет; для Java — @Throws — исключения |
Один сценарий на двух языках
Задача — хранить пользователя и поздороваться по имени. Если имя пустое или отсутствует, вернуть "Hello, World".
Java (класс POJO)
public class User {
private final Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public boolean equals(Object o) { /* ... */ return false; }
@Override
public int hashCode() { /* ... */ return 0; }
}
public String greet(User user) {
if (user == null || user.getName() == null) {
return "Hello, World";
}
return "Hello, " + user.getName().trim();
}
Разбор:
- Класс POJO требует конструктор, геттеры,
equalsиhashCode— это и есть boilerplate. - Без проверки
user.getName() == nullвозможен NPE в продакшене. - Класс
Optionalиз стандартной библиотеки помогает, но делает код длиннее.
С Java 16+ тот же DTO короче через record (неизменяемый класс данных):
public record User(Long id, String name) {}
record не подходит для всех случаев — например, JPA-сущности с ленивой загрузкой связей из баз данных. Подробнее — ООП в Java.
Kotlin (data class и null-safety)
data class User(val id: Long, val name: String?)
fun greet(user: User?): String {
val trimmed = user?.name?.trim()
return if (trimmed.isNullOrBlank()) "Hello, World" else "Hello, $trimmed"
}
Разбор:
data class— компилятор сам генерируетequals,hashCode,copy,toString.String?— тип, который допускаетnull; обычныйString— нет.?.(safe call) — вызвать метод только если слева неnull; иначе результат всего выраженияnull.isNullOrBlank()— проверяет иnull, и пустую/пробельную строку.
Дальше по теме — типы и null, ООП в Kotlin.
Ключевые различия
Работа с null
В Java любая переменная-ссылка по умолчанию может хранить null. Разработчик пишет проверки if (obj != null) или оборачивает значение в Optional<T>.
В Kotlin обычный тип String не принимает null. Для этого есть отдельная запись String?. Компилятор не даст вызвать .length у потенциально пустой ссылки без проверки.
| Ситуация | Java | Kotlin |
|---|---|---|
| Тип без null | Соглашение в команде, аннотации @NonNull | String — проверка компилятором |
| Тип с null | Любая ссылка; @Nullable | String? |
| Безопасный вызов метода | if (x != null) x.f() | x?.f() |
| Значение, если слева null | Тернарный оператор, orElse у Optional | Элвис ?:, например name ?: "unknown" |
| Принудительное "точно не null" | Риск NPE без синтаксиса | name!! — осознанный риск NPE |
| Вызов Java из Kotlin | — | Platform type T! — interop |
val len: Int = name.length // ошибка, если name: String?
val len: Int = name?.length ?: 0 // корректно
Миллионы строк Java-кода написаны без дисциплины null. Kotlin в новом коде заставляет явно помечать nullable-типы; на границе с Java остаются platform types.
Меньше шаблонного кода
| Задача | Java | Kotlin |
|---|---|---|
| Поле только для чтения | private final String name + getName() | val name: String |
| Конструктор с полями | Отдельный метод или Lombok | class User(val id: Long) в заголовке класса |
| Один экземпляр на приложение | enum-приём или holder-класс | object Config — ООП |
| Фабричный метод | public static User create() | companion object { fun create() } |
| Неизменяемый DTO | record или ручной POJO | data class |
На практике Kotlin-код моделей и UI часто короче на 20–40%; на чистых алгоритмах разница небольшая.
Extension-функции
В Kotlin можно "добавить" метод к уже существующему классу, в том числе из сторонней библиотеки:
fun String.addExclamation(): String = "$this!"
fun List<Int>.secondOrNull(): Int? = getOrNull(1)
println("Hi".addExclamation()) // Hi!
В Java ту же идею выражают статическим классом-утилитой:
public final class StringUtils {
private StringUtils() {}
public static String addExclamation(String s) {
return s + "!";
}
}
Extension-функция не меняет исходный класс и не участвует в полиморфизме: компилятор выбирает функцию по статическому типу переменной. Для разного поведения в иерархии — интерфейсы и override.
Коллекции
| Задача | Java | Kotlin |
|---|---|---|
| Список только для чтения | Collections.unmodifiableList, List.of | Интерфейс List в Kotlin — read-only view |
| Список с добавлением элементов | ArrayList, изменяемый List | Явный MutableList |
| Литерал списка | List.of(1, 2, 3) | listOf(1, 2, 3) |
| Ленивая цепочка операций | Stream | Sequence — коллекции и Sequence |
На границе языков Kotlin-List может оборачивать изменяемый Java-ArrayList — см. коллекции и мутабельность.
Асинхронность и параллелизм
Поток (thread) — задача уровня операционной системы; создание тысяч потоков дорого по памяти. Общая теория — процессы и потоки.
Java исторически опиралась на:
- потоки ОС и пулы потоков;
CompletableFuture— цепочки асинхронных вызовов без блокировки потока, но часто в стиле callback;- virtual threads (Java 21, Project Loom) — JVM создаёт много "лёгких" потоков, подходящих для I/O:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<String> f = executor.submit(() -> fetch(url));
return f.get();
}
Kotlin использует корутины — библиотека kotlinx.coroutines. Ключевое слово suspend помечает функцию, которая может приостановиться и не блокировать поток:
suspend fun loadTitle(url: String): String = coroutineScope {
async { fetch(url) }.await()
}
| Аспект | Java | Kotlin |
|---|---|---|
| Единица конкуренции | Поток JVM, virtual thread | Корутина на пуле потоков |
| Стиль кода | Callback, Future, блокирующий get() | Линейный код с suspend |
| Структурированная конкуренция | StructuredTaskScope (Java 21+) | coroutineScope, supervisorScope |
| Потоки событий / реактивность | RxJava, Project Reactor | Flow — статья |
| Android | Executor, callback-и | lifecycleScope, viewModelScope — корутины |
На backend с Java 21 virtual threads и Kotlin-корутины решают похожие задачи (много одновременных I/O-операций). Выбор чаще зависит от стека команды и библиотек, чем от разницы в скорости. Сравнение с Java — асинхронность Java.
Объектная модель
| Тема | Java | Kotlin |
|---|---|---|
Можно ли наследовать класс без open | Да, класс открыт по умолчанию | Нет, нужно open |
Можно ли переопределить метод без open | Да, метод виртуальный | Нет, нужны open и override |
| Метод в интерфейсе с реализацией | default (Java 8+) | Тело метода в интерфейсе |
| Несколько родительских классов | Только один класс | Только один класс |
| Конечный набор вариантов (UI, Result) | enum, иерархия классов | sealed class + исчерпывающий when |
| Передача работы другому объекту | Ручное делегирование | class Repo by cache : Repo |
Таблицы и примеры — ООП в Kotlin, ООП в Java, общие принципы — раздел ООП.
Где языки расходятся на практике
| Ситуация | Java | Kotlin |
|---|---|---|
| Новый экран Android | Возможен, Google рекомендует Kotlin | Jetpack Compose, корутины, KSP |
| REST на Spring | Огромная документация, де-факто стандарт | Spring Boot на Kotlin |
| Лёгкий HTTP-сервис | Spark, Javalin, Spring | Ktor |
| Big Data (Spark, Hadoop) | Основной язык API | Обычно Java или Scala |
| Скрипты сборки Gradle | Groovy DSL | Kotlin DSL — типы в скрипте |
| Банковский legacy | Миллионы строк Java | Новые фичи на Kotlin + interop |
| Общий код mobile + desktop | Отдельные стеки | Kotlin Multiplatform + Compose |
| Холодная сборка в CI | Часто быстрее | K2 и кэш Gradle сокращают разрыв |
См. также экосистема Kotlin, экосистема Java, мобильные приложения, справочник Android.
Плюсы и минусы
Java
Плюсы
- Огромная экосистема
- Spring, Hibernate, Kafka, Elasticsearch, Apache Spark
- библиотеки проверены годами в продакшене
- Широкий рынок труда
- enterprise, банки, телеком, госсектор
- Быстрая компиляция
javacна очень больших репозиториях - Понятный профиль кандидата для HR и аудита
- Новые возможности (records, sealed, virtual threads) без отказа от старого кода — версии Java
- Много учебников и курсов на любом языке
Минусы
- Много повторяющегося кода в моделях данных (POJO)
- NPE остаётся главным классом runtime-ошибок в legacy
- Новые фичи языка проходят долгий путь JCP и совместимости
equals,hashCode,toStringбез Lombok пишут вручную
Kotlin
Плюсы
- Короткий и читаемый синтаксис — основы
- Null-safety в системе типов
- Корутины и Flow для Android и сервисов — 222, 226
- Официальный язык нового Android; Compose, KSP
- Полная совместимость с Java в одном JAR — interop
- Kotlin Multiplatform для общего кода — 224
data class,sealed, extension — меньше утилитных классов
Минусы
- Компиляция на крупных проектах дольше, чем у чистого Java (K2 уменьшил разрыв)
- Меньше узких специалистов вне Android и Ktor
- Лучший опыт разработки — IntelliJ IDEA / Android Studio
- В mixed-проекте нужны interop,
@JvmStatic, platform types - KMP требует платформенных слоёв; это не "один файл на все ОС"
Даже при работе только на Kotlin придётся читать Java-документацию, stack trace и API Spring, JDBC, Android. Практика вызовов — совместимость Kotlin и Java.
Критерии выбора
Когда подходит Kotlin
- новая Android-разработка с Compose, Navigation, Room;
- новый JVM-модуль рядом с существующим Java-кодом;
- нужны корутины и линейный асинхронный код без глубокой вложенности callback;
- Ktor-сервис или продукт на KMP;
- команда уже в IntelliJ IDEA или Android Studio.
Когда подходит Java
- крупный enterprise на Spring с большим legacy;
- Big Data, Spark, Hadoop, Kafka Streams;
- нужен максимальный пул кандидатов и привычный стек для аудита;
- критична скорость инкрементальной компиляции в гигантском монорепозитории;
- команда пока не готова к двум языкам в одном репозитории.
Сводка по сферам (2026)
| Сфера | Что чаще выбирают | Комментарий |
|---|---|---|
| Android, новый код | Kotlin | Google, Compose, современные гайды |
| Android, старый код | Java + постепенный Kotlin | Читать Java, писать новое на Kotlin |
| Enterprise backend | Java; Kotlin набирает долю | Spring Boot поддерживает оба — 232 |
| Микросервис с нуля | Kotlin (Ktor) или Java (Spring) | Зависит от команды и найма |
| Big Data | Java, Scala | Kotlin в ядре стека редок |
| Обучение JVM с нуля | Java, затем Kotlin | Фундамент JVM — Java intro |
| Только мобилка | Kotlin | Чтение Java SDK всё равно понадобится |
Маршруты для новичка
| Откуда пришли | Куда идти дальше |
|---|---|
| Цель — Android | Kotlin сразу; Java API по мере задач — основы, Compose |
| Цель — enterprise backend | Java и Spring — Java intro; Kotlin для новых модулей |
| Уже знаете Java | Синтаксис Kotlin за 1–2 недели + interop |
| Уже знаете C# | Kotlin близок по свойствам и null; экосистема JVM — через Java |
Общий гид по выбору языка — Как выбрать язык программирования.
Частые заблуждения
| Миф | Как на самом деле |
|---|---|
| Kotlin полностью вытеснил Java на JVM | Java активно развивается; в enterprise оба языка |
| Kotlin быстрее Java при выполнении | Байт-код сопоставим; отличаются синтаксис и библиотеки |
| В Kotlin невозможен NPE | Возможен через !!, platform types и Java interop |
| В проекте можно только один язык | Смешанные .java и .kt — обычная практика |
| Java мёртв для Android | Legacy на Java поддерживается; новый UI — на Kotlin |
| Корутины всегда лучше virtual threads | Зависит от стека; на Java 21 Loom конкурентен для I/O |
Миграция и сосуществование
Типичные шаги команды:
- Подключить Kotlin в Gradle или Maven — первая программа.
- Новые фичи писать в
.kt; стабильный legacy оставить в.java. - Заменять POJO на
data class; сервисы — с внедрением через конструктор. - На публичной границе с Java —
@JvmStatic,@JvmOverloads— interop. - Удалять
.javaпосле зелёных тестов и ревью.
Подробный разбор аннотаций, SAM, platform types — совместимость на практике.
Чеклист понимания
- Объяснить, почему
.ktи.javaпопадают в один JAR без отдельного моста. - Написать
data classв Kotlin и эквивалент в Java (recordили POJO). - Показать разницу
StringиString?; назначение?.и?:. - Описать разницу между корутиной
suspendи virtual thread в Java 21. - Назвать три причины выбрать Kotlin для Android и три — Java для банковского монолита.
- Объяснить platform type
T!при вызове Java из Kotlin. - Перечислить пять отличий объектной модели (
open/final,data class, extension).
См. также
| Тема | Статья |
|---|---|
| Вызовы между языками, аннотации, миграция | Kotlin и Java — совместимость |
| ООП, краткая таблица отличий | ООП в Kotlin |
| Типы и null | Типы данных |
| Корутины | Корутины в Kotlin |
| Spring | Spring Boot — первая программа |
| Android | Мобильные приложения |
| Обзор Java | Java — о разделе |
| Сравнение C# и Java (тот же формат) | Сравнение C# и Java |
| История Kotlin | История языка |
| Тесты на JVM | Тестирование на Kotlin |
| Рекомендации по стилю | Рекомендации по разработке |