Skip to content

Commit 68c7b41

Browse files
committed
feat: add @JvmExposeBoxed annotation for 2.2.0
1 parent 22c8e1b commit 68c7b41

File tree

3 files changed

+99
-28
lines changed

3 files changed

+99
-28
lines changed

docs/topics/compiler-reference.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ To show advanced options, use `-X`.
6262
6363
### -X
6464
65+
<primary-label ref="experimental-general"/>
66+
6567
Display information about the advanced options and exit. These options are currently unstable:
6668
their names and behavior may be changed without notice.
6769
@@ -125,6 +127,8 @@ fully qualified name.
125127

126128
### -Xrepl
127129

130+
<primary-label ref="experimental-general"/>
131+
128132
Activates the Kotlin REPL.
129133

130134
```bash
@@ -217,6 +221,8 @@ Use a custom JDK home directory to include into the classpath if it differs from
217221

218222
### -Xjdk-release=version
219223

224+
<primary-label ref="experimental-general"/>
225+
220226
Specify the target version of the generated JVM bytecode. Limit the API of the JDK in the classpath to the specified Java version.
221227
Automatically sets [`-jvm-target version`](#jvm-target-version).
222228
Possible values are `1.8`, `9`, `10`, ..., `24`.
@@ -255,6 +261,14 @@ into the classpath.
255261

256262
Script definition template classes. Use fully qualified class names and separate them with commas (**,**).
257263

264+
### -Xjvm-expose-boxed
265+
266+
<primary-label ref="experimental-general"/>
267+
268+
Generate boxed versions of all inline value classes in the module, along with boxed variants of functions that use them,
269+
making both accessible from Java. For more information, see [Inline value classes](java-to-kotlin-interop.md#inline-value-classes)
270+
in the guide to calling Kotlin from Java.
271+
258272
## Kotlin/JS compiler options
259273

260274
The Kotlin compiler for JS compiles Kotlin source files into JavaScript code.

docs/topics/inline-classes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ fun compute(x: Int) { }
179179
fun compute(x: UInt) { }
180180
```
181181

182+
By default, Kotlin compiles inline classes using **unboxed representations**, which makes them difficult to access from Java.
183+
To learn how to compile inline classes into **boxed representations** that are accessible from Java, see the guide to
184+
[Calling Kotlin from Java](java-to-kotlin-interop.md#inline-value-classes).
185+
182186
## Inline classes vs type aliases
183187

184188
At first sight, inline classes seem very similar to [type aliases](type-aliases.md). Indeed, both seem to introduce

docs/topics/jvm/java-to-kotlin-interop.md

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ On this page, we'll describe the ways to tailor the interop of your Kotlin code
1010

1111
A Kotlin property is compiled to the following Java elements:
1212

13-
* a getter method, with the name calculated by prepending the `get` prefix
14-
* a setter method, with the name calculated by prepending the `set` prefix (only for `var` properties)
15-
* a private field, with the same name as the property name (only for properties with backing fields)
13+
* a getter method, with the name calculated by prepending the `get` prefix.
14+
* a setter method, with the name calculated by prepending the `set` prefix (only for `var` properties).
15+
* a private field, with the same name as the property name (only for properties with backing fields).
1616

1717
For example, `var firstName: String` compiles to the following Java declarations:
1818

@@ -28,9 +28,9 @@ public void setFirstName(String firstName) {
2828
}
2929
```
3030

31-
If the name of the property starts with `is`, a different name mapping rule is used: the name of the getter will be
32-
the same as the property name, and the name of the setter will be obtained by replacing `is` with `set`.
33-
For example, for a property `isOpen`, the getter will be called `isOpen()` and the setter will be called `setOpen()`.
31+
If the name of the property starts with `is`, a different name mapping rule is used: the name of the getter is
32+
the same as the property name, and the name of the setter is obtained by replacing `is` with `set`.
33+
For example, for a property `isOpen`, the getter is called `isOpen()` and the setter is called `setOpen()`.
3434
This rule applies for properties of any type, not just `Boolean`.
3535

3636
## Package-level functions
@@ -109,7 +109,7 @@ org.example.Utils.getDate();
109109
## Instance fields
110110

111111
If you need to expose a Kotlin property as a field in Java, annotate it with the [`@JvmField`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-field/index.html) annotation.
112-
The field will have the same visibility as the underlying property. You can annotate a property with `@JvmField` if it:
112+
The field has the same visibility as the underlying property. You can annotate a property with `@JvmField` if it:
113113
* has a backing field
114114
* is not private
115115
* does not have `open`, `override` or `const` modifiers
@@ -132,14 +132,14 @@ class JavaClient {
132132
```
133133

134134
[Late-Initialized](properties.md#late-initialized-properties-and-variables) properties are also exposed as fields.
135-
The visibility of the field will be the same as the visibility of `lateinit` property setter.
135+
The visibility of the field is the same as the visibility of the `lateinit` property setter.
136136

137137
## Static fields
138138

139-
Kotlin properties declared in a named object or a companion object will have static backing fields
139+
Kotlin properties declared in a named object or a companion object have static backing fields
140140
either in that named object or in the class containing the companion object.
141141

142-
Usually these fields are private but they can be exposed in one of the following ways:
142+
Usually these fields are private, but they can be exposed in one of the following ways:
143143

144144
- [`@JvmField`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-field/index.html) annotation
145145
- `lateinit` modifier
@@ -210,7 +210,7 @@ int version = C.VERSION;
210210
As mentioned above, Kotlin represents package-level functions as static methods.
211211
Kotlin can also generate static methods for functions defined in named objects or companion objects if you annotate those
212212
functions as [`@JvmStatic`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-static/index.html).
213-
If you use this annotation, the compiler will generate both a static method in the enclosing class of the object and
213+
If you use this annotation, the compiler generates both a static method in the enclosing class of the object and
214214
an instance method in the object itself. For example:
215215

216216
```kotlin
@@ -232,7 +232,7 @@ C.Companion.callStatic(); // instance method remains
232232
C.Companion.callNonStatic(); // the only way it works
233233
```
234234

235-
Same for named objects:
235+
Similarly, for named objects:
236236

237237
```kotlin
238238
object Obj {
@@ -265,8 +265,8 @@ interface ChatBot {
265265
}
266266
```
267267

268-
`@JvmStatic` annotation can also be applied on a property of an object or a companion object
269-
making its getter and setter methods static members in that object or the class containing the companion object.
268+
You can also apply `@JvmStatic` annotation to the property of an object or a companion object making its getter and setter
269+
methods static members in that object or the class containing the companion object.
270270

271271
## Default methods in interfaces
272272

@@ -358,15 +358,15 @@ Only compatibility bridges and `DefaultImpls` classes are generated.
358358

359359
The Kotlin visibility modifiers map to Java in the following way:
360360

361-
* `private` members are compiled to `private` members
361+
* `private` members are compiled to `private` members.
362362
* `private` top-level declarations are compiled to `private` top-level declarations. Package-private accessors are also included,
363363
if accessed from within a class.
364-
* `protected` remains `protected` (note that Java allows accessing protected members from other classes in the same package
365-
and Kotlin doesn't, so Java classes will have broader access to the code)
366-
* `internal` declarations become `public` in Java. Members of `internal` classes go through name mangling, to make
364+
* `protected` remains `protected`. (Note that Java allows accessing protected members from other classes in the same package
365+
and Kotlin doesn't, so Java classes will have broader access to the code.)
366+
* `internal` declarations become `public` in Java. Members of `internal` classes go through name mangling, to make.
367367
it harder to accidentally use them from Java and to allow overloading for members with the same signature that don't see
368-
each other according to Kotlin rules
369-
* `public` remains `public`
368+
each other according to Kotlin rules.
369+
* `public` remains `public`.
370370

371371
## KClass
372372

@@ -380,7 +380,7 @@ kotlin.jvm.JvmClassMappingKt.getKotlinClass(MainView.class)
380380

381381
## Handling signature clashes with @JvmName
382382

383-
Sometimes we have a named function in Kotlin, for which we need a different JVM name in the bytecode.
383+
Sometimes we have a named function in Kotlin, for which we need a different JVM name in bytecode.
384384
The most prominent example happens due to *type erasure*:
385385

386386
```kotlin
@@ -400,9 +400,9 @@ fun List<String>.filterValid(): List<String>
400400
fun List<Int>.filterValid(): List<Int>
401401
```
402402

403-
From Kotlin they will be accessible by the same name `filterValid`, but from Java it will be `filterValid` and `filterValidInt`.
403+
From Kotlin, they are accessible by the same name `filterValid`, but from Java it is `filterValid` and `filterValidInt`.
404404

405-
The same trick applies when we need to have a property `x` alongside with a function `getX()`:
405+
The same trick applies when we need to have a property `x` along with a function `getX()`:
406406

407407
```kotlin
408408
val x: Int
@@ -423,7 +423,7 @@ var x: Int = 23
423423

424424
## Overloads generation
425425

426-
Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full
426+
Normally, if you write a Kotlin function with default parameter values, it is visible in Java only as a full
427427
signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the
428428
[`@JvmOverloads`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-overloads/index.html) annotation.
429429

@@ -436,9 +436,8 @@ class Circle @JvmOverloads constructor(centerX: Int, centerY: Int, radius: Doubl
436436
}
437437
```
438438

439-
For every parameter with a default value, this will generate one additional overload, which has this parameter and
440-
all parameters to the right of it in the parameter list removed. In this example, the following will be
441-
generated:
439+
For every parameter with a default value, this generates one additional overload, which has this parameter and
440+
all parameters to the right of it in the parameter list removed. In this example, the following is generated:
442441

443442
```java
444443
// Constructors:
@@ -452,7 +451,7 @@ void draw(String label) { }
452451
```
453452

454453
Note that, as described in [Secondary constructors](classes.md#secondary-constructors), if a class has default
455-
values for all constructor parameters, a public constructor with no arguments will be generated for it. This works even
454+
values for all constructor parameters, a public constructor with no arguments is generated for it. This works even
456455
if the `@JvmOverloads` annotation is not specified.
457456

458457
## Checked exceptions
@@ -586,3 +585,57 @@ fun emptyList(): List<Nothing> = listOf()
586585
// is translated to
587586
// List emptyList() { ... }
588587
```
588+
589+
### Inline value classes
590+
591+
<primary-label ref="experimental-general"/>
592+
593+
If you want Java code to work smoothly with Kotlin's [inline value classes](inline-classes.md), you can use the
594+
`@JvmExposeBoxed` annotation or the `-Xjvm-expose-boxed` compiler option. These approaches ensure Kotlin generates the
595+
necessary boxed representations for Java interoperability.
596+
597+
By default, Kotlin compiles inline value classes to use **unboxed representations**, which are often inaccessible from Java.
598+
For example, you can't call the constructor for the `MyInt` class from Java:
599+
600+
```kotlin
601+
@JvmInline
602+
value class MyInt(val value: Int)
603+
```
604+
605+
So the following Java code fails:
606+
607+
```java
608+
MyInt input = new MyInt(5);
609+
```
610+
611+
You can use the [`@JvmExposeBoxed`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.jvm/-jvm-expose-boxed/) annotation so that Kotlin generates a public constructor that you can call from Java directly.
612+
You can apply the annotation at the following levels to ensure fine-grained control over what's exposed to Java:
613+
614+
* Class
615+
* Constructor
616+
* Function
617+
618+
Before using the `@JvmExposeBoxed` annotation in your code, you must opt in by using `@OptIn(ExperimentalStdlibApi::class)`.
619+
For example:
620+
621+
```kotlin
622+
@OptIn(ExperimentalStdlibApi::class)
623+
@JvmExposeBoxed
624+
@JvmInline
625+
value class MyInt(val value: Int)
626+
627+
@OptIn(ExperimentalStdlibApi::class)
628+
@JvmExposeBoxed
629+
fun MyInt.timesTwoBoxed(): MyInt = MyInt(this.value * 2)
630+
```
631+
632+
With these annotations, Kotlin generates a Java-accessible constructor for the `MyInt` class **and** a variant for the
633+
extension function that uses the boxed form of the value class. So the following Java code runs successfully:
634+
635+
```java
636+
MyInt input = new MyInt(5);
637+
MyInt output = ExampleKt.timesTwoBoxed(input);
638+
```
639+
640+
To apply this behavior to all inline value classes and the functions that use them within a module, compile it with the `-Xjvm-expose-boxed` option.
641+
Compiling with this option has the same effect as if every declaration in the module has the `@JvmExposeBoxed` annotation.

0 commit comments

Comments
 (0)