Help us understand the problem. What is going on with this article?

Kotlinから使えないJavaコード(社内勉強会用)

Kotlinから使えないJavaコード(社内勉強会用)

by komitake
1 / 9
  • 概要
    • 業務でハマった問題について発表します
    • 実際のところ私もよく分ってないので、ぜひ分かる方はコメント下さい
  • 前提
    • Android Kotlin/JVMで、Java製のLibraryをKotlinから利用

実際にハマったコード

Desk.java
package library_package;

abstract class BaseDesk {
    public enum Color { BLUE, ORANGE }
}

public class Desk extends BaseDesk {
    private Color color;

    public Desk(Color color) {
        this.color = color;
    }
}

こういうJava実装を、Javaから使うのは、当然うまくいく。

java
new Desk(Desk.Color.BLUE);

Kotlinから使うと、Compile Errorになる。

kotlin
Desk(Desk.Color.BLUE) // Unresolved reference: Color
kotlin
import library_package.BaseDesk // Cannot access 'BaseDesk': it is package-private in 'library_package'

Desk(BaseDesk.Color.BLUE) // Cannot access 'Color': it is public in 'BaseDesk'

どう回避したか

この問題を解決するために(だろうか?) getColorEnum() という static method が用意されていた。

Desk.java
package library_package;

abstract class BaseDesk {
    public enum Color { BLUE, ORANGE }

    public static Color getColorEnum(String name) {
        return Color.valueOf(name);
    }
}
kotlin
@Suppress("INACCESSIBLE_TYPE") // suppress warning は必要だが
Desk(Desk.getColorEnum("BLUE")) // DeskコンストラクタにColorインスタンスを入れることが出来た

なぜエラーになったり/ならなかったり?

どういうケースでエラーとなるのか見てみると...

Foo.java
class BaseFoo {
    public static int i = 0;

    public static int inc() {
        return ++i;
    }

    public static class FooFoo {
        public static int j = 0;
    }
}

public class Foo extends BaseFoo {}
java
int foo_i = Foo.i;
int foo_inc = Foo.inc();
int foo_foo_j = Foo.FooFoo.j;
kotlin
val foo_i = Foo.i
val foo_inc = Foo.inc()
val foo_foo_j: Int = Foo.FooFoo.j // Unresolved reference: FooFoo

Kotlinでは、super class の static inner class にアクセスできないようだ。
根拠となる言語仕様を軽く調べてみたが、わからなかった。


なぜメソッドの戻り値としては使えたのか?

Javaは、もともと不可視なクラスを戻り値に定義できるようだ。

public class Bar {
    public static InternalBar getBar() {
        return new InternalBar();
    }

    private static class InternalBar extends Bar {}
}

Bar bar = Bar.getBar(); // OK

Kotlinでは、こんな事はできない。

open class Baz {
    companion object {
        fun getInternalBaz(): InternalBaz { // 'public' function exposes its 'private' return type InternalBaz
            return InternalBaz()
        }

        fun getBaz(): Baz { // OK
            return InternalBaz()
        }
    }
    private class InternalBaz : Baz()
}

INACCESSIBLE_TYPE warning とは?

参照できない場合にエラーとするKotlin Compilerの挙動が、前述のようなJavaの仕様と上手く合致せずにコンパイルできない問題があり、いまはCompile ErrorではなくWarningにしているが、今後適切に、本当にアクセス不可な場合だけコンパイルエラーとするように再変更する予定、ということらしい。

https://youtrack.jetbrains.com/issue/KT-11398 を参照。


もし Library 側に getColorEnum() が用意されてなかったら?

自分でJavaでUtility Methodを作ればよい。

DeskColorResolver.java
import library_package.Desk;

public class DeskColorResolver {
    public static Desk.Color getColor(String name) {
        return Desk.Color.valueOf(name);
    }
}
kotlin
val desk = Desk(DeskColorResolver.getColorEnum("BLUE"))

と書ける。
本当にこれが正解だろうか...?


thanks!

komitake
access
SDNからセンサ、家電、電子書籍まで。ACCESSはあらゆるレイヤのデバイス、サービスを「繋げて」いきます。
http://jp.access-company.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away