33
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KotlinでDeprecatedなメソッドの代替への置換をサジェストする

Last updated at Posted at 2016-03-10

はじめに

ライブラリや共通で使われる処理を作っていると諸事情によりAPIをDeprecatedしたくなることがあると思います。
KotlinでもJavaと同じくDeprecatedアノテーションが使えるのですが、Kotlinでは代替のAPIへの置換をユーザにサジェストすることが出来ます。

今回、実際にDeprecatedを書いてみて非常に便利だったので紹介します。

実際の例

拙作のKotlin製ライブラリKotprefで、KotprefというobjectのbulkをDeprecatedして、拡張関数bulkに置き換える例で見てみます。

変更前(Kotpref.kt)
object Kotpref {
    // 省略…
    
    fun <T : KotprefModel> bulk(receiver: T, f: T.() -> Unit) {
        // 省略…
    }
}
変更後(KotprefExtensions.kt)
inline fun <T : KotprefModel> T.bulk(block: T.() -> Unit) {
    // 省略…
}

このbulkは利用側では以下のようになっています。
Deprecatedアノテーションを使うことで、このソースに対して置換を行う方法について考えていきます。

object UserInfo : KotprefModel() {
    var name: String by stringPrefVar()
}

class Example {
   fun deprecateExample() {
        Kotpref.bulk(UserInfo) {
            name = "chibatching"
        }
   }
}

Deprecatedアノテーション

KotlinのDeprecatedアノテーションは最大3つの引数を取ります。

引数 意味
message 必須。ユーザに表示するメッセージ、空にしてもDeprecatedである旨は表示される。
replaceWith 今回の肝、置換の方法を指定する。次で説明。
level Deprecatedのレベルを指定する。デフォルトWARN
HIDDENを指定すると単純にAPIが見えなくなってしまい置換のサジェストもできないので注意。

ReplaceWith

今回の肝となるReplaceWithアノテーションは次のように定義されています

public annotation class ReplaceWith(val expression: String, vararg val imports: String)

このReplaceWithアノテーションの一つ目の引数expressionで置換の方法を指定することが出来ます。
利用側では下のように置換できればDeprecatedされたAPIの移行を完了できるので、この置換ができるようなexpressionを考えてみます。

Kotpref.bulk(UserInfo) {
    name = "chibatching"
}
// いい感じに↓のように置換したい
UserInfo.bulk {
    name = "chibatching"
}

expressionを考える

ところで、あるAPIをDeprecatedして新しいAPIに移行したい時、DeprecatedしたAPIの実装はどうしてますか?
そのままというパターンもあると思いますが、新しいAPIを呼び出す実装にしていることが多いのではないでしょうか?
例えば、今回の場合だと↓こんな具合で。

fun <T : KotprefModel> bulk(receiver: T, f: T.() -> Unit) {
    receiver.bulk(f) // 廃止するAPIでは新設するAPIを実行する
}

inline fun <T : KotprefModel> T.bulk(block: T.() -> Unit) {
    // 省略…
}

なぜ突然こんなことを言い出したかというと、この新しいAPIを呼び出す部分。これがそのままexpressionとして使えるんです!
なので、DeprecatedしたいAPIの宣言はこうなります。

@Deprecated(
        message = "",
        replaceWith = ReplaceWith("receiver.bulk(f)")
)
fun <T : KotprefModel> bulk(receiver: T, f: T.() -> Unit) {
    receiver.bulk(f)
}

これだけで、IDE上でDeprecatedの警告と一緒に新しいAPIへの置換をサジェスト&自動置換ができるようになってしまいます。
実際に置換を行ってみるとAndroidStudio上では↓のようになります。

replacewith.gif

このようにメソッドの引数やブロックをいい感じに展開して置換してくれていることがわかります。
また、明示的にimportを追加したい場合にもReplaceWithの第2引数で指定することが可能です。

公式ドキュメントだけではどのような書き方が出来るのかほとんどわからないので、他にどんな書き方ができるかはKotlinのソースを見てみるのがいいと思います。
Search Results

さいごに

引数が増える場合など、今回の例のように綺麗に置換できないパターンも多いとは思いますが、うまく一発で置換できると気持ちいいです。
また、一括置換もできるのでReplaceWithがしっかりと機能するようにしておけば、ユーザ側の新APIへの移行もスムーズに行えるのではないでしょうか。

33
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?