{"id":58065,"date":"2020-07-16T12:10:36","date_gmt":"2020-07-16T11:10:36","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=kotlin&#038;p=58065"},"modified":"2023-08-08T16:22:19","modified_gmt":"2023-08-08T15:22:19","slug":"kotlin-1-4-m3-generating-default-methods-in-interfaces","status":"publish","type":"kotlin","link":"https:\/\/blog.jetbrains.com\/ja\/kotlin\/2020\/07\/kotlin-1-4-m3-generating-default-methods-in-interfaces","title":{"rendered":"Kotlin 1.4-M3: Generating Default Methods in Interfaces"},"content":{"rendered":"<p>In Kotlin 1.4, we\u2019re adding new experimental ways for generating default methods in interfaces in the bytecode for the Java 8 target. Later, we\u2019re going to be deprecating the <code>@JvmDefault<\/code> 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.<\/p>\n<p><a href=\"https:\/\/blog.jetbrains.com\/ja\/kotlin\/2020\/07\/kotlin-1-4-m3-is-out-standard-library-changes#how-to-try\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/Blog_Banners_m3_1500h751.png\" alt=\"\" width=\"1501\" height=\"752\" class=\"alignnone size-full wp-image-55596\" \/><\/a><\/p>\n<p>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. <\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\ninterface Alien {\r\n   fun speak() = &quot;Wubba lubba dub dub&quot;\r\n}\r\n\r\nclass BirdPerson : Alien\r\n<\/pre>\n<p>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:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\npublic interface Alien {\r\n  String speak();\r\n\r\n  public static final class DefaultImpls {\r\n     public static String speak(Alien obj) {\r\n        return &quot;Wubba lubba dub dub&quot;;\r\n     }\r\n  }\r\n}\r\npublic final class BirdPerson implements Alien {\r\n  public String speak() {\r\n    return Alien.DefaultImpls.speak(this);\r\n  }\r\n}\r\n<\/pre>\n<p>The Kotlin compiler generates the <code>DefaultImpls<\/code> class with the <code>speak<\/code> method. This method contains the default implementation. It takes an instance of an interface as a parameter and interprets it as <code>this<\/code> (in case you call other members of this interface inside). The class <code>BirdPerson<\/code> implementing the interface contains the same method, which only delegates to the implementation in <code>DefaultImpls<\/code> passing an actual <code>this<\/code> as an argument.<\/p>\n<p>In Kotlin 1.2, we added experimental support for <code>@JvmDefault<\/code> annotation that works if your code targets Java 8. You can annotate each interface method having default implementation with <code>@JvmDefault<\/code> in order to get the default implementation generated in the bytecode: <\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\ninterface Alien {\r\n   @JvmDefault\r\n   fun speak() = &quot;Wubba lubba dub dub&quot;\r\n}\r\n\r\nclass BirdPerson : Alien\r\n<\/pre>\n<p>That only works in <a href=\"https:\/\/kotlinlang.org\/api\/latest\/jvm\/stdlib\/kotlin.jvm\/-jvm-default\/\" target=\"_blank\" rel=\"noopener\">a special compiler mode<\/a>: you can only use it when you specify the <code>-Xjvm-default<\/code> compiler argument.<\/p>\n<p>The <code>@JvmDefault<\/code> annotation is going to be deprecated later. There\u2019s 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.<\/p>\n<p>Eventually, we want to generate method bodies in interfaces by default when your code targets Java 8 or higher. It\u2019s not easy to quickly make this change: we want to make sure you don\u2019t 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 \u201cunderstand\u201d the old scheme of default methods, but we\u2019ll slowly migrate to the new scheme.<\/p>\n<h3> New modes for generating default methods in interfaces <\/h3>\n<p>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: <code>-Xjvm-default=all<\/code> or <code>-Xjvm-default=all-compatibility<\/code>.<\/p>\n<p>In <code>all<\/code> mode, you only have default methods generated by the compiler, no more <code>DefaultImpls<\/code> objects, and no need to additionally annotate separate methods. This is the generated code for our initial sample:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\n\/\/ -Xjvm-default=all\r\npublic interface Alien {\r\n  default String speak() {\r\n     return &quot;Wubba lubba dub dub&quot;;\r\n }\r\n}\r\npublic final class BirdPerson implements Alien {}\r\n<\/pre>\n<p>Note that class <code>BirdPerson<\/code> implementing the interface doesn\u2019t contain the <code>speak<\/code> method: it automatically reuses the &quot;super&quot; implementation thanks to the JVM support.<\/p>\n<p>The Kotlin compiler of the newer versions will \u201cunderstand\u201d the old scheme. If your class compiled with the new scheme implements an interface compiled with the old scheme (with <code>DefaultImpls<\/code>), the compiler will recognize this and generate a hidden method in the class that delegates to the corresponding <code>DefaultImpls<\/code> method, as before.<\/p>\n<p>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\u2019t recompile. In this case, use the <code>all-compatibility<\/code> mode. Then both default method bodies and <code>DefaultImpls<\/code> classes are generated:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\n\/\/ -Xjvm-default=all-compatibility\r\npublic interface Alien {\r\n  default String speak() {\r\n     return &quot;Wubba lubba dub dub&quot;;\r\n  }\r\n\r\n  public static final class DefaultImpls {\r\n     public static String speak(Alien obj) {\r\n        \/\/ Calling the default method from the interface:\r\n        return obj.$default$speak();\r\n     }\r\n  }\r\n}\r\npublic final class BirdPerson implements Alien {}\r\n<\/pre>\n<p>Inside <code>DefaultImpls<\/code> 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.)<\/p>\n<p>With <code>all-compatibility<\/code> mode you don\u2019t need to recompile the classes that already use your interface; they continue to work correctly:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\npublic final class Moopian implements Alien {\r\n  public String speak() {\r\n    return Alien.DefaultImpls.speak(this);\r\n  }\r\n}\r\n<\/pre>\n<p><code>all-compatibility<\/code> mode guarantees binary compatibility for Kotlin clients but generates more methods and classes in the bytecode.<\/p>\n<h3> Fixing an issue with delegates <\/h3>\n<p>Before, it was a bit confusing to use an interface with <a href=\"https:\/\/kotlinlang.org\/api\/latest\/jvm\/stdlib\/kotlin.jvm\/-jvm-default\/\" target=\"_blank\" rel=\"noopener\"><code>@JvmDefault<\/code><\/a> methods together with the \u201cimplementation by delegation\u201d feature. If you used an interface with <code>@JvmDefault<\/code> as a <a href=\"https:\/\/kotlinlang.org\/docs\/reference\/delegation.html#implementation-by-delegation\" target=\"_blank\" rel=\"noopener\">delegate<\/a>, the default method implementations were called even if the actual delegate type provided its own implementation:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\ninterface Producer {\r\n   fun produce() = &quot;in interface&quot;\r\n}\r\n\r\nclass ProducerImpl : Producer {\r\n   override fun produce() = &quot;in class&quot;\r\n}\r\n\r\nclass DelegatedProducer(val p: Producer) : Producer by p\r\n\r\nfun main() {\r\n   val prod = ProducerImpl()\r\n   \/\/ prints &quot;in interface&quot; if &#039;produce()&#039; is annotated with @JvmDefault\r\n   \/\/ prints &quot;in class&quot; in new jvm-default modes\r\n   println(DelegatedProducer(prod).produce())\r\n}\r\n<\/pre>\n<p>With the new jvm-default modes, it works as you would expect: the overridden version of <code>produce<\/code> is called when you delegate your implementation to the <code>ProducerImpl<\/code> class.<\/p>\n<h3> @JvmDefaultWithoutCompatibility <\/h3>\n<p>If you compile your code with <code>all-compatibility<\/code> mode and add a new interface, you can annotate it with the <code>@JvmDefaultWithoutCompatibility<\/code> annotation. It turns on \u201cno compatibility mode\u201d (<code>-Xjvm-default=all<\/code>) for this specific class. This way, no <code>DefaultImpls<\/code> objects will be generated. Since you\u2019ve just added a new interface, there\u2019s no code that calls it via the old scheme, and nothing can break.<\/p>\n<p>To be precise, in <code>all-compatibility<\/code> mode you can use  <code>@JvmDefaultWithoutCompatibility<\/code> to annotate all interfaces which aren\u2019t a part of the public API (more correct is to consider public binary interface, and to say ABI), and therefore aren\u2019t used by the existing clients.<\/p>\n<h3> More about all-compatibility mode for library authors <\/h3>\n<p>The <code>all-compatibility<\/code> 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.<\/p>\n<p>Guaranteeing the binary compatibility between the new and old schemes is not totally \u201cseamless\u201d.<br \/>\nTo prevent compatibility issues that might arise, the compiler reports an error in specific corner cases, while the <code>@JvmDefaultWithoutCompatibility<\/code> annotation suppresses this error. The following section describes the reasons for it and the use cases.<\/p>\n<p>Consider a class that inherits from a generic interface:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\ninterface LibGeneric&lt;T&gt; {\r\n   fun foo(p: T): T = p\r\n}\r\n\r\nopen class LibString : LibGeneric&lt;String&gt;\r\n<\/pre>\n<p>In <code>-Xjvm-default=all-compatibility<\/code> mode, the Kotlin compiler generates an error. Let\u2019s first see why and then discuss how you can fix it.<\/p>\n<p>Under the hood, to make such code work with <code>DefaultImpls<\/code> scheme, the Kotlin compiler of the previous version (or without using any <code>-Xjvm-default<\/code> flags) generates an additional method with the specialized signature in the class:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nopen class LibString {\r\n   \/\/ Generated implicitly:\r\n   fun foo(String): String { ... }\r\n}\r\n<\/pre>\n<p>Sometimes, this specialized method is called in the generated bytecode. In pure Kotlin, it happens only in rare cases when your <code>LibString<\/code> class is open and you call <code>foo<\/code> from a subclass of <code>LibString<\/code> via <code>super.foo()<\/code>. In mixed projects, if you use this code from Java, the specialized version gets called every time you call <code>foo<\/code> on a <code>LibString<\/code> instance!<\/p>\n<p>Without such override, you could easily break the binary compatibility: if you recompiled your <code>LibString<\/code> class with the new <code>all-compatibility<\/code> mode, and run it against the old binaries, you could get a <code>NoSuchMethodError<\/code> error!<\/p>\n<p>The goal of <code>all-compatibility<\/code> mode is to guarantee binary compatibility at least for the Kotlin clients. That\u2019s 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 <code>all-compatibility<\/code> to <code>all<\/code> mode, and it would also have issues with using default methods in diamond hierarchies. Generating such auxiliary implicit methods was necessary with the <code>DefaultImpls<\/code> scheme but is not needed when <code>default<\/code> methods are supported on the JVM level and can cause more confusion (for more details see <a href=\"https:\/\/blog.jetbrains.com\/ja\/kotlin\/2020\/07\/kotlin-1-4-m3-generating-default-methods-in-interfaces#why-not-inline\">Appendix: why we don\u2019t like implicit methods<\/a>).<\/p>\n<p>We decided to prevent this binary compatibility problem by making your choice explicit.<\/p>\n<h3> Fixing the compiler error <\/h3>\n<p>One option you have is to provide an explicit override:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\ninterface LibGeneric&lt;T&gt; {\r\n   fun foo(p: T): T = p\r\n}\r\n\r\nopen class LibString : LibGeneric&lt;String&gt; {\r\n   override fun foo(p: String): String = super.foo(p)\r\n}\r\n<\/pre>\n<p>Yes, it\u2019s 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 <code>all-compatibility<\/code> mode.<\/p>\n<p>Another option is to annotate your class with the <code>@JvmDefaultWithoutCompatibility<\/code> annotation. It turns on \u201cno compatibility mode\u201d for this specific class. Then an explicit override method is not required and no implicit methods are generated: <\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\ninterface LibGeneric&lt;T&gt; {\r\n   fun foo(p: T): T = p\r\n}\r\n\r\n@JvmDefaultWithoutCompatibility\r\nopen class LibString : LibGeneric&lt;String&gt; {\r\n    \/\/ no implicit member\r\n}\r\n<\/pre>\n<h3 id=\u201dwhy-not-inline\u201d> Appendix: Why we don\u2019t like implicit methods <\/h3>\n<p>Why don\u2019t we generate hidden methods like in the old scheme? Consider the following diagram which represents a diamond hierarchy \u2013 the Java class <code>JavaClass<\/code> implements the Kotlin <code>Base<\/code> interface both through extending <code>KotlinClass<\/code> and implementing <code>Derived<\/code> interface:<\/p>\n<p><a href=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/JvmDefault-7.png\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/07\/JvmDefault-7.png\" alt=\"\" width=\"700\" class=\"alignnone size-full wp-image-58076\" \/><\/a><\/p>\n<p>Let\u2019s imagine that Kotlin continues to generate implicit overrides (as was necessary before with the <code>DefaultImpls<\/code> scheme). Then the code <code>JavaClass().foo()<\/code> prints <code>0<\/code> and not <code>42<\/code>! That becomes a new puzzler: there are only two methods (returning <code>0<\/code> and returning <code>42<\/code>) and it\u2019s really confusing why the method from the base class is called and not the more specific one from <code>Derived<\/code>. When you take into consideration an implicit method from <code>KotlinClass<\/code>, the result makes sense. But we really want to avoid such puzzlers by not generating the implicit methods in the first place \u2013 and rather force developers to provide explicit methods when it\u2019s necessary for compatibility reasons.<\/p>\n<h3> Conclusion <\/h3>\n<p>If you used the <code>@JvmDefault<\/code> annotation before, you can safely remove it and use one of the new modes. If you already used <code>-Xjvm-default=enable<\/code>, which generated only the default method implementations, you can now replace it with <code>-Xjvm-default=all<\/code>.<\/p>\n<p>So far this support remains experimental but we\u2019re going to switch the default mode continuously first to <code>all-compatibility<\/code> and then to <code>all<\/code> in the future major Kotlin versions. If no <code>-Xjvm-default<\/code> is specified now, the generated code will continue to use <code>DefaultImpls<\/code>.<\/p>\n<h3>  How to try it <\/h3>\n<p>You can already try these new modes with Kotlin 1.4-M3 version. See <a href=\"https:\/\/kotlinlang.org\/eap\/install-eap-plugin.html\" target=\"_blank\" rel=\"noopener\">here<\/a> how to update the Kotlin Plugin to it.<\/p>\n<h3> Share your feedback <\/h3>\n<p>We\u2019re grateful for all your bug reports in our <a href=\"https:\/\/youtrack.jetbrains.com\/issues\/KT\" target=\"_blank\" rel=\"noopener\">issue tracker<\/a>, and we\u2019ll do our best to fix all the most important issues before the final release.<\/p>\n<p>You are also welcome to join the <a href=\"https:\/\/app.slack.com\/client\/T09229ZC6\/C0KLZSCHF\" target=\"_blank\" rel=\"noopener\">#eap channel<\/a> in our <a href=\"https:\/\/app.slack.com\/client\/T09229ZC6\/C0KLZSCHF\" target=\"_blank\" rel=\"noopener\">Kotlin Slack<\/a> (get an invite <a href=\"http:\/\/slack.kotlinlang.org\/\" target=\"_blank\" rel=\"noopener\">here<\/a>). In this channel, you can ask questions, participate in discussions, and get notifications of new preview builds.<\/p>\n<p>Let\u2019s Kotlin!<\/p>\n","protected":false},"author":42,"featured_media":58310,"comment_status":"open","ping_status":"closed","template":"","categories":[907],"tags":[600,477],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/kotlin\/58065"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/kotlin"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/types\/kotlin"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/comments?post=58065"}],"version-history":[{"count":2,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/kotlin\/58065\/revisions"}],"predecessor-version":[{"id":379480,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/kotlin\/58065\/revisions\/379480"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/media\/58310"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/media?parent=58065"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/categories?post=58065"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/tags?post=58065"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ja\/wp-json\/wp\/v2\/cross-post-tag?post=58065"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}