KotlinからJavaのメソッドを呼ぶ際のジェネリクス変性について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 817

mosa

score 208

いつもありがとうございます。

http://jenetics.io/
こちらにあるJavaのコードをkotlinで記述しようと思っています。

// 2.) Definition of the fitness function.
fun eval(gt: Genotype<BitGene>): Int {
  return gt.chromosome
      .`as`(BitChromosome::class.java)
      .bitCount()
}

fun main(vararg args: String) {

  // 1.) Define the genotype (factory) suitable for the problem.
  val gtf = Genotype.of(BitChromosome.of(10, 0.5))

  // 3.) Create the execution environment.
  val engine = Engine.builder({ gf: Genotype<BitGene> -> eval(gf) }, gtf)

  // 4.) Start the execution (evolution) and collect the result.
  val result = engine.stream()
      .limit(100)
      .collect(EvolutionResult.toBestGenotype())

  println("Hello World:\n$result")

}

「3.」のEngine.builder の部分でコンパイルエラーとなっています。
エラーは以下の通りです。

エラー

呼び出し先のメソッドの定義は以下の通りです。

public static <G extends Gene<?, G>, C extends Comparable<? super C>> Engine.Builder<G, C> builder(Function<? super Genotype<G>, ? extends C> ff, Factory<Genotype<G>> genotypeFactory) {
  return new Engine.Builder(genotypeFactory, ff);
}

ラムダ式  { gf: Genotype<BitGene> -> eval(gf) } の型は (Genotype<BitGene>) -> Int) です。

JavaとKotlinのジェネリクス変性について私が理解できていないか、理解が誤っていることが原因だと思うのですが、何が問題でどう記述するのが正しいかがわかりません。
ちょっと複雑なので混乱しているだけかもしれません。
ご教示いただければと思います。

Kotlin: 1.2.31
Java: 1.8.0_152-release-1136-b27 amd64
IntelliJ IDEA 2018.1.1 (Ultimate Edition) Build #IU-181.4445.78
Windows10 


■追記1

「3.」の部分を以下のように書き換えたところ、コンパイルは通ったのですが、実行時に以下のエラーとなってしまいました。

val engine = Engine.builder(java.util.function.Function { gf: Genotype<BitGene> -> eval(gf) }, gtf).build()

IDEが出力したエラー

Error:Kotlin: [Internal Error] org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Error type encountered: (???..???) (FlexibleTypeImpl).
Cause: Error type encountered: (???..???) (FlexibleTypeImpl).
File being compiled at position: (26,3) in C:/Users/mosa/Documents/hoge/src/main/java/HelloWorld.kt
The root cause was thrown at: KotlinTypeMapper.java:122
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:328)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.genStatement(ExpressionCodegen.java:372)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.generateBlock(ExpressionCodegen.java:1193)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.generateBlock(ExpressionCodegen.java:1138)
    at org.jetbrains.kotlin.codegen.CodegenStatementVisitor.visitBlockExpression(CodegenStatementVisitor.java:56)
    at org.jetbrains.kotlin.codegen.CodegenStatementVisitor.visitBlockExpression(CodegenStatementVisitor.java:22)
    at org.jetbrains.kotlin.psi.KtBlockExpression.accept(KtBlockExpression.java:44)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:307)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.genStatement(ExpressionCodegen.java:372)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.gen(ExpressionCodegen.java:338)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.returnExpression(ExpressionCodegen.java:1590)
        ・
        ・
        ・

■追記2

「3.」の部分を以下のように書き換えたところ、追記1と同じ現象になってしましました。

  val func: java.util.function.Function<Genotype<BitGene>, Int> = java.util.function.Function { gf: Genotype<BitGene> -> eval(gf) }
  val engine = Engine.builder(func, gtf).build()

■追記3

元の問題についてはだいぶわかりました。変性は関係ありませんでした。
当たり前なのかもしれませんが、 Java の Function を引数にとるメソッドに Kotlin のラムダ式を渡そうとしても型が違うのでコンパイルが通らない、ということを理解していませんでした。

// これの型は (Genotype<BitGene>) -> Int
val hoge = { gf: Genotype<BitGene> -> eval(gf) }

// これの型は java.util.function.Function<Genotype<GitGene>, Int>
val piyo = java.util.function.Function { gf: Genotype<BitGene> -> eval(gf) }

■追記4

KSwordOfHasteさんに教えていただいた結果、動作するようにできました。
ご指摘のとおり、kotlinのコンパイラのバグのようです。
Collectorのメソッド型引数を明示的にすることで CompilationException 発生しなくなりました。

// 2.) Definition of the fitness function.
fun eval(gt: Genotype<BitGene>): Int {
  return gt.chromosome
      .`as`(BitChromosome::class.java)
      .bitCount()
}

fun main(vararg args: String) {

  // 1.) Define the genotype (factory) suitable for the problem.
  val gtf = Genotype.of(BitChromosome.of(10, 0.5))

  // 3.) Create the execution environment.
  val engine = Engine.builder(java.util.function.Function<Genotype<BitGene>, Int> { eval(it) }, gtf).build()

  // 4.) Start the execution (evolution) and collect the result.
  val result = engine.stream()
      .limit(100)
      .collect(EvolutionResult.toBestGenotype<BitGene, Int>())

  println("Hello World:\n$result")

}

ちなみに結果は以下のようになり、遺伝的アルゴリズムにより10個体100世代で10bitのうち最もビットが立つものを探索する、というものでした。

Hello World:
[00000011|11111111]

ありがとうございました。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • mosa

    2018/04/16 20:08

    もうちょっとかかります。すみません。

    キャンセル

  • mosa

    2018/04/17 17:11

    別のPCで「追記1」「追記2」を実行してみましたが、同じようにCompilationExceptionが発生してしまいました。 元の問題についてはご指摘いただいたように型を詳しく調べたらだいぶ理解できてきました。ありがとうございます。 「追記3」に記載しました。私のところだけPCを変えてもCompilationExceptionが発生してしまう理由はまだわかりません。

    キャンセル

  • mosa

    2018/04/17 22:41

    追記4を記載しました。ありがとうございました。

    キャンセル

回答 1

checkベストアンサー

+2

追記2の方ですが当方でも再現しました。

そのまま載せても見づらいので、横長にはなりますがコードタグで囲みます。

Information:Kotlin: kotlinc-jvm 1.2.31 (JRE 1.8.0_162-b12)
Information:2018/04/17 17:52 - Compilation completed with 1 error and 0 warnings in 29 s 830 ms
Error:Kotlin: [Internal Error] org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Error type encountered: (???..???) (FlexibleTypeImpl).
Cause: Error type encountered: (???..???) (FlexibleTypeImpl).
File being compiled at position: (28,5) in C:/idea2016-3/KotTest2/src/g/gg.kt
The root cause was thrown at: KotlinTypeMapper.java:122
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.genQualified(ExpressionCodegen.java:328)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.genStatement(ExpressionCodegen.java:372)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.generateBlock(ExpressionCodegen.java:1193)
    at org.jetbrains.kotlin.codegen.ExpressionCodegen.generateBlock(ExpressionCodegen.java:1138)
...省略

Caused by: java.lang.IllegalStateException: Error type encountered: (???..???) (FlexibleTypeImpl).
    at org.jetbrains.kotlin.codegen.state.KotlinTypeMapper$1.processErrorType(KotlinTypeMapper.java:122)
    at org.jetbrains.kotlin.load.kotlin.TypeSignatureMappingKt.mapType(typeSignatureMapping.kt:104)
         ^^^^^^ 多分ここで何かまずいことが起きた

    at org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.mapType(KotlinTypeMapper.java:436)

    at org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.writeGenericArguments(KotlinTypeMapper.java:585)
    at org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.writeGenericArguments(KotlinTypeMapper.java:559)
    at org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.writeGenericType(KotlinTypeMapper.java:501)

当方の再現確認でコンパイラーが死んでしまったのは

File being compiled at position: (28,5) in C:/idea2016-3/KotTest2/src/g/gg.kt

と出ていることから28行目です。そこに書かれているのは以下の行です。

val result = engine.stream()
        .limit(100)
        .collect(EvolutionResult.toBestGenotype())

GitHubには大分先のバージョン(1.3.50)のソースのためか例外が起きたあたりのソースの行番号はずれずれですが、Kotlinの型をJavaの型へマッピングするようなところなのかなぁと何となくですが想像しました。おそらくcollectの結果の型を料理する過程で何かがおきたんじゃないでしょうか。

いずれにせよコンパイラーのバグに思えるので対処は「それが起きないような平易な書き方を探る」ということになると思います。

賢く型推論をしてくれたりするコンパイラーはありがたいはありがたいのですが、コンパイラーがバグっていたのではしかたありません。ガチガチの書き方にしてみてください。例えばtoBestGenotypeに明示的に型引数を指定して見たらどうでしょうか?

残念ながら自分はこのライブラリー(遺伝的アルゴリズムの計算ライブラリーでしょうか?)について何も知らないので、質問者さんがresultにどういう型を期待しておられるのかがわからず、これ以上は試せてません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/18 19:20

    ありがとうございます。InteliJの IDE Fatal Error Report からは報告済みです。(どこに反映されているのかがよくわかっていませんが。。)jenetics.io を抜いた状態で再現する最小のコードがわかりましたら報告してみます。

    キャンセル

  • 2018/04/19 00:22

    >>KSwordOfHasteさんへ
    コメント欄をお借りして失礼しました。返信ありがとうございました。

    >>mosaさんへ
    Issue Trackerへの登録報告ありがとうございました。

    キャンセル

  • 2018/04/19 13:53

    こちらこそありがとうございます。かなり先になるかと思いますが、バグの解消を確認、もしくはバグが再現する最小コードがわかりましたら、こちらにもご報告しようかと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる