Kotlin Features Survey Edition #2
We’re planning the next steps for Kotlin’s development and would like to know your opinion about the relative importance of upcoming features. To this end, we’re launching the Kotlin Features Survey, along with a webinar hosted by Roman Elizarov and Svetlana Isakova.
The survey provides an opportunity for you to have your say about the features that should be prioritized in Kotlin. You can find the descriptions of all the features and their use cases below. During the webinar you will be able to ask your questions and discuss the future of the language.
One of the goals of Kotlin, as a pragmatic language, is to stay both concise and versatile at the same time. That’s why designing new features is always hard. New features bring new functionality, but they can complicate the language and provide an easy opportunity for misuse.
The use cases of our users are our brightest beacon in this ocean of choices and trade-offs, helping us navigate and make the right language design decisions. Features that address popular use cases are a lot more likely to be implemented first.
Webinar with Roman Elizarov and Svetlana Isakova
The webinar is over. You can watch the recording.
If you’re interested in learning more about the Kotlin design process and your opportunities to contribute to it, take a look at these materials:
Feature descriptions and instructions
Below, you will find the descriptions of features and the use cases they address. To vote, please fill out the form.
We ask you to choose the three features that would bring the most benefit to you, and downvote one that will create more problems for your code. We also ask you to vote for specific solutions, as some of the potential features can be implemented in multiple ways and in some aspects they may contradict one another. Your votes will help us a lot!
Please note that this is not an exhaustive list of features we are currently working on. The listed features are also at various stages of development. We cannot promise that any of these features will make it into the language any time soon, even if they receive a lot of votes. You also won’t see any of them in version 1.6, as your votes help us to prioritize our work in the longer term.
- Companion objects and static extensions
- Multicatch and union types
- Kotlin properties overriding Java accessors
- Package-private visibility
- Multiple receivers on extension functions and properties
- Default implementations for expect declarations
- Overloadable bitwise operators like I and &
- Name-based destructuring
- Collection literals
- Structure literals (Tuples)
- Operator and DSL improvements
- Lateinit for nullable and primitive types
- Having “public” and “private” property types
- Annotation to warn about the unused return value
- Unqualified enum constants & sealed subclasses in when expressions
Companion objects and static extensions
Problem: Suppose that we want to define the
String.CASE_INSENSITIVE_ORDER property that is available on the String class directly (not on a String instance). In Kotlin, you can have an extension to the class if, and only if, the corresponding class declares a companion object.
You cannot add a similar extension to a third-party class that does not have a companion object.
Related problem: In libraries, there’s often the need to group a number of declarations together so that they can be used with a common prefix. Well-known examples include
Delegates.notNull in the standard library and
kotlinx-coroutines. The only mechanism for this that Kotlin currently provides is the object (companion or not). However, exposing an object instance is a significant maintenance commitment for a library when no instance is actually needed.
Another related problem: Inside declarations of your own classes, companion objects represent the only way to declare static members that are private to the class, which adds additional overhead to the JVM bytecode, especially when compared with the static methods in Java.
- Do not do anything conceptually new, as that would complicate the language. Instead optimize the JVM bytecode for private companion objects where possible.
- Introduce the new concept of a namespace – a kind of ephemeral object without an instance that every class automatically possesses, so that an extension to a class’s namespace can be introduced to any third-party class, and namespace members are naturally compiled down to static members on the JVM. This keeps static helpers grouped together in the source, but removes all the object overhead.
More details: KT-11968
Multicatch and union types
Problem: When working with exception-heavy Java APIs in Kotlin, you cannot catch multiple exception types in a single catch() clause. You’ll have to write multiple catch() clauses or use a
when expression to test the exception type:
Related problem: When writing Kotlin APIs that can succeed or fail in various ways that yield additional information, you’d use a sealed class as a result, providing a type-safe listing of all possible types of errors that a function can fail with. This can become quite verbose too:
- Don’t do anything specifically for exceptions because modern Java APIs don’t abuse exceptions that much (and older ones will eventually be superseded), but provide a concise enum-like syntax for declaring sealed classes, so that Kotlin-style error-returning functions are easier to write.
We could have a simpler syntax for the cases that are “between” enums and full-power sealed classes, which would be equivalent to enums if all the options are constants:
That would make it possible to implement various optimizations, like efficiently implementing constants without separate classes and generating the correct toString for them right away.
- Add support for union types, so that it is possible to declare a function returning one of the possible values without having to introduce a dedicated ParseResult type at all:
This also provides a syntax for multicatch when working with legacy Java APIs:
More details: KT-7128
Kotlin properties overriding Java accessors
Problem: Kotlin properties can’t override Java-style getters and setters:
A Kotlin property can’t override Java’s
getName() method (or the
name() method in a Java record). This is because Kotlin sees a Java interface with the
getFoo() method as having a member function
getFoo() (which can be overridden) and an extension property
foo for idiomatic access from Kotlin (which cannot be overridden).
Related problem: In multiplatform projects, Kotlin’s expect
val/var cannot be provided by an actual Java function:
Supporting this feature could greatly simplify the process of adding idiomatic common Kotlin modules for Java libraries.
Attempts to override Java functions with Kotlin properties could be supported (as opposed to producing an error). This could cause a number of weird cases in mixed Java/Kotlin hierarchies (e.g. when one interface overrides a Java
getFoo() function with a Kotlin
getFoo() function, while another interface overrides it with a Kotlin
foo property). All those cases could be detected during compilation to report errors, and various JVM-specific annotations could be provided to explicitly specify what is supposed to happen in such cases and how to work around the corresponding errors.
More details: KT-6653
Problem: In Kotlin, you can limit the scope of declarations to either a file (which is usually small) by using a
private modifier or to a whole module (which tends to be big) by using an
internal modifier. There’s no intermediate scope that allows a group of related files to share their implementation details, like package visibility in Java does. Packages in Kotlin are simply namespaces: they provide “full names” to their content declarations but have no visibility restrictions.
Instead, Kotlin has internal visibility, which means a declaration with this visibility modifier is “visible inside a module”.
Still, many developers would like to have package-private visibility for their mixed Java / Kotlin projects.
- Introduce a new Java / C#-like visibility modifier that sits somewhere in between
private(limited to the same package in the same module).
- Don’t add any new visibility, but instead introduce a way to mark a package as “confined / isolated”, so that all
internaldeclarations in the package become visible only to this package.
More details: KT-29227
Multiple receivers on extension functions and properties
Problem: In Kotlin, you can define an extension function inside a class. A member extension function inside a class has two receivers – a dispatch receiver from the class and an extension receiver from the extension member:
You can only call the
dp property within the scope where View is available as a receiver:
It would be useful to be able to define such a member, not only as a member function or property but also outside of the context class (
View). That would make it a function or property with multiple receivers (in this example, with two receivers:
Float). When the context class belongs to a separate library, it would be the only option.
We could define a function or property to be used only in contexts where a specific receiver is available:
When you use the
dp property, you would still need to provide
View as an implicit receiver, as before. But now it wouldn’t need to be a member of the
You could define a top-level function requiring scope. For example, you could inject a logger, or specify that an operation can be performed only in the context of the database transaction:
You wouldn’t be able to call such a top-level function as an extension on its context receiver (logger can’t become an explicit receiver). The function could only be called “in the context with this receiver”, but the receiver wouldn’t become the main object operated on, as is the case with extensions.
You could define several context receivers, as well as ones with generic parameters. Read the detailed description of this functionality’s design, including the reasoning of how to conceptually distinguish between use cases of context receivers and regular extension receivers in the corresponding “Context receivers” KEEP.
Default implementations for expect declarations
Problem: Currently, expect declarations in the common code can’t have default implementations. Functions, properties, and member functions inside an
expect class can’t define bodies. It’d be useful to provide default implementations in the common code and tweak them in the platform code if needed.
Possible solution direction:
Allow specifying default implementations for
More details: KT-20427
Overloadable bitwise operators like I and &
Problem. Bitwise operators like
<< (left shift),
>> (right shift),
^(xor, “exclusive or”), and
~(tilde, or “bitwise negation”) are used mainly for bit-manipulation code.
In Kotlin, you use them by writing their explicit names:
While this is more readable for developers unfamiliar with bit manipulation practices, many people who are used to writing such code in other languages find this approach too verbose and limiting.
Add the corresponding operators to make this code familiar for people who often write bit-manipulation code:
Following the general Kotlin philosophy, these operations would become overloadable operators in Kotlin, also to be used with Number types from different libraries. But it’s also important to consider that this could make it too easy to misuse them in DSLs and APIs.
More details: KT-1440
Problem. Position-based destructuring works great for “set in stone” library classes like Pair or Triple, but it presents a danger when working with custom data classes:
If we later modify the class and add the property
postalCode in the second position (between street and city), the Kotlin code will continue to compile. But it’ll contain a bug: because of the positional destructuring, the
postalCode data will be assigned to the
In addition to this, positional destructuring does not scale to real-life entities that can have dozens of properties, of which a particular piece of code may only need to retrieve a few.
That’s a known problem when using positional destructuring with custom data classes, and name-based destructuring should solve it:
You would then refer to properties by names, not by their positions. If you add a new
postalCode property, the code would continue to work correctly and assign the right property to the
If we introduce the new named destructuring syntax, we would be able to consider making the positional destructuring of data classes an opt-in feature (with some kind of positional class modifier) and stop offering it by default for all data classes.
More details: KT-19627
Problem. In Kotlin, you create collections by calling library functions, such as
listOf(1, 2, 3) or
mapOf(“one” to 1, “two” to 2, “three” to 3). Such invocations look verbose for data-heavy applications. What’s more, the general creation of collections is currently inefficient in Kotlin due to the underlying use of
We could make collection creation more concise and convenient by having a specialized syntax:
By default, such literals would create a read-only
Map<String, Int>, but if you need different types, you should be able to specify them explicitly or have them inferred from the context:
The construction of collection literals would be efficient by design. The underlying mechanism would not rely on varargs, pairs, or other wrappers.
As usual, our goal is to provide a general solution, not a built-in syntax for a specific use case. We need a solution that works for library types, as well as for user-defined types. The syntax above is supposed to be desugared into something like:
A user-defined collection type would have an option to provide a similar kind of “collection builder operator” that is recognized by the compiler and used under the hood.
More details: KT-43871
Structure literals (Tuples)
Problem: Kotlin doesn’t support tuples, finite ordered sequences of elements, which are a common feature in most modern languages. If you need to return several values from a function, you should either explicitly use the library classes
Triple, or define a new data class for this case. While defining a separate class is generally a better solution from the API design perspective, it might feel too verbose.
Related problem: It would be convenient to be able to create data classes without calling constructors explicitly, especially for data-heavy scenarios, or when your data class has many optional constructor parameters. Compare the following two declarations (assuming we can use collection literals syntax […]):
The second one reads much better when you need to create lots of
Points in the code.
Introduce structure literals. You could explicitly specify their types:
Without explicit specifications, the types of tuples could represent anonymous data classes (or value classes, when they become supported). The component names could be either explicit or implicit, and you could omit the type, so the following variable would get an anonymous tuple type (Int, Int):
Tuple types should support naming their components, so (
x = Int, y = Int) is different from (
first = Int, second = Int).
Then you would be able to use tuple types for the ad-hoc returning of multiple values from a function:
However, in order to assign this anonymous tuple value to a real type, the compiler would need to perform under the hood “conversion”. That poses new challenges. For instance, you would potentially observe weird and unintuitive behavior if a value were implicitly converted and became not equal to its original value (it’s no longer possible to look it up in an ordinary collection). Allowing user-defined conversions would add an additional non-trivial thing to learn and understand, amounting to a potential “overcomplication” of the language.
Operator and DSL improvements
Problem: Kotlin supports overloading for a restricted set of commonly used operators. This and other features (like extension lambdas) play a big role in improving APIs and creating DSLs. Sometimes, however, the exact operator conventions seem too restrictive, and there are different requests for extending them.
One of the most common requests is to upgrade the convention for comparison operators to allow the
compareTo method return a custom type instead of
Int. Currently, the replacements must be defined as functions named
equalTo can’t return a custom type, these comparisons are defined as
infix functions with explicit names:
Review the current list of conventions, trying to extend them when possible (of course, this can only be done in a backward-compatible way), so that this and similar requests could receive better support in the corresponding DSLs and frameworks. Like using
However, for consistency, this would mean that the equality comparison operator
== would also be allowed to change its meaning for certain types in certain contexts, and that it would potentially return something other than a Boolean value in those cases.
Lateinit for nullable and primitive types
Problem: In Kotlin, a
lateinit property can be neither nullable nor of a primitive type, like
Long. This restriction is connected with the underlying implementation: the “uninitialized value” is stored as
null. However, this restriction is often seen as artificial, surprising, and inconvenient. Why not simply allow
lateinit to be nullable and allow
The reason is that
lateinit in Kotlin/JVM serves the dual role of being used as an injection point for DI (dependency injection) frameworks and also of exposing the underlying field as a public field on the JVM for those frameworks.
- Lateinit primitives could be implemented by boxing, or by using an additional bitmask. For nullable types, the compiler can’t use
nullas an uninitialized marker, but it could use another internal
UNINITIALIZEDvalue instead and check it on each access (similar to how it currently works with
nulls). However, using another special value modifies the type of the property and therefore would complicate the scenarios of using nullable
lateinitproperties for dependency injection frameworks (the proper type of nullable field would no longer be recognized by those frameworks, causing strange errors).
- Decouple lateinit into two separate features. Leave it alone for DI frameworks (with current limitations on non-nullity and non-primitiveness) with potential deprecation and replacement in the future. Focus on optimizing simple delegated properties, so that
var property by Delegates.lateInit()is efficiently compiled, supports all Kotlin types, and can be used in all non-DI use-cases as a replacement for
lateinit var property.
More details: KT-15284
Having “public” and “private” property types
Problem: We often store values in a
MutableList property, and use this property inside the class as a
MutableList, but what if we want to expose it outside the class as a read-only
We could explicitly have
private types for the same property!
Annotation to warn about the unused return value
Problem: Sometimes the result of a function call shouldn’t be ignored. If you forget to use this result in some way, it’s a straightforward indication of an error. One example is not assigning the result of adding an element to a list somewhere:
The initial list hasn’t changed, and the list returned by
+ is lost. Another “trap” is forgetting to call a terminal operation on
One specific case where it’s a mistake to ignore the result is pure functions. A function is called pure if its result depends on the arguments only and doesn’t operate over the global state. Such a function can only use other pure functions and constants.
One more example is the result of an async-style function, which returns
Deferred or another kind of “future” (“promise”) object. If you never access the
Deferred value afterward and its computation ends up with an exception, that exception is going to be ignored.
Introduce an annotation on types indicating that the value can’t be ignored and should be somehow used. The exact name still needs to be discussed – for now, let’s call it
future functions from the
kotlinx.coroutines package could return a value of
@CheckResult Deferred type, and the compiler could emit an error if your function returns a value that you never use.
Possible annotation names (you can indicate your favorite if you vote for this feature):
An alternative would be to “swap the default”:
- Start emitting the compiler warnings if the result of any function is ignored.
- Introduce an annotation (like
DiscardableResult) to mark the functions for which dropping the result is fine.
Unqualified enum constants & sealed subclasses in when expressions
Problem: Referring to
enum constants with fully qualified names like
Color.BLUE often looks verbose. Of course, there is an alternative – importing constants by name. But then they become available in the whole file and not just inside the given context like a
What’s more, the import trick may not work with short universal constant names like
OFF, since it can result in ambiguous names.
Related problem: When enumerating all the subclasses of a sealed class, you also have the option of either importing the subclasses explicitly or using the qualified names:
That’s verbose, too.
Support context-sensitive resolution for names. The compiler could resolve the when branch condition in the context with the expected type, specifically, with the type of the
The same applies to
The use-cases for context-sensitive resolution are not limited to enums, sealed classes, and the context of when expressions. It works whenever there’s an expected type:
This could greatly simplify repetitive code like modifier
= Modifier.foo() when resolving this Modifier method in the context of the parameter type (with the
Modifier expected type):
More details: KT-16768