From e046faf4ea369d37a63b47b3394672d5f37ed24d Mon Sep 17 00:00:00 2001 From: Konstantin Alekseev Date: Fri, 12 Oct 2018 23:07:13 +0300 Subject: [PATCH] Update java-to-kotlin-interop.md --- java-to-kotlin-interop.md | 538 +++++++++++++++++++++++++++++++------- 1 file changed, 443 insertions(+), 95 deletions(-) diff --git a/java-to-kotlin-interop.md b/java-to-kotlin-interop.md index 824d1ed..b64133c 100644 --- a/java-to-kotlin-interop.md +++ b/java-to-kotlin-interop.md @@ -7,39 +7,61 @@ title: "Вызов кода Kotlin из Java" # Вызов кода Kotlin из Java -Kotlin code can be called from Java easily. +Код Kotlin может быть вызван из JAVA просто. + -## Properties +## Свойства (Properties) -A Kotlin property is compiled to the following Java elements: +Свойство Kotlin компилируется в следующие Java элементы: + + * Метод getter, имя которого вычисляется путем добавления префикса `get`; + * Метод setter, имя которого вычисляется путем добавления префикса `set` (только для свойств `var`); + * Приватное поле с тем же именем, что и имя свойства (только для свойств с backing fields). + +Например, `var firstName: String` компилируется в следующие объявления Java: +``` java +private String firstName; + +public String getFirstName () { +     return firstName; +} + +public void setFirstName (String firstName) { +     this.firstName = firstName; +} +``` + +Если имя свойства начинается с `is`, используется другое правило сопоставления имен: имя метода getter будет совпадать с +именем свойства, а имя метода setter будет получено путем замены `is` на `set`. Например, для свойства `isOpen`, getter будет +называться `isOpen()` и setter будет называться `setOpen()`. +Это правило применяется к свойствам любого типа, а не только к `Boolean`. + -## Package-Level Functions +## Функции уровня пакета (Package-Level Functions) -All the functions and properties declared in a file `example.kt` inside a package `org.foo.bar`, including extension functions, -are compiled into static methods of a Java class named `org.foo.bar.ExampleKt`. +Все функции и свойства, объявленные в файле `example.kt` внутри пакета `org.foo.bar`, включая функции расширения, +скомпилированы в статические методы класса Java с именем `org.foo.bar.ExampleKt`. ``` kotlin // example.kt @@ -59,7 +81,7 @@ demo.ExampleKt.bar(); ``` -The name of the generated Java class can be changed using the `@JvmName` annotation: +Имя генерируемого JAVA класса может быть выбранно при помощи аннотации `@JvmName`: ``` kotlin @@ -82,10 +104,10 @@ demo.DemoUtils.bar(); ``` -Having multiple files which have the same generated Java class name (the same package and the same name or the same -@JvmName annotation) is normally an error. However, the compiler has the ability to generate a single Java facade -class which has the specified name and contains all the declarations from all the files which have that name. -To enable the generation of such a facade, use the @JvmMultifileClass annotation in all of the files. +Наличие нескольких файлов, имеющих одинаковое имя Java-класса (тот же пакет и одинаковое имя или аннотацию `@JvmName`), +является ошибкой. Однако компилятор имеет возможность генерировать один Java класс фасада, который имеет указанное имя и +содержит все объявления из всех файлов, которые имеют такое имя. +Чтобы включить генерацию такого фасада, используйте аннотацию `@JvmMultifileClass` во всех файлах. ``` kotlin @@ -117,13 +139,62 @@ fun bar() { ... } demo.Utils.foo(); demo.Utils.bar(); ``` + +## Поля экземпляра (Instance Fields) - -## Instance Fields - -If you need to expose a Kotlin property as a field in Java, you need to annotate it with the `@JvmField` annotation. -The field will have the same visibility as the underlying property. You can annotate a property with `@JvmField` -if it has a backing field, is not private, does not have `open`, `override` or `const` modifiers, and is not a delegated property. +Если вам нужно представить свойство Котлина в качестве поля в Java, вам нужно добавить к нему аннотацию `@JvmField`. +Поле будет иметь такую же видимость, что и базовое свойство.Вы можете добавить свойству аннотацию `@JvmField`, +если оно имеет backing field , не является приватным, не имеет `open`, `override` или `const` модификаторов и не является делегированным свойством. ``` kotlin @@ -143,22 +214,41 @@ class JavaClient { } ``` - +Свойства с [поздней инициализацией](properties.html#late-initialized-properties-and-variables) также отображаются как поля. +Видимость поля будет такой же, как видимость сеттера свойства с поздней инициализацией. + -## Static Fields +## Статические поля (Static Fields) -Kotlin properties declared in a named object or a companion object will have static backing fields -either in that named object or in the class containing the companion object. +Свойства Kotlin, объявленные в именованном объекте или объекте-помощнике, будут иметь статические backing fields в этом именованном объекте или в классе, содержащем объект-помощник. -Usually these fields are private but they can be exposed in one of the following ways: +Обычно эти поля являются приватными, но они могут быть представлены одним из следующих способов: - - `@JvmField` annotation; - - `lateinit` modifier; - - `const` modifier. + - `@JvmField` аннотацией; + - `lateinit` модификатором; + - `const` модификатором. -Annotating such a property with `@JvmField` makes it a static field with the same visibility as the property itself. + Аннотирование такого свойства с помощью `@JvmField` делает его статическим полем с той же видимостью, что и само свойство. ``` kotlin @@ -179,8 +269,7 @@ Key.COMPARATOR.compare(key1, key2); ``` -A [late-initialized](properties.html#late-initialized-properties-and-variables) property in an object or a companion object -has a static backing field with the same visibility as the property setter. +Свойство с [поздней инициализацией](properties.html#late-initialized-properties-and-variables) в объекте или объекте-помощнике имеет статическое backing field с той же видимостью, что и сеттер свойства. ``` kotlin @@ -198,7 +287,8 @@ Singleton.provider = new Provider(); ``` -Properties annotated with `const` (in classes as well as at the top level) are turned into static fields in Java: +Свойства, аннотированные `const` (как в классах, так и на верхнем уровне), превращаются в статические поля в Java: + ``` kotlin @@ -218,7 +308,7 @@ const val MAX = 239 ``` -In Java: +В Java: ``` java @@ -226,14 +316,67 @@ int c = Obj.CONST; int d = ExampleKt.MAX; int v = C.VERSION; ``` + +## Статические методы (Static Methods) -## Static Methods - -As mentioned above, Kotlin represents package-level functions as static methods. -Kotlin can also generate static methods for functions defined in named objects or companion objects if you annotate those functions as `@JvmStatic`. -If you use this annotation, the compiler will generate both a static method in the enclosing class of the object and an instance method in the object itself. -For example: +Как упоминалось выше, Kotlin представляет функции уровня пакета как статические методы. +Kotlin также может генерировать статические методы для функций, определенных в именованных объектах или объектах-помощниках, если вы добавите аннотацию `@JvmStatic` к функции. +Если вы используете эту аннотацию, компилятор создаст как статический метод во включающем классе объекта, так и метод экземпляра в самом объекте. +Например: ``` kotlin @@ -246,7 +389,7 @@ class C { ``` -Now, `foo()` is static in Java, while `bar()` is not: +Теперь `foo()` является статическим методом в Java, в то время как `bar()` нет: ``` java @@ -257,7 +400,7 @@ C.Companion.bar(); // the only way it works ``` -Same for named objects: +То же самое для именованных объектов: ``` kotlin @@ -268,7 +411,7 @@ object Obj { ``` -In Java: +В Java: ``` java @@ -279,13 +422,58 @@ Obj.INSTANCE.foo(); // works too ``` +Аннотацию `@JvmStatic` можно также применить к свойству объекта или объекта-помощника, сделав его методы getter и setter статическими элементами в этом объекте или классе, содержащем объект-помощник. + -## Visibility +## Видимость (Visibility) -The Kotlin visibilities are mapped to Java in the following way: +Видимость в Kotlin представляется в Java следующим образом: +* `private` элементы компилируются в `private` элементы; +* `private` объявления верхнего уровня компилируются в локальные объявления пакетов; +* `protected` остаются `protected` (обратите внимание, что java разрешает доступ к защищенным членам из других классов в том же пакете, а Kotlin-нет, поэтому классы Java будут иметь более широкий доступ к коду); +* `internal`  объявления становятся `public` в JAVA. Члены `internal` классов проходят через искажение имен, чтобы усложнить случайное использование их из Java и позволить перегрузку для чле +* `public` остаются `public`. + ## KClass +Иногда вам нужно вызвать метод Kotlin с параметром типа `KClass`. +Автоматического преобразования из `Class` в `KClass` нет, поэтому вам нужно сделать это вручную, вызывая эквивалент свойства расширения `Class.kotlin`: + + +```kotlin +kotlin.jvm.JvmClassMappingKt.getKotlinClass(MainView.class) +``` + -## Handling signature clashes with @JvmName +## Обработка столкновений сигнатур с @JvmName -Sometimes we have a named function in Kotlin, for which we need a different JVM name the byte code. -The most prominent example happens due to *type erasure*: +Иногда у нас есть именованная функция в Kotlin, для которой нам нужно другое JVM-имя байтового кода. +Самый яркий пример происходит вследствии *стирания типа*: ``` kotlin @@ -319,8 +516,8 @@ fun List.filterValid(): List ``` -These two functions can not be defined side-by-side, because their JVM signatures are the same: `filterValid(Ljava/util/List;)Ljava/util/List;`. -If we really want them to have the same name in Kotlin, we can annotate one (or both) of them with `@JvmName` and specify a different name as an argument: +Эти две функции не могут быть определены вместе, потому что их подписи JVM одинаковы: `filterValid(Ljava/util/List;)Ljava/util/List;`. +Если мы действительно хотим, чтобы они имели одно и то же имя в Kotlin, мы можем добавить одному (или обоим) аннотацию `@JvmName` и указать другое имя в качестве аргумента: ``` kotlin @@ -331,9 +528,9 @@ fun List.filterValid(): List ``` -From Kotlin they will be accessible by the same name `filterValid`, but from Java it will be `filterValid` and `filterValidInt`. +Из Kotlin они будут доступны с одинаковым именем `filterValid`, но из Java это будет `filterValid` и `filterValidInt`. -The same trick applies when we need to have a property `x` alongside with a function `getX()`: +Этот же трюк применяется, когда нам нужно иметь свойство `x` вместе с функцией `getX()`: ``` kotlin @@ -345,7 +542,7 @@ fun getX() = 10 ``` -To change the names of generated accessor methods for properties without explicitly implemented getters and setters, you can use `@get:JvmName` and `@set:JvmName`: +Чтобы изменить имена созданных методов доступа для свойств без явно введенных геттеров и сеттеров, вы можете использовать `@get:JvmName` и `@set:JvmName`: ``` kotlin @@ -353,16 +550,44 @@ To change the names of generated accessor methods for properties without explici @set:JvmName("changeX") var x: Int = 23 ``` + -## Overloads Generation +## Генерация перегрузок -Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full -signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the -`@JvmOverloads` annotation. +Обычно, если вы пишете функцию Kotlin со значениями параметров по умолчанию, она будет видна в Java только как полная +сигнатура со всеми параметрами. Если вы хотите предоставить многократные перегрузки вызовам Java, можно использовать аннотацию `@JvmOverloads`. -The annotation also works for constructors, static methods etc. It can't be used on abstract methods, including methods -defined in interfaces. +Аннотации также работают для конструкторов, статических методов и т.д. Их нельзя использовать для абстрактных методов, включая методы, определенные в интерфейсах. ``` kotlin @@ -372,9 +597,7 @@ class Foo @JvmOverloads constructor(x: Int, y: Double = 0.0) { ``` -For every parameter with a default value, this will generate one additional overload, which has this parameter and -all parameters to the right of it in the parameter list removed. In this example, the following will be -generated: +Для каждого параметра со значением по умолчанию это создаст одну дополнительную перегрузку, которая имеет этот параметр и все параметры справа от него в удаленном списке параметров. В этом примере будет создано следующее: ``` java @@ -389,16 +612,43 @@ void f(String a) { } ``` +Обратите внимание, как описаны [вторичные конструкторы](classes.html#secondary-constructors), если класс имеет значения по +умолчанию для всех параметров конструктора, то для него будет создан открытый конструктор без аргументов. Это работает, +даже если не указана аннотация `@JvmOverloads`. + +## Проверенные исключения (Checked Exceptions) -## Checked Exceptions - -As we mentioned above, Kotlin does not have checked exceptions. -So, normally, the Java signatures of Kotlin functions do not declare exceptions thrown. -Thus if we have a function in Kotlin like this: +Как мы уже упоминали выше, Котлин не имеет проверенных исключений. +Таким образом, как правило, сигнатуры Java функций Kotlin не объявляют исключения. +Поэтому если мы имеем такую функцию в Котлине: ``` kotlin @@ -411,7 +661,7 @@ fun foo() { ``` -And we want to call it from Java and catch the exception: +И мы хотим вызвать её из Java и поймать исключение: ``` java @@ -425,8 +675,8 @@ catch (IOException e) { // error: foo() does not declare IOException in the thro ``` -we get an error message from the Java compiler, because `foo()` does not declare `IOException`. -To work around this problem, use the `@Throws` annotation in Kotlin: +мы получаем сообщение об ошибке из компилятора Java, потому что `foo()` не объявляет `IOException`. +Чтобы обойти эту проблему, используйте аннотацию `@Throws` в Kotlin: ``` kotlin @@ -435,18 +685,54 @@ fun foo() { throw IOException() } ``` + -## Null-safety +## Null-безопасность (Null-safety) +При вызове Kotlin функций из Java никто не мешает нам передавать *null*{: .keyword} в качестве ненулевого параметра. +Вот почему Kotlin генерирует проверки времени выполнения для всех публичных функций, которые ожидают непустые значения. +Таким образом, мы немедленно получаем исключение `NullPointerException` в Java-коде. + ## Variant generics -When Kotlin classes make use of [declaration-site variance](generics.html#declaration-site-variance), there are two -options of how their usages are seen from the Java code. Let's say we have the following class and two functions that use it: +Когда классы Kotlin используют [вариативность на уровне объявления](generics.html#declaration-site-variance), есть два варианта того, как их использование видно из кода Java. Допустим, у нас есть следующий класс и две функции, которые его используют: ``` kotlin @@ -460,7 +746,7 @@ fun unboxBase(box: Box): Base = box.value ``` -A naive way of translating these functions into Java would be this: +Наивный способ перевести эти функции в Java будет ``` java @@ -469,23 +755,24 @@ Base unboxBase(Box box) { ... } ``` -The problem is that in Kotlin we can say `unboxBase(boxDerived("s"))`, but in Java that would be impossible, because in Java - the class `Box` is *invariant* in its parameter `T`, and thus `Box` is not a subtype of `Box`. - To make it work in Java we'd have to define `unboxBase` as follows: - - +Проблема в том, что в Kotlin мы можем сказать `unboxBase(boxDerived ("s"))`, но в Java это было бы невозможно, потому что в +Java класс `Box` является *инвариантным* по своему параметру `T` и, следовательно, `Box ` не является подтипом +`Box `. +Чтобы заставить его работать на Java, нам нужно определить `unboxBase` следующим образом: + + ``` java Base unboxBase(Box box) { ... } ``` -Here we make use of Java's *wildcards types* (`? extends Base`) to emulate declaration-site variance through use-site -variance, because it is all Java has. +Здесь мы используем *типы подстановок* Java (`? extends Base`), чтобы эмулировать вариативность на уровне объявления +с помощью вариативности на уровне употребления, потому что она имеется в Java. -To make Kotlin APIs work in Java we generate `Box` as `Box` for covariantly defined `Box` -(or `Foo` for contravariantly defined `Foo`) when it appears *as a parameter*. When it's a return value, -we don't generate wildcards, because otherwise Java clients will have to deal with them (and it's against the common -Java coding style). Therefore, the functions from our example are actually translated as follows: +Чтобы заставить API Kotlin работать в Java, мы генерируем `Box` как `Box` для ковариантно определенных +`Box`(или `Foo ` для контравариантно определенных `Foo`), когда он появляется *как параметр*. Когда это +возвращаемое значение, мы не создаем подстановочные знаки, потому что в противном случае клиенты Java будут иметь дело с ними +(а это противоречит общему стилю кодирования Java). Поэтому функции из нашего примера фактически переводятся следующим образом: ``` java @@ -497,10 +784,9 @@ Base unboxBase(Box box) { ... } ``` -NOTE: when the argument type is final, there's usually no point in generating the wildcard, so `Box` is always - `Box`, no matter what position it takes. +ПРИМЕЧАНИЕ. Когда тип аргумента является окончательным, обычно нет смысла создавать подстановочный знак, поэтому `Box` всегда `Box`, независимо от того, какую позицию он занимает. -If we need wildcards where they are not generated by default, we can use the `@JvmWildcard` annotation: +Если нам нужны подстановочные знаки, где они не генерируются по умолчанию, мы можем использовать аннотацию `@JvmWildcard`: ``` kotlin @@ -510,7 +796,7 @@ fun boxDerived(value: Derived): Box<@JvmWildcard Derived> = Box(value) ``` -On the other hand, if we don't need wildcards where they are generated, we can use `@JvmSuppressWildcards`: +С другой стороны, если нам не нужны подстановочные знаки, где они созданы, мы можем использовать `@JvmSuppressWildcards`: ``` kotlin @@ -520,18 +806,80 @@ fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value ``` +ПРИМЕЧАНИЕ. `@JvmSuppressWildcards` можно использовать не только для индивидуальных аргументов типа, но и для всех объявлений, таких как функции или классы, что приводит к подавлению всех подстановочных знаков внутри них. + + +## Перевод типа Nothing (Translation of type Nothing) + +Тип [`Nothing`](exceptions.html#the-nothing-type) является особым, потому что он не имеет естественного аналога на Java. +Действительно, каждый ссылочный тип Java, включая `java.lang.Void`, принимает значение `null` как значение, а Nothing не +принимает даже этого. Таким образом, этот тип не может быть точно представлен в Java-мире. Вот почему Kotlin генерирует +необработанный тип, где аргумент типа `Nothing` используется: + +``` kotlin +fun emptyList(): List = listOf() +// is translated to +// List emptyList() { ... } +``` +