Auto Added by WPeMatico

Kotlin Plugin 2021.1 Released: Improved IDE Performance and Better Support for Refactorings

The newest release of IntelliJ IDEA, version 2021.1, comes with the improved Kotlin plugin. Enjoy an improved developer experience with faster code completion and highlighting, change signatures with better IDE support, benefit from better debugging experience for evaluating properties, and more.

Here is what you’ll get by installing the new plugin:

Auto-update to this new release

IntelliJ IDEA will give you the option to automatically update to the new release once it is out.

If you are not yet an IntelliJ IDEA user, you can download the newest version and it will already come bundled with the latest Kotlin plugin.

Enjoy quick code completion and highlighting

Sometimes writing code may not be as much fun as it could be, especially if you need to wait for the IDE to highlight your code and help you with completion. Our goal is to make code highlighting and completion seamless for you.

The new Kotlin plugin significantly improves performance for Kotlin code highlighting and completion.

Based on our tests, highlighting speed has improved by about 25% and code completion is now more than 50% faster, which brings it much closer to the performance level offered for Java.

Note that these numbers are just based on our tests. The improvements in your actual projects may not be as significant as our tests show, but you should notice much better performance.

Kotlin code highlighting in the new plugin

Here you can see a comparison of the speed of Kotlin code highlighting between the previous Kotlin plugin, version 2020.3, and the new one, version 2021.1. The results are based on our benchmark tests that check code highlighting in complex Kotlin files.

Faster Kotlin code highlighting

Kotlin code completion in the new plugin

And here is a comparison of the performance of Kotlin code completion between version 2020.3 and version 2021.1. The results are based on our benchmark tests that check code completion in complex Kotlin files.

Faster Kotlin code completion

Change signature with better IDE support

If you’ve used the Change Signature refactoring in previous versions of the Kotlin plugin, you may have encountered issues and limitations, as well as a lack of visibility regarding what went wrong in those cases.

Update to the new Kotlin plugin to reap the benefits of more than 40 bug fixes and improvements to the Change Signature refactoring.

Here are some of the most important improvements:

Evaluate custom getters right in the Variables view

Based on our research, the debugging experience for Kotlin requires significant improvements to deliver a better developer experience, and we already have a number of plans in the works to provide them.

This release provides a small but important improvement for evaluating properties in the Variables view.

Previously, during a debug session, you could only see the properties that didn’t have a custom getter and those with a backing field. Properties with a custom getter didn’t appear because they are represented as regular methods on the JVM. In version 2021.1 of the Kotlin plugin, you can see all such properties and evaluate them on demand by clicking on

get()

near the property name.

For example, when debugging the following code, you can execute the

get()

method to see the

versionString

value:

class LanguageVersion(val major: Int, val minor: Int) {
    val isStable: Boolean
        get() = major <= 1 && minor <= 4
    val isExperimental: Boolean
        get() = !isStable
    val versionString: String
        get() = "$major.$minor"
    override fun toString() = versionString
}
fun main() {
    val version = LanguageVersion(1, 4)
}

In the Debug window, you can see the values of the properties:

Debugging in Variables view

We would greatly appreciate it if you could try this feature out and provide your feedback in this ticket or as comments to this blog post.

Use code completion for type parameters

The new Kotlin plugin can now complete code for type parameters. Previously, you had to write this code manually without the benefit of the IDE’s assistance features.

Now code completion suggests functions and properties after generic functions and restores type arguments where needed. When you select such a function from the list, the IDE adds the correct type parameter to the preceding code.

In the following example, the IDE automatically adds the

&lt;String&gt;()

type:

Code completion for type parameters

After you apply the IDE’s suggestion, you’ll get the following code:

fun typeParametersAtCodeCompletion() {
    //  Function definition from stdlib:
    // public fun <T> emptyList(): List<T>
    val listA: List<String> = emptyList() // T is inferred from the context (explicit variable type)
    val listB: List<String> =
        emptyList<String>().reversed() // type argument for emptyList() is required to evaluate expression type
}

Review the structure of your Kotlin code with class diagrams

With the new release, you can review the structure of your Kotlin code via UML Class diagrams. To build a diagram, select Diagrams | Show Diagram… | Kotlin Classes in the Project View.

UML Class diagrams for Kotlin code

Currently, the diagrams only show inheritance and nesting relationships. Additional, more detailed association connections, like aggregation, construction, dependency, and others, will become available in future releases.

Benefit from other IDE improvements

Since the Kotlin plugin and the IntelliJ Platform have been moved to the same codebase and now ship simultaneously, you will also have the ability to do the following to benefit your Kotlin experience:

See also

Continue Reading Kotlin Plugin 2021.1 Released: Improved IDE Performance and Better Support for Refactorings

Kotlin 1.5.0-M2 Released – Ensure Smooth Migration to Kotlin 1.5.0

Kotlin 1.5.0-M2 is the last milestone release for Kotlin 1.5.0, which is coming this spring. So this is the last chance to make sure that your projects will successfully work with Kotlin 1.5.0.

Install 1.5.0-M2

If you migrate your projects now, you can save yourself time and energy in the future, when Kotlin 1.5.0 comes out, and you can help us provide urgent fixes before the release.

For example, if you try the new JVM IR backend, which is becoming the default in 1.5.0, and discover any issues now, we’ll try to deliver fixes before the release. You can report any issues you encounter to our issue tracker, YouTrack.

Note that Jetpack Compose only works with the new JVM IR backend. So if you’ve tried Jetpack Compose, you’ve already used the new backend.

Enjoy simplified configuration for preview releases

Previously, if you decided to use a preview release in your existing projects, you had to specify an additional Bintray repository in your Gradle files. Now all Kotlin preview artifacts are stored in Maven Central and there is no need to add the repository manually.

Save time! Install the M2 release, change the Kotlin version in your projects to

1.5.0-M2

, and adjust any library dependencies if necessary.

Install 1.5.0-M2

Share your real-world cases with new language features

Have you had a chance to test out the experimental language features in Kotlin 1.4.30?

If you haven’t, give them a try now! And if you’ve already worked with them or have just been experimenting, you might have a good use case for us. 😉 Please don’t keep it in secret. Share it with us!

Our documentation team often gets feedback like this 🗣:

“Please add more samples to documentation”.

“It’s not clear without real-world examples”.

“Could you provide samples with detailed explanations?”

Please help us provide more real-world examples in our language docs and make it more helpful for developers like you.

Share a language feature sample

Don’t miss the Kotlin 1.5.0 videos

The Kotlin YouTube channel is available for those of you who like watching videos. We are continually updating the channel with new videos, and we plan to publish a video series dedicated to Kotlin 1.5.0. Be the first to watch 🎥!

Subscribe to Kotlin YouTube


Haven’t validated your projects with Kotlin 1.5.0 yet? Install 1.5.0-M2 now to avoid issues in the future.

Continue Reading Kotlin 1.5.0-M2 Released – Ensure Smooth Migration to Kotlin 1.5.0

Kotlin 1.4-M3: Generating Default Methods in Interfaces

In Kotlin 1.4, we’re adding new experimental ways for generating default methods in interfaces in the bytecode for the Java 8 target. Later, we’re going to be deprecating the

@JvmDefault

annotation in favor of generating all the method bodies in interfaces directly when the code is compiled in a special mode. Read more details of how it currently works and what will change, below.

In Kotlin, you can define methods with bodies in interfaces. It works if your code runs on Java 6 or 7, even before support for the default methods appeared on the JVM.

interface Alien {
   fun speak() = "Wubba lubba dub dub"
}

class BirdPerson : Alien

To make it work for older Java versions, the Kotlin compiler generates an additional class that contains an implementation of a default method as a static member. This is what the generated code looks like under the hood, at the bytecode level:

public interface Alien {
  String speak();

  public static final class DefaultImpls {
     public static String speak(Alien obj) {
        return "Wubba lubba dub dub";
     }
  }
}
public final class BirdPerson implements Alien {
  public String speak() {
    return Alien.DefaultImpls.speak(this);
  }
}

The Kotlin compiler generates the

DefaultImpls

class with the

speak

method. This method contains the default implementation. It takes an instance of an interface as a parameter and interprets it as

this

(in case you call other members of this interface inside). The class

BirdPerson

implementing the interface contains the same method, which only delegates to the implementation in

DefaultImpls

passing an actual

this

as an argument.

In Kotlin 1.2, we added experimental support for

@JvmDefault

annotation that works if your code targets Java 8. You can annotate each interface method having default implementation with

@JvmDefault

in order to get the default implementation generated in the bytecode:

interface Alien {
   @JvmDefault
   fun speak() = "Wubba lubba dub dub"
}

class BirdPerson : Alien

That only works in a special compiler mode: you can only use it when you specify the

-Xjvm-default

compiler argument.

The

@JvmDefault

annotation is going to be deprecated later. There’s no need to annotate each member with it; most probably you had to annotate all the interface methods with bodies, and it was quite verbose.

Eventually, we want to generate method bodies in interfaces by default when your code targets Java 8 or higher. It’s not easy to quickly make this change: we want to make sure you don’t have problems when you mix the libraries or modules of your application that are compiled with different Kotlin versions and different modes. The Kotlin compiler for future versions will continue to “understand” the old scheme of default methods, but we’ll slowly migrate to the new scheme.

New modes for generating default methods in interfaces

If your code targets Java 8 and you want to generate default methods in interfaces, you can use one of two new modes in Kotlin 1.4:

-Xjvm-default=all

or

-Xjvm-default=all-compatibility

.

In

all

mode, you only have default methods generated by the compiler, no more

DefaultImpls

objects, and no need to additionally annotate separate methods. This is the generated code for our initial sample:

// -Xjvm-default=all
public interface Alien {
  default String speak() {
     return "Wubba lubba dub dub";
 }
}
public final class BirdPerson implements Alien {}

Note that class

BirdPerson

implementing the interface doesn’t contain the

speak

method: it automatically reuses the “super” implementation thanks to the JVM support.

The Kotlin compiler of the newer versions will “understand” the old scheme. If your class compiled with the new scheme implements an interface compiled with the old scheme (with

DefaultImpls

), the compiler will recognize this and generate a hidden method in the class that delegates to the corresponding

DefaultImpls

method, as before.

The only problem that may arise is if you recompile your old code with the default method implementation and some other code depends on it, which you don’t recompile. In this case, use the

all-compatibility

mode. Then both default method bodies and

DefaultImpls

classes are generated:

// -Xjvm-default=all-compatibility
public interface Alien {
  default String speak() {
     return "Wubba lubba dub dub";
  }

  public static final class DefaultImpls {
     public static String speak(Alien obj) {
        // Calling the default method from the interface:
        return obj.$default$speak();
     }
  }
}
public final class BirdPerson implements Alien {}

Inside

DefaultImpls

the Kotlin compiler calls specifically the default method defined in the interface. (To make it a non-virtual call, the compiler makes a special trick: it generates an additional synthetic method inside an interface and calls it instead.)

With

all-compatibility

mode you don’t need to recompile the classes that already use your interface; they continue to work correctly:

public final class Moopian implements Alien {
  public String speak() {
    return Alien.DefaultImpls.speak(this);
  }
}

all-compatibility

mode guarantees binary compatibility for Kotlin clients but generates more methods and classes in the bytecode.

Fixing an issue with delegates

Before, it was a bit confusing to use an interface with

@JvmDefault

methods together with the “implementation by delegation” feature. If you used an interface with

@JvmDefault

as a delegate, the default method implementations were called even if the actual delegate type provided its own implementation:

interface Producer {
   fun produce() = "in interface"
}

class ProducerImpl : Producer {
   override fun produce() = "in class"
}

class DelegatedProducer(val p: Producer) : Producer by p

fun main() {
   val prod = ProducerImpl()
   // prints "in interface" if 'produce()' is annotated with @JvmDefault
   // prints "in class" in new jvm-default modes
   println(DelegatedProducer(prod).produce())
}

With the new jvm-default modes, it works as you would expect: the overridden version of

produce

is called when you delegate your implementation to the

ProducerImpl

class.

@JvmDefaultWithoutCompatibility

If you compile your code with

all-compatibility

mode and add a new interface, you can annotate it with the

@JvmDefaultWithoutCompatibility

annotation. It turns on “no compatibility mode” (

-Xjvm-default=all

) for this specific class. This way, no

DefaultImpls

objects will be generated. Since you’ve just added a new interface, there’s no code that calls it via the old scheme, and nothing can break.

To be precise, in

all-compatibility

mode you can use

@JvmDefaultWithoutCompatibility

to annotate all interfaces which aren’t a part of the public API (more correct is to consider public binary interface, and to say ABI), and therefore aren’t used by the existing clients.

More about all-compatibility mode for library authors

The

all-compatibility

mode is designed specifically for library authors to allow them to switch to the new scheme gradually and guarantee the binary compatibility for the library. And so, the following details and compatibility issues are aimed mainly at library authors.

Guaranteeing the binary compatibility between the new and old schemes is not totally “seamless”.
To prevent compatibility issues that might arise, the compiler reports an error in specific corner cases, while the

@JvmDefaultWithoutCompatibility

annotation suppresses this error. The following section describes the reasons for it and the use cases.

Consider a class that inherits from a generic interface:

interface LibGeneric<T> {
   fun foo(p: T): T = p
}

open class LibString : LibGeneric<String>

In

-Xjvm-default=all-compatibility

mode, the Kotlin compiler generates an error. Let’s first see why and then discuss how you can fix it.

Under the hood, to make such code work with

DefaultImpls

scheme, the Kotlin compiler of the previous version (or without using any

-Xjvm-default

flags) generates an additional method with the specialized signature in the class:

open class LibString {
   // Generated implicitly:
   fun foo(String): String { ... }
}

Sometimes, this specialized method is called in the generated bytecode. In pure Kotlin, it happens only in rare cases when your

LibString

class is open and you call

foo

from a subclass of

LibString

via

super.foo()

. In mixed projects, if you use this code from Java, the specialized version gets called every time you call

foo

on a

LibString

instance!

Without such override, you could easily break the binary compatibility: if you recompiled your

LibString

class with the new

all-compatibility

mode, and run it against the old binaries, you could get a

NoSuchMethodError

error!

The goal of

all-compatibility

mode is to guarantee binary compatibility at least for the Kotlin clients. That’s why having unexpected NoSuchMethodError errors is unacceptable. In order to prevent this, the Kotlin compiler could potentially generate the same hidden specialized method as before, however, it would cause problems when updating from

all-compatibility

to

all

mode, and it would also have issues with using default methods in diamond hierarchies. Generating such auxiliary implicit methods was necessary with the

DefaultImpls

scheme but is not needed when

default

methods are supported on the JVM level and can cause more confusion (for more details see Appendix: why we don’t like implicit methods).

We decided to prevent this binary compatibility problem by making your choice explicit.

Fixing the compiler error

One option you have is to provide an explicit override:

interface LibGeneric<T> {
   fun foo(p: T): T = p
}

open class LibString : LibGeneric<String> {
   override fun foo(p: String): String = super.foo(p)
}

Yes, it’s a bit of verbosity but for a good reason! If this code can be used from subclasses in Kotlin or from Java, adding explicit override guarantees that the older binaries will continue to work with new versions of your library compiled in

all-compatibility

mode.

Another option is to annotate your class with the

@JvmDefaultWithoutCompatibility

annotation. It turns on “no compatibility mode” for this specific class. Then an explicit override method is not required and no implicit methods are generated:

interface LibGeneric<T> {
   fun foo(p: T): T = p
}

@JvmDefaultWithoutCompatibility
open class LibString : LibGeneric<String> {
    // no implicit member
}

Appendix: Why we don’t like implicit methods

Why don’t we generate hidden methods like in the old scheme? Consider the following diagram which represents a diamond hierarchy – the Java class

JavaClass

implements the Kotlin

Base

interface both through extending

KotlinClass

and implementing

Derived

interface:

Let’s imagine that Kotlin continues to generate implicit overrides (as was necessary before with the

DefaultImpls

scheme). Then the code

JavaClass().foo()

prints

0

and not

42

! That becomes a new puzzler: there are only two methods (returning

0

and returning

42

) and it’s really confusing why the method from the base class is called and not the more specific one from

Derived

. When you take into consideration an implicit method from

KotlinClass

, the result makes sense. But we really want to avoid such puzzlers by not generating the implicit methods in the first place – and rather force developers to provide explicit methods when it’s necessary for compatibility reasons.

Conclusion

If you used the

@JvmDefault

annotation before, you can safely remove it and use one of the new modes. If you already used

-Xjvm-default=enable

, which generated only the default method implementations, you can now replace it with

-Xjvm-default=all

.

So far this support remains experimental but we’re going to switch the default mode continuously first to

all-compatibility

and then to

all

in the future major Kotlin versions. If no

-Xjvm-default

is specified now, the generated code will continue to use

DefaultImpls

.

How to try it

You can already try these new modes with Kotlin 1.4-M3 version. See here how to update the Kotlin Plugin to it.

Share your feedback

We’re grateful for all your bug reports in our issue tracker, and we’ll do our best to fix all the most important issues before the final release.

You are also welcome to join the #eap channel in our Kotlin Slack (get an invite here). In this channel, you can ask questions, participate in discussions, and get notifications of new preview builds.

Let’s Kotlin!

Continue Reading Kotlin 1.4-M3: Generating Default Methods in Interfaces

End of content

No more pages to load