Kotlin 1.4 released to improve performance
Last week, JetBrains released Kotlin 1.4 to the programming community. The update added some new language features but also put a strong focus on improving quality-of-life and performance, especially when paired with JetBrains’s own IntelliJ IDEA IDE.
Kotlin is a programming language best known as “Java without the warts.” It can compile both to Java bytecode and native code, as well as transpile to JavaScript. It’s been in our top five most loved languages for three years running and, partly thanks to Android development moving to a Kotlin-first approach, it’s moved to the 13th most popular language in this year’s survey. Java is still more popular and in active development, but Kotlin partisans are fervent in their appreciation for it.
So what can users expect from this new version? Read on.
Performance improvements
The primary focus of this release was to improve performance across the board. Jetbrains (and the other contributors) fixed over 60 performance-related issues. This includes a lot of IDE bugs that were slowing things down, causing memory leaks and freezes. Autocomplete results now surface faster, one of the benefits of using a programming language created by an IDE company.
For performance slowdowns, nothing beats the compiler. Big compilation times drain developer productivity, so this release includes a brand new compiler. Kotlin code should compile to any of the supported target platforms faster. Additionally, they added an API that allows future extensions to the compiler.
Functional interfaces
Single abstract method (SAM) interfaces, aka functional interfaces, were previously only supported when the interface was written in Java. While Kotlin is interoperable with Java libraries, this could lead to complications where you would have to write your interface in Java and your implementations in Kotlin. With the increasing relevance of lambda expressions, this became increasingly burdensome and didn’t really fit in with Kotlin’s philosophy of remaining concise and improving developer happiness.
With SAM interfaces now supported directly in Kotlin, you can automatically convert a lambda expression that matches a SAM interface into an instance of a class that implements that interface without having to write the implementation class manually.
Explicit API mode
Library authors have an additional complication compared to devs writing applications: visibility. They need to make sure that their API methods are visible to anyone incorporating their functionality and anyone taking this library as a dependency needs to know what type any exposed variables are. Visible by default could mean a dev exposes something never meant to be seen by application developers, and explicitly hiding something previously visible would mean breaking changes.
With explicit API mode, all methods must have their visibility stated and all types must be specified. This is a compiler feature, either as a gradle setting or command line option, and can throw either warnings or errors, depending on how militant you want to be about your API library.
Syntax conveniences
In line with its goal of making life easier for developers, Kotlin 1.4 adds a number of small language features that help avoid getting caught by errors of oversight:
- Mixing named and positional arguments in functions. Want to clarify an argument by explicitly mentioning its parameter name? Now you can without causing errors. For example `reformat(‘This is a String!’, uppercaseFirstLetter = false , ‘-‘)` Previously, you could only pass values in the order specified in the method.
- Trailing commas. In any comma-separated list of values or parameters, you can now include a comma at the end of the list without causing errors. Helpful for lists that are regularly edited and change over time.
- Use `continue` and `break` within `when` loops without labeling. These keywords can now just be used as is without labeling with `@LOOP`.
Will these new features continue Kotlin’s growth or just please the existing base? Let us know in the comment if you think any of these additions make Kotlin a Java killer or if they are just riding the hype train.
Tags: kotlin
8 Comments
In the bulleted list at the end, the uppercaseFirstLetter = false is not a variable assignment, it’s passing an argument named uppercaseFirstLetter to the function. See the original example in context: https://kotlinlang.org/docs/reference/whatsnew14.html#mixing-named-and-positional-arguments
What’s happening is that Kotlin loosened the rules about named and positional arguments. When languages support both arg types, often they don’t let you put a named before a positional like like f(1, b=2, 3). That avoids confusion about whether the 3 is supposed to be the second or third positional argument. Python has this rule, for instance.
Kotlin has decided to carve out an exception that you can, in effect, “label” an argument in the middle of a positional argument list with its name, for clarity when people are reading the code. So f(1, b=2, 3) is defined to be the same as f(1, 2, 3) but better labeled, and you can’t do f(1, q=2, 3) if q isn’t normally the second arg. Isn’t an approach I’d thought of, but neat.
Ah, interesting, I misread the docs there. I don’t think I’ve seen that sort of positional assignment before, but I can see how it could be useful, especially if you don’t want to memorize the order of variables in a function.
There’s still a misleading sentence:
“Want to set a variable within a function call?”
should be something like
“Want to clarify an argument by explicitly mentioning its parameter name?”
Nice explanation, Randall! I hadn’t considered the confusion caused by something like f(1, b=2, 3). Now it makes sense to me why Kotlin didn’t have this feature from the get-go.
Thank you for the Functional interface update! I like to define my SAMs as interfaces to give the method name and arguments self-documenting names and I had to use typealiases instead which took care of the ‘type’ name, but the method name was still invoke() and I got a compiler issue for certain scenarios of argument names (can’t remember the error or scenario that caused it but it was a known issue with a ticket already logged).
Maybe I spoke too soon. Upgraded to 1.4.0 (both IntelliJ kotlin plugin and build.gradle version) and the code below still does not compile. Maybe I am misunderstanding the change?
In case the code below doesn’t format properly: https://gist.github.com/pbriggs28/750c5722248ebb197cbaaf4122b26b88
// As typealias (works)
typealias FooTypeAlias = () -> Boolean
fun runAsTypeAlias(func: FooTypeAlias): Boolean {
return func.invoke()
}
// As Java SAM interface (works)
// Defined in FooJavaInterface.java: public interface FooJavaInterface { boolean samJavaInterfaceCall();}
fun runAsJavaInterface(func: FooJavaInterface): Boolean {
return func.samJavaInterfaceCall()
}
// As Kotlin SAM interface (doesn’t work)
interface FooKotlinInterface {
fun samKotlinInterfaceCall(): Boolean
}
fun runAsKotlinInterface(func: FooKotlinInterface): Boolean {
return func.samKotlinInterfaceCall()
}
// Function that *should* work for typealias, Java SAM interface, and Kotlin SAM interface calls
fun doSomething(): Boolean {
return true
}
fun main() {
// prints 1.4.0
println(KotlinVersion.CURRENT)
// compiles
runAsTypeAlias { doSomething() }
// compiles
runAsJavaInterface { doSomething() }
// Should compile in kotlin 1.4.0 but doesn’t:
// Type mismatch.
// Required:
// FooKotlinInterface
// Found:
// () → Boolean
runAsKotlinInterface { doSomething() }
}
Hello Preston,
you need to explicitly define the Kotlin interface as “fun interface Xyz” instead of “interface Xyz”.
See more information in the docs:
https://kotlinlang.org/docs/reference/fun-interfaces.html
Finally, trailing commas! This feature has seemed like a no-brainer ever since I encountered it in Go, and I’m glad Kotlin finally supports it. Any ideas on why this feature might be harder to support than it seems, or why it wasn’t in earlier versions of Kotlin?
Mixing named and positional arguments is another feature I’ve been missing. It’s especially useful when your function has some arguments whose purpose is obvious from their type, alongside other arguments that could easily but disastrously be misinterpreted. Something like:
fun sendEmail(recipient: User, useHtml: Boolean, giveFreeTrialAccess: Boolean, delaySend: TimeInterval) { … }
sendEmail(user, true, false, hours(1)) // Concise, but opaque. Could easily give a free trial when none was intended, or vice versa.
sendEmail(recipient = user, useHtml = true, giveFreeTrialAccess = false, delaySend = hours(1)) // a bit wordy
sendEmail(user, useHtml = true, giveFreeTrialAccess = false, hours(1)) // just right!
I’ve never used SAMS, but the other features mentioned here (faster compilation, fewer IDE bugs, faster IDE autocomplete) are extremely welcome. They’ve all been pain points for me over the past year. About once a week, IntelliJ would enter a bad state where it failed to understand relationships between files. A lot of syntax highlighting and type annotations would disappear, and clicking into a function or variable that was declared in a different file wouldn’t work. I never did figure out how to reliably get out of the bad state, but it usually resolved after about an hour. Hopefully this is fixed in the Kotlin 1.4 plugin!