Files
obsidian/Kotlin/Синтаксис и типы/Базовый синтаксис/Пакеты и импорты.md
T

7.2 KiB

title, description, tags
title description tags
Пакеты и импорты Объявление пакетов, импорты, псевдонимы и автоматически импортируемые пакеты
kotlin
syntax

Пакет

Объявляется первой строкой файла:

package com.example.app

fun helper() { }
class Service

Полное имя функции - com.example.app.helper.

Отличие от Java: структура каталогов не обязана совпадать с пакетом, и имя файла не связано с содержимым. Компилятор не возражает. На практике соглашение соблюдают: иначе навигация по проекту ломается.

Файл без package попадает в корневой пакет (default package).

Содержимое файла

В одном файле может быть сколько угодно объявлений верхнего уровня:

package com.example

const val VERSION = "1.0"

fun parse(text: String): Int = text.toInt()

class User(val name: String)
class Admin(val name: String)

private fun internalHelper() { }   // видна только в этом файле

Никаких public class на файл - ограничения «один публичный класс на файл» в Kotlin нет.

Импорты

import com.example.app.Service          // конкретный класс
import com.example.app.helper           // функция верхнего уровня
import com.example.app.*                // всё из пакета
import com.example.app.User.Companion.create  // член companion object

Импортировать можно не только типы, но и функции, свойства, enum-константы - всё, что объявлено на верхнем уровне или как статический член.

import kotlin.math.PI
import kotlin.math.sqrt

fun main() {
    println(sqrt(PI))      // 1.7724538509055159
}

Псевдонимы - as

Когда имена конфликтуют или слишком длинные:

import com.example.data.User as DataUser
import com.example.api.User as ApiUser

fun convert(u: DataUser): ApiUser = ApiUser(u.name)

Псевдоним действует только в своём файле. Если нужно имя на весь модуль - это typealias.

Импортируются автоматически

Эти пакеты доступны без import:

Пакет Что содержит
kotlin.* Int, String, Any, базовые функции
kotlin.annotation.* аннотации
kotlin.collections.* List, Map, listOf, операции
kotlin.comparisons.* compareBy, maxOf
kotlin.io.* println, readln
kotlin.ranges.* диапазоны, step, downTo
kotlin.sequences.* Sequence, sequenceOf
kotlin.text.* операции со строками
java.lang.* только на JVM
kotlin.jvm.* только на JVM

Поэтому println, listOf, Int работают из коробки. А kotlin.math.sqrt - нет, его импортируют явно.

Видимость на верхнем уровне

public fun api() { }       // видна везде (public — по умолчанию)
internal fun moduleOnly() { }  // видна в своём модуле
private fun fileOnly() { }     // видна только в этом файле

protected на верхнем уровне запрещён - нет класса, от которого наследоваться. Подробнее: Модификаторы видимости

Имя класса-обёртки

Функции верхнего уровня попадают в класс, названный по файлу: Utils.kt → класс UtilsKt. Имя можно задать:

@file:JvmName("Utils")
package com.example

fun helper() { }     // из Java: Utils.helper()

Аннотации уровня файла ставятся до package. Подробнее: Kotlin из Java

[!info] Отличия от Java

Java Kotlin
каталоги обязаны совпадать с пакетом не обязаны (но так принято)
имя файла = имя публичного класса имя файла произвольно
один публичный класс на файл сколько угодно объявлений
import static для статики обычный import для всего
псевдонимов нет import ... as Name
автоимпорт только java.lang десять пакетов kotlin.*
package-private по умолчанию public по умолчанию

Последняя строка важна: в Java класс без модификатора виден только своему пакету, в Kotlin - всем. Для сокрытия внутренностей библиотеки используют internal.

Сравнить: Модификаторы в Java

[!warning] Подводные камни Нет аналога package-private. internal ограничивает модулем (единицей компиляции Gradle), а не пакетом. Разделить код на пакеты и спрятать их друг от друга внутри одного модуля нельзя - только разносить по модулям.

import * не затягивает вложенные пакеты. import com.example.* не даёт доступа к com.example.data.User.

Конфликт импортов - ошибка компиляции, а не предупреждение. Два User из разных пакетов требуют as или полного имени.

Функции с одинаковым именем в разных файлах одного пакета конфликтуют. Два private fun helper() в разных файлах - нормально, а два fun helper() с одной сигнатурой - ошибка: оба попадают в один пакет.

@file:JvmName не действует, если имя файла совпадает с именем класса в нём. Коллизия имён вызовет ошибку компиляции.

Дополнительно: Структура проекта Далее: Null-безопасность