Перейти к основному содержимому

Сравнение 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 — первая программа.
POJOPlain Old Java Object — обычный класс с полями, конструктором, геттерами; типичная "модель данных" в Java.
DTOData Transfer Object — объект для передачи данных между слоями (API, БД, UI); в Kotlin чаще data class.
BoilerplateПовторяющийся шаблонный код (геттеры, equals, конструкторы), который не несёт бизнес-смысла.
Null / nullСпециальное значение "ссылка ни на что". Вызов метода у null даёт NPE (NullPointerException).
NPENullPointerException — падение программы при обращении к 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.
LTSLong-Term Support — версия с долгой поддержкой для продакшена (Java 21, Java 25).
KMPKotlin Multiplatform — общий код для Android, iOS, Desktop — Compose Multiplatform.

Общее наследие

JavaKotlin
Первый публичный релиз1995 (JDK 1.0)2016 (Kotlin 1.0)
СоздательSun Microsystems (Джеймс Гослинг)JetBrains (Андрей Бреслав)
Целевая платформа (изначально)JVMJVM; позже JavaScript, Native, WebAssembly
ЛицензияOpenJDK (GPL+Classpath)Apache 2.0
ИменованиеОстров ЯваОстров Котлин
Статус на AndroidПоддерживается для старого кодаПриоритетный язык Google с 2019 года
Актуальные версии (2026)Java 21 LTS в продакшене, Java 25Kotlin 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

Сводное сравнение

КритерийJavaKotlin
СинтаксисРазвёрнутый, в духе C; точка с запятой обязательнаКороче; точка с запятой чаще не нужна — синтаксис
Работа с nullЛюбая ссылка может быть null; защита через проверки и OptionalТипы String и String? различаются на этапе компиляции — типы
Шаблонный кодГеттеры, equals, конструкторы вручную или через Lombokdata class, свойства val/varООП
Расширение чужих классовСтатические утилиты (StringUtils.trim)Extension-функции fun String.myExt()встроенные функции
КоллекцииList из java.util часто изменяемыйList только для чтения, MutableList — для изменений — коллекции
НаследованиеКласс и метод по умолчанию можно переопределитьНужно явно пометить open, иначе класс закрыт — наследование в ООП
Статические членыКлючевое слово staticcompanion object, аннотация @JvmStatic
АсинхронностьПотоки ОС, CompletableFuture, virtual threads (Java 21)Корутины suspend, библиотека Flowкорутины, Flow
Модели данныхrecord (Java 16+), POJO, Lombokdata class с методом copy()
Закрытые иерархии типовsealed (Java 17+)sealed class, sealed interface
Скорость компиляцииjavac обычно быстрее на огромных модуляхK2 заметно ускорил Kotlin; кэш Gradle помогает обоим
Скорость работы программыЭталон для JVM-приложенийСопоставима: тот же байт-код, те же библиотеки
IDEIntelliJ IDEA, Eclipse, VS CodeIntelliJ IDEA, Android Studio
Типичные сферыEnterprise, Big Data, банки, legacy AndroidНовый Android, Ktor, KMP, Spring
Рынок специалистовОчень широкийМеньше в enterprise, стандарт в Android

Возможности языка (подробнее)

ВозможностьJavaKotlin
Лямбды (короткие функции)С 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 / switchPattern matching в switch (21+)Компилятор требует все ветки для sealed
Лямбда вместо интерфейса с одним методомSAM-конверсия (Java 8+)То же; плюс fun interface
КроссплатформенностьJVM, GraalVM Native ImageJVM, 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 у потенциально пустой ссылки без проверки.

СитуацияJavaKotlin
Тип без nullСоглашение в команде, аннотации @NonNullString — проверка компилятором
Тип с nullЛюбая ссылка; @NullableString?
Безопасный вызов методаif (x != null) x.f()x?.f()
Значение, если слева nullТернарный оператор, orElse у OptionalЭлвис ?:, например name ?: "unknown"
Принудительное "точно не null"Риск NPE без синтаксисаname!! — осознанный риск NPE
Вызов Java из KotlinPlatform type T!interop
val len: Int = name.length // ошибка, если name: String?
val len: Int = name?.length ?: 0 // корректно

Миллионы строк Java-кода написаны без дисциплины null. Kotlin в новом коде заставляет явно помечать nullable-типы; на границе с Java остаются platform types.

Меньше шаблонного кода

ЗадачаJavaKotlin
Поле только для чтенияprivate final String name + getName()val name: String
Конструктор с полямиОтдельный метод или Lombokclass User(val id: Long) в заголовке класса
Один экземпляр на приложениеenum-приём или holder-классobject ConfigООП
Фабричный методpublic static User create()companion object { fun create() }
Неизменяемый DTOrecord или ручной POJOdata 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.

Коллекции

ЗадачаJavaKotlin
Список только для чтенияCollections.unmodifiableList, List.ofИнтерфейс List в Kotlin — read-only view
Список с добавлением элементовArrayList, изменяемый ListЯвный MutableList
Литерал спискаList.of(1, 2, 3)listOf(1, 2, 3)
Ленивая цепочка операцийStreamSequenceколлекции и 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()
}
АспектJavaKotlin
Единица конкуренцииПоток JVM, virtual threadКорутина на пуле потоков
Стиль кодаCallback, Future, блокирующий get()Линейный код с suspend
Структурированная конкуренцияStructuredTaskScope (Java 21+)coroutineScope, supervisorScope
Потоки событий / реактивностьRxJava, Project ReactorFlowстатья
AndroidExecutor, callback-иlifecycleScope, viewModelScopeкорутины

На backend с Java 21 virtual threads и Kotlin-корутины решают похожие задачи (много одновременных I/O-операций). Выбор чаще зависит от стека команды и библиотек, чем от разницы в скорости. Сравнение с Java — асинхронность Java.

Объектная модель

ТемаJavaKotlin
Можно ли наследовать класс без openДа, класс открыт по умолчаниюНет, нужно open
Можно ли переопределить метод без openДа, метод виртуальныйНет, нужны open и override
Метод в интерфейсе с реализациейdefault (Java 8+)Тело метода в интерфейсе
Несколько родительских классовТолько один классТолько один класс
Конечный набор вариантов (UI, Result)enum, иерархия классовsealed class + исчерпывающий when
Передача работы другому объектуРучное делегированиеclass Repo by cache : Repo

Таблицы и примеры — ООП в Kotlin, ООП в Java, общие принципы — раздел ООП.


Где языки расходятся на практике

СитуацияJavaKotlin
Новый экран AndroidВозможен, Google рекомендует KotlinJetpack Compose, корутины, KSP
REST на SpringОгромная документация, де-факто стандартSpring Boot на Kotlin
Лёгкий HTTP-сервисSpark, Javalin, SpringKtor
Big Data (Spark, Hadoop)Основной язык APIОбычно Java или Scala
Скрипты сборки GradleGroovy DSLKotlin 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 требует платформенных слоёв; это не "один файл на все ОС"
Стандартная библиотека и Android SDK — на Java

Даже при работе только на 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, новый кодKotlinGoogle, Compose, современные гайды
Android, старый кодJava + постепенный KotlinЧитать Java, писать новое на Kotlin
Enterprise backendJava; Kotlin набирает долюSpring Boot поддерживает оба — 232
Микросервис с нуляKotlin (Ktor) или Java (Spring)Зависит от команды и найма
Big DataJava, ScalaKotlin в ядре стека редок
Обучение JVM с нуляJava, затем KotlinФундамент JVM — Java intro
Только мобилкаKotlinЧтение Java SDK всё равно понадобится

Маршруты для новичка

Откуда пришлиКуда идти дальше
Цель — AndroidKotlin сразу; Java API по мере задач — основы, Compose
Цель — enterprise backendJava и Spring — Java intro; Kotlin для новых модулей
Уже знаете JavaСинтаксис Kotlin за 1–2 недели + interop
Уже знаете C#Kotlin близок по свойствам и null; экосистема JVM — через Java

Общий гид по выбору языка — Как выбрать язык программирования.


Частые заблуждения

МифКак на самом деле
Kotlin полностью вытеснил Java на JVMJava активно развивается; в enterprise оба языка
Kotlin быстрее Java при выполненииБайт-код сопоставим; отличаются синтаксис и библиотеки
В Kotlin невозможен NPEВозможен через !!, platform types и Java interop
В проекте можно только один языкСмешанные .java и .kt — обычная практика
Java мёртв для AndroidLegacy на Java поддерживается; новый UI — на Kotlin
Корутины всегда лучше virtual threadsЗависит от стека; на Java 21 Loom конкурентен для I/O

Миграция и сосуществование

Типичные шаги команды:

  1. Подключить Kotlin в Gradle или Maven — первая программа.
  2. Новые фичи писать в .kt; стабильный legacy оставить в .java.
  3. Заменять POJO на data class; сервисы — с внедрением через конструктор.
  4. На публичной границе с Java — @JvmStatic, @JvmOverloadsinterop.
  5. Удалять .java после зелёных тестов и ревью.

Подробный разбор аннотаций, SAM, platform types — совместимость на практике.


Чеклист понимания

  1. Объяснить, почему .kt и .java попадают в один JAR без отдельного моста.
  2. Написать data class в Kotlin и эквивалент в Java (record или POJO).
  3. Показать разницу String и String?; назначение ?. и ?:.
  4. Описать разницу между корутиной suspend и virtual thread в Java 21.
  5. Назвать три причины выбрать Kotlin для Android и три — Java для банковского монолита.
  6. Объяснить platform type T! при вызове Java из Kotlin.
  7. Перечислить пять отличий объектной модели (open/final, data class, extension).

См. также

ТемаСтатья
Вызовы между языками, аннотации, миграцияKotlin и Java — совместимость
ООП, краткая таблица отличийООП в Kotlin
Типы и nullТипы данных
КорутиныКорутины в Kotlin
SpringSpring Boot — первая программа
AndroidМобильные приложения
Обзор JavaJava — о разделе
Сравнение C# и Java (тот же формат)Сравнение C# и Java
История KotlinИстория языка
Тесты на JVMТестирование на Kotlin
Рекомендации по стилюРекомендации по разработке