1. 概要

Javaのバックグラウンドから来ているので、 Deprecated アノテーションと、それがJavaランドでどのように機能するかをよく知っているかもしれません。

このチュートリアルでは、Kotlinがこの単純な非推奨のアイデアをJavaから取得し、それをまったく新しいレベルに引き上げる方法を見ていきます。

2. 非推奨

JVMエコシステムでの非推奨に関しては、素敵な java.util.Date クラスは、非推奨のすべてのメソッドで常に名誉ある言及です。

この古代の過ちを繰り返すために、Kotlinでこの日付を実装しましょう。

data class Date(val millisSinceEpoch: Long) {

    private val internal = LocalDateTime.ofInstant(Instant.ofEpochMilli(millisSinceEpoch), ZoneId.of("UTC"))

    fun monthNumber(): Int = internal.get(ChronoField.MONTH_OF_YEAR)
}

上に示したように、 Date dataクラスは、Kotlinの日時情報をカプセル化することになっています。 ここで、 monthNumber()メソッドは現在の日付の月番号を返します。

val epoch = Date(0)
println(epoch.monthNumber()) // prints 1

しばらくすると、ゼロベースのvsを回避します。 1ベースの月番号の混乱、数値の代わりに列挙型を返す新しいメソッドを追加します。

fun month(): Month = internal.month

また、開発者が monthNumber()メソッドを使用できないようにするために、非推奨にすることができます。 Kotlinでこれを行うには、@Deprecatedアノテーションでメソッドにアノテーションを付けることができます

@Deprecated("Use the new month() method")
fun monthNumber(): Int = internal.get(ChronoField.MONTH_OF_YEAR)

メッセージプロパティは、非推奨のアノテーションでは常に必須です。 古い、現在は非推奨のメソッドを使用すると、Kotlinコンパイラは非推奨について警告します

'monthNumber(): Int' is deprecated. Use the new month() method

上記のように、コンパイラはメッセージを、自動生成された非推奨の説明とともに表示します。

3. 非推奨レベル

デフォルトでは、Kotlinコンパイラは、非推奨のメソッドを使用した場合にのみ警告メッセージを出力します。 ただし、このコンパイラアクションは、レベル注釈プロパティを介して構成できます。

デフォルトのレベルは、Deprecation.WARNINGと同じです。 そのため、プログラムはコンパイルを続行しますが、迷惑な警告メッセージが表示されます。

簡単な警告に加えて、 非推奨のメソッドのコンパイル中にエラーを生成するようにコンパイラーに指示することができます。 それを行うために、私たちは使用することができます DeprecationLevel.ERROR 価値:

@Deprecated("Use the new month() method", level = DeprecationLevel.ERROR)
fun monthNumber(): Int = internal.get(ChronoField.MONTH_OF_YEAR)

非推奨のメソッドを使用すると、Kotlinコンパイラは次のエラーメッセージで失敗します。

Using 'monthNumber(): Int' is an error. Use the new month() method

メソッドがそもそも存在しないかのように、非推奨のメソッドを完全に非表示にすることも可能です

@Deprecated("Use the new month() method", level = DeprecationLevel.HIDDEN)
fun monthNumber(): Int = internal.get(ChronoField.MONTH_OF_YEAR)

レベルDeprecationLevel.HIDDENの場合、コンパイラーは非推奨のメソッドを見つけることさえできません。

Unresolved reference: monthNumber

コンパイラはHIDDENまたはERRORメソッドの使用を許可しませんが、それらはバイトコードレベルで存在します javap を使用して生成されたバイトコードを覗いて、この引数を確認しましょう。

$ javap -c -p -v com.baeldung.deprecation.Date
// truncated
 public final int monthNumber();
    descriptor: ()I
    flags: (0x1011) ACC_PUBLIC, ACC_FINAL, ACC_SYNTHETIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #27     // Field internal:LLocalDateTime;
         4: getstatic     #33     // Field ChronoField.MONTH_OF_YEAR:LChronoField;
         7: checkcast     #35     // class TemporalField
        10: invokevirtual #41     // Method LocalDateTime.get:(LTemporalField;)I
        13: ireturn
    Deprecated: true

これにより、バイナリ互換性を維持しながら、コンパイル時にメソッドが存在しないふりをすることができます。 簡単に言えば、すでにコンパイルされたコードは、このような非推奨のメソッドを引き続き正常に呼び出すことができます。 ただし、コンパイラでは、これらのメソッドを新しいコードで使用することはできません。

4. 交換

replaceWith アノテーションプロパティを使用して、非推奨のメソッドの置換を指定することもできます。 この値は、指定されている場合、非推奨のAPI使用の代わりに使用する必要があるコードフラグメントを表します。

たとえば、ここでは、[X90X] monthNumber()の代わりに month()関数を指定しています。

@Deprecated("Use the new month() method", replaceWith = ReplaceWith("month()"))
fun monthNumber(): Int = internal.get(ChronoField.MONTH_OF_YEAR)

IDEおよびその他のツールは、この情報を使用して、置換を自動的に適用できます。 たとえば、IntelliJIDEAの

monthNumber()usageを新しいmonth()関数に置き換えることを提案していることがわかります。

デフォルトでは、置換式は、使用されているシンボルのコンテキストで解釈され、囲んでいるクラスのメンバーを参照できます。 具体的には、 ReplaceWith( “month()”)とは、同じオブジェクトインスタンスで month()を呼び出す必要があることを意味します。

同じコンテキストで解釈されているにもかかわらず、非推奨のメソッドを含むファイル内のインポートステートメントは、置換式ではアクセスできません。 したがって、これらのインポートステートメントのいずれかが必要な場合は、ReplaceWithimportsannotationプロパティを使用する必要があります。 例えば:

companion object {

    @Deprecated("Use java.time instead", 
      replaceWith = ReplaceWith("LocalDateTime.now()", imports = ["java.time.LocalDateTime"]))
    fun now(): Date = Date(0)
}

ここでは、 Date.now()の代わりに LocalDateTime.now()を使用することをお勧めします。 また、 LocalDateTime は現在のコンテキストでは使用できないため、 imports property — java.time.LocalDateTimeinを使用して必要なインポートを指定しています。この場合。

ここで、IntelliJ IDEAに置換を適用すると、廃止された関数呼び出しの置換に加えて、指定されたインポートが追加されます。

前述のように、 replaceWithプロパティは、IntelliJ IDEAで見たツール統合など、より優れたツールの機会を提供するためにあります。

5. 柔軟なターゲット

これまでのところ、Kotlinの関数またはメソッドのみを非推奨にしました。 メソッドに加えて、Kotlinの他の多くのコンストラクトに非推奨を適用できます。

たとえば、インスタンス変数を非推奨にすることができます。

@Deprecated("No longer valid")
private val zeroBased = true

タイプエイリアスを廃止することも可能です。

@Deprecated("Use plain string instead")
typealias DateFormat = String

非推奨のアノテーションソースコードを見て、考えられるすべてのターゲットを確認しましょう。

@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
public annotation class Deprecated(
    val message: String,
    val replaceWith: ReplaceWith = ReplaceWith(""),
    val level: DeprecationLevel = DeprecationLevel.WARNING
)

したがって、前述のターゲットに加えて、このアノテーションは、クラス、他のアノテーション、セッター/ゲッター、関数、およびコンストラクターにも適用可能であることがわかります。

6. 結論

このチュートリアルでは、Kotlinでさまざまなプログラミング構造を廃止する方法を説明しました。 また、非推奨のレベルを制御でき、特定の非推奨の構成の代替を提案できることも学びました。

いつものように、すべての例はGitHubから入手できます。