kotlin Cloneableの奇妙な行為

2895 ワード

kotlin Cloneableの奇妙な行為
kotlinのCloneableを使っていると、変な表現をしていることに気づきました.クラスが直接Cloneableを継承している場合、その表現は正常でjavaの使用とあまり差がありません.以下のようにします.
package demo

interface Foo {
    fun createClone(): Foo
    fun doSomething()
}

class Bar: Foo, Cloneable {
    override fun createClone(): Bar {
        return this.clone() as Bar
    }

    override fun doSomething() {
        println("Hello, world!")
    }
}

fun main(args:Array) {
    val bar = Bar()
    bar.doSomething()

    val barCloned = bar.createClone()
    barCloned.doSomething()
}

この部分のコードは正常に動作しますが、インタフェースFooもCloneableを継承している場合、結果はおかしくなります.コードは次のとおりです.
package demo

interface Foo: Cloneable {
    fun createClone(): Foo
    fun doSomething()
}

class Bar: Foo, Cloneable {
    override fun createClone(): Bar {
        return this.clone() as Bar
    }

    override fun doSomething() {
        println("Hello, world!")
    }
}

fun main(args:Array) {
    val bar = Bar()
    bar.doSomething()

    val barCloned = bar.createClone()
    barCloned.doSomething()
}
bar.createClone()が呼び出されると、例外が発生します.
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Cloneable$DefaultImpls
    at demo.Foo$DefaultImpls.clone(demo.kt)
    at demo.Bar.clone(demo.kt:8)
    at demo.Bar.createClone(demo.kt:10)
    at demo.DemoKt.main(demo.kt:22)
Caused by: java.lang.ClassNotFoundException: java.lang.Cloneable$DefaultImpls
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 4 more

奇妙なことに、jclasslibを使用して生成されたclassファイルを分析し、興味深い状況を発見しました.
  • Barが直接Cloneableを継承する場合、Barのcloneメソッドは、
     0 aload_0
     1 invokespecial #47 
     4 areturn
    このときjava Objectを直接呼び出すcloneメソッド
  • である.
  • FooがCloneableを継承した場合、Barのcloneメソッドは:
     0 aload_0
     1 invokestatic #51 
     4 areturn
    ええ、ここで呼び出されたのはFooインタフェースデフォルト実装クラスです.java 8のインタフェースデフォルト実装です.故障もないようですが、Foo$DefaultImpls.clone
     0 aload_0
     1 checkcast #9 
     4 invokestatic #14 
     7 areturn
    を見てみると少し間違っているようです.Cloneableは古いインタフェースで、デフォルト実装はありません.jclasslibもCloneable$DefaultImplsクラスを見つけられなかった.

  • 問題が見つかりました.FooインタフェースにはCloneableが継承されていますが、BarがFooインタフェースを実装すると、kotlinコンパイル後のclassファイルにCloneable$DefaultImplsが呼び出され、kotlinはインタフェースのデフォルト実装を処理する上で問題があります.
    この問題はjetbrainsのkotlinコミュニティに報告されています.優先度はMajorです.KT-241993を参照してください.
    effiective javaによると、javaのCloneableは奇妙なインタフェースであり、通常のインタフェースの動作とは異なり、言語メカニズム自体を超える約束を使用して、オブジェクトの呼び出しclone()時の動作を示す.
    一般的にはclone()の使用は避け,類似の機能を実現するためにコピー構造関数を用いることが考えられる.