ITメモ

LinuxやMac関連を中心に技術的に役立つ情報をメモしています。 参考になったら、一言コメントでも残してくれると有り難いです。

カテゴリ: Programing

※翻訳がわかりにくい場合は原文を読んでください。


KotlinコードはJavaから簡単に呼び出すことができます。

プロパティ

Kotlinプロパティは、次のJava要素にコンパイルされます。

  • get接頭辞を前置することによって計算された名前を持つgetterメソッド
  • set接頭辞を前置することによって計算された名前を持つsetterメソッド(varプロパティに対してのみ)
  • プロパティ名と同じ名前のプライベートフィールド(バッキングフィールドを持つプロパティのみ)

例えば、 var firstName: String は以下のJava宣言にコンパイルされます

プロパティの名前が isで始まる場合、別の名前マッピング規則が使用されます。ゲッターの名前は次のようになります。 プロパティ名と同じで、setterの名前は issetに置き換えることで得られます。 たとえば、プロパティisOpenの場合、getterはisOpen()と呼ばれ、setterはsetOpen()と呼ばれます。 このルールは、 Booleanだけでなくあらゆるタイプのプロパティに適用されます。

パッケージレベルの関数

拡張機能を含む org.foo.barパッケージ内のexample.ktファイル内で宣言されたすべての関数とプロパティは、 org.foo.bar.ExampleKt`という名前のJavaクラスの静的メソッドにコンパイルされます。

生成されたJavaクラスの名前は、 @JvmNameアノテーションを使用して変更できます。

生成された同じJavaクラス名を持つ複数のファイルを持つ(同じパッケージで同じ名前または同じ @JvmNameアノテーション)は通常エラーです。 ただし、コンパイラには、指定された名前を持ち、その名前を持つすべてのファイルのすべての宣言が含まれる単一のJavaファサードクラスを生成する機能があります。 このようなファサードの生成を可能にするには、すべてのファイルで@JvmMultifileClassアノテーションを使用します。

インスタンスフィールド

JavaのフィールドとしてKotlinプロパティを公開する必要がある場合は、それを @JvmFieldアノテーションでアノテートする必要があります。 フィールドは、基になるプロパティと同じ可視性を持ちます。 @JvmFieldにバッキングフィールドがある、プライベートでない、 openoverrideconst修飾子を持たず、委譲されたプロパティでない場合、プロパティに注釈を付けることができます。

Late-Initialized プロパティもフィールドとして公開されます。 フィールドの可視性は、 lateinitプロパティーセッターの可視性と同じになります。

静的フィールド

名前付きオブジェクトまたはコンパニオンオブジェクトで宣言されたKotlinプロパティは、名前付きオブジェクトまたはコンパニオンオブジェクトを含むクラスの静的なバッキングフィールドを持ちます。

通常、これらのフィールドはプライベートですが、次のいずれかの方法で公開することができます。

  • @JvmField アノテーション
  • lateinit 修飾子
  • const 修飾子

このようなプロパティに @JvmFieldを付けると、プロパティ自体と同じ可視性を持つ静的フィールドになります。

オブジェクトまたはコンパニオンオブジェクトの late-initialized プロパティはプロパティセッターと同じ可視性を持つ静的なバッキングフィールドを持っています。

constでアノテーションされたプロパティ(クラス内とトップレベル)は、Javaの静的フィールドに変換されます

Javaの場合:

静的メソッド

前述のように、Kotlinは静的メソッドとしてパッケージレベルの関数を表します。 これらの関数に @JvmStaticと注釈を付けると、Kotlinは名前付きオブジェクトやコンパニオンオブジェクトで定義された関数の静的メソッドを生成することもできます。 このアノテーションを使用すると、コンパイラはオブジェクトの囲むクラスに静的メソッドを生成し、オブジェクト自体にインスタンスメソッドを生成します。 例えば、

今、 foo()はJavaでは静的ですが、 bar()は静的ではありません

名前付きオブジェクトと同じ

Javaの場合:

@JvmStaticアノテーションは、オブジェクトまたはコンパニオンオブジェクトのプロパティに適用することもできます。そのため、getterメソッドとsetterメソッドは、そのオブジェクトまたはコンパニオンオブジェクトを含むクラスの静的メンバになります。

## 可視性

Kotlinの可視性は、次のようにJavaにマッピングされます。

  • privateメンバはprivateメンバにコンパイルされます。
  • privateトップレベル宣言はパッケージローカル宣言にコンパイルされます。
  • protectedprotectedのままです(Javaは同じパッケージ内の他のクラスから保護されたメンバーにアクセスすることができます Kotlinはそうしないので、Javaクラスはコードへのより広いアクセスを持つでしょう)。
  • internal宣言はJavaでpublicになります。 internalクラスのメンバは、誤ってJavaからそれらを使用するのを困難にし、Kotlinの規則に従ってお互いに見えない同じ署名を持つメンバのためにオーバーロードを許容するために、名前修飾を受けます。
  • publicpublicのままです。

## Kクラス

時々 KClass型のパラメータでKotlinメソッドを呼び出す必要があります。 ClassからKClassへの自動変換はありませんので、 Class <T> .kotlin拡張プロパティと同等のものを呼び出すことで手動で行う必要があります:

@JvmNameで署名の衝突を処理する

Kotlinには、バイトコードとは異なるJVM名が必要な名前付き関数があります。 最も顕著な例は型消去のために起こります。

これら2つの関数は、JVMのシグネチャが同じ filterValid(Ljava/util/List;)Ljava/util/List; であるため、並行して定義することはできません。 Kotlinに同じ名前をつけたいのであれば、そのうちの1つ(または両方)に @JvmNameを付けて、別の名前を引数として指定することができます。

Kotlinからは filterValidと同じ名前でアクセスできますが、JavaからはfilterValidfilterValidIntがあります。

同じトリックが、 getX()関数の横にプロパティ xを持つ必要があるときに適用されます。

オーバーロード生成

通常、デフォルトのパラメータ値を持つKotlin関数を記述すると、Javaでは完全なシグネチャとしてのみ表示され、すべてのパラメータが表示されます。複数のオーバーロードをJava呼び出し側に公開する場合は、 @JvmOverloadsアノテーションを使用できます。

注釈は、コンストラクタ、静的メソッドなどにも使用できます。インタフェースで定義されたメソッドを含む抽象メソッドでは使用できません。

デフォルト値を持つすべてのパラメータについて、これは追加のオーバーロードを1つ生成します。このオーバーロードには、このパラメータがあり、パラメータリストのその右側のすべてのパラメータが削除されます。この例では、次のものが生成されます。

Secondary Constructorsで説明されているように、クラスにすべてのコンストラクタパラメータのデフォルト値がある場合は、引数なしのpublicコンストラクタが生成されます。これは、@JvmOverloadsアノテーションが指定されていなくても機能します。

チェックされた例外

上記のように、Kotlinは例外をチェックしていません。 したがって、通常、Kotlin関数のJavaシグネチャは例外がスローされたことを宣言しません。 したがって、Kotlinに次のような関数があるとします。

そして、Javaから呼び出して例外をキャッチしたい

foo()IOExceptionを宣言しないので、Javaコンパイラからエラーメッセージが出ます。 この問題を回避するには、Kotlinで @Throwsアノテーションを使用してください。

Null-safety

JavaからKotlin関数を呼び出すときに、null以外のパラメータとして null{: .keyword } を渡すことができなくなります。 だからこそ、Kotlinは、非nullを期待するすべてのパブリック関数のランタイムチェックを生成します。 このようにして、Javaコードで即座に NullPointerExceptionを取得します。

バリアントジェネリックス

Kotlinクラスが 宣言サイトの分散 を利用する場合、 その使用法がJavaコードからどのように見えるかという2つのオプションがあります。次のクラスとそれを使用する2つの関数があるとしましょう。

これらの関数をJavaに変換する素朴な方法は次のとおりです。

問題は、Kotlinでは unboxBase(boxDerived("s")) と言うことができますが、Javaでは不可能です。なぜなら、JavaではクラスBoxはパラメータT不変であるため、従って Box<Derived>Box<Base> のサブタイプではありません。 Javaで動作させるには、次のように unboxBaseを定義する必要があります。

ここでは、Javaのワイルドカード型? extends Base)を使用して、使用サイトの分散によって宣言サイトの分散をエミュレートします。なぜなら、それはJavaが持つすべてのものだからです。

Kotlin APIをJavaで動作させるために、パラメータとして現れるときに、共変に定義された Box(反逆的に定義されたFooの場合は Foo<? super Bar>)のBox<Super>Box<? extends Super>として生成します。戻り値の場合、ワイルドカードは生成されません。そうしなければ、Javaクライアントは対処する必要があります(一般的なJavaコーディングスタイルに反します)。したがって、この例の関数は実際には次のように変換されます。

注意:引数の型がfinalの場合、通常はワイルドカードを生成する点はないので、どの位置にあっても Box <String>は常に Box <String>です。

デフォルトで生成されないワイルドカードが必要な場合は、 @JvmWildcardアノテーションを使用できます。

一方、生成されるワイルドカードが必要ない場合は、 @ JvmSuppressWildcardsを使用することができます。

注意: @JvmSuppressWildcardsは、個々の型引数だけでなく、関数やクラスなどの宣言全体でも使用でき、その中のすべてのワイルドカードを抑止できます。

Nothing型の翻訳

Nothing は、Javaに自然な対応がないため特別です。 実際、 java.lang.Voidを含むすべてのJava参照型は値としてnullを受け取り、 Nothingはそれを受け入れません。したがって、このタイプはJavaの世界では正確に表現できません。 これがKotlinが Nothing型の引数が使われる生の型を生成する理由です:

  • キューの作成
(Swift2まで)
let queue1 = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_SERIAL)
let queue2 = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_CONCURRENT)
(Swift3以降)
let queue1 = DispatchQueue(label: "arrayQ")
let queue2= DispatchQueue(label: "arrayQ", attributes: .concurrent)

(Swift2まで)
let queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
let queue4 = dispatch_get_main_queue()
(Swift3以降)
let queue3 = DispatchQueue.global(qos:DispatchQoS.QoSClass.background)
let queue4= DispatchQueue.main

  • グループの作成
(Swift2まで)
let group = dispatch_group_create()
(Swift3以降)
let group = DispatchGroup()

  • グループのwait
(Swift2まで)
dispatch_group_wait(group, dispatch_time_t(DISPATCH_TIME_FOREVER))
(Swift3以降)
group.wait(wallTimeout: .distantFuture)

  • 同期処理
(Swift2まで)
dispatch_sync(queue, {() in
...
}
(Swift3以降)
queue.sync {() in
...
}

  • 非同期処理
(Swift2まで)
dispatch_async(queue, {() in
...
}
(Swift3以降)
queue.async {() in
...
}

(Swift2まで)
dispatch_barrier_async(queue, {() in
...
}
(Swift3以降)
queue.async(flags: .barrier) {() in
...
}

  • セマフォ宣言
(Swift2まで)
let semaphore:dispatch_semaphore_t
(Swift3以降)
let semaphore:DispatchSemaphore

  • セマフォの作成
(Swift2まで)
semaphore = dispatch_semaphore_create(Int)
(Swift3以降)
semaphore = DispatchSemaphore(value: Int)

  • セマフォのwait
(Swift2まで)
if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) == 0) { ... }
(Swift3以降)
if (semaphore.wait(timeout:.distantFuture) == .success) { ... }

  • セマフォのsignal
(Swift2まで)
dispatch_semaphore_signal(semaphore)
(Swift3以降)
semaphore.signal()

NSArrayにはremoveObjectというインデックスではなくオブジェクトを指定して配列内の該当する要素を削除するメソッドがあります。

同じものがSwiftのArrayにもあるかと思ったのですが無く困ってましたが、以下のようにextensionを使ってArrayを拡張すれば対応可能になります。

続きを読む

↑このページのトップヘ