Kotlinでキーワードを開く
1. 概要
このチュートリアルでは、継承ルールとKotlinのopenキーワードについて説明します。
まず、継承に関する少しの哲学から始めます。 次に、ギアを切り替えて、 open キーワードがKotlinのクラス、メソッド、およびプロパティにどのように影響するかを確認します。 最後に、openキーワードとSpringなどのエンタープライズフレームワークがどのように相互作用するかを見ていきます。
2. 継承のための設計
Javaでは、クラスとメソッドはデフォルトで拡張可能です。 これは、 final としてマークされていない限り、Javaの任意のクラスから拡張したり、任意のメソッドをオーバーライドしたりできることを意味します。 継承に伴う密接な関係とそのような関係のリスクのために、JoshuaBlochはEffectiveJavaでこれに全項目を捧げました。
継承のための設計と文書化、またはそれを禁止する
これは、クラスとメソッドを拡張またはオーバーライドする正当な理由がない限り、クラスとメソッドを封印し、拡張またはオーバーライドできないようにする必要があることを意味します。 また、クラスを拡張用に開く必要があると判断した場合は、メソッドをオーバーライドした場合の影響を文書化する必要があります。
より良い方法は、デフォルトで最終であることを支持しますが、それでも実装には別のアイデアがあります。
Kotlinが同じ概念にどのようにアプローチするかを見てみましょう。
3. クラス階層
Kotlinでは、すべてがデフォルトで最終版です。 したがって、デフォルト構成の任意のクラスから拡張しようとすると、次のようになります。
class Try
class Success : Try()
その後、コンパイラは失敗します。
Kotlin: This type is final, so it cannot be inherited from
クラスを拡張用にオープンにするには、そのクラスをopenキーワードでマークする必要があります。
open class Try
class Success : Try()
これで、Tryクラスを拡張できます。 Success クラスを拡張する必要がある場合は、openキーワードでもマークする必要があります。 したがって、開いている キーワードは、クラスに推移的な影響を与えません。
open キーワードでクラスをマークしない場合、Kotlinコンパイラはバイトコードレベルでfinalクラスを発行します。
ささいなクラスを使用してこれを確認しましょう:
class Sealed
それでは、生成されたバイトコードを調べてみましょう。
$ kotlinc Inheritance.kt
$ javap -c -p -v com.baeldung.inheritance.Sealed
Compiled from "Inheritance.kt"
public final class com.baeldung.inheritance.Sealed
// truncated
ご覧のとおり、このクラスはJavaのfinalクラスと同じです。
4. メソッドとプロパティのオーバーライド
クラスと同様に、デフォルトでは、メソッドとプロパティは、囲んでいるクラスがopen であっても、Kotlinでfinalになります。 例えば:
open class Try {
fun isSuccess(): Boolean = false
}
class Success : Try() {
override fun isSuccess(): Boolean = true
}
最終的なメソッドをオーバーライドできないため、コンパイラはSuccessクラスをコンパイルできません。
Kotlin: 'isSuccess' in 'Try' is final and cannot be overridden
クラスと同様に、コンパイラはここでfinalメソッドを発行します。
$ kotlinc Inheritance.kt
$ javap -c -p -v com.baeldung.inheritance.Try
// truncated
public final boolean isSuccess();
descriptor: ()Z
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
サブクラスのメソッドをオーバーライドするには、スーパークラスのopenキーワードでそのメソッドをマークする必要があります。
open class Try {
open fun isSuccess(): Boolean = false
}
class Success : Try() {
override fun isSuccess(): Boolean = true
}
プロパティについても同じことが言えます。 オープンキーワードでマークしない限り、これらは最終です。
open class Try {
open val value: Any? = null
// omitted
}
class Success(override val value: Any?) : Try() {
// omitted
}
上に示したように、サブクラスコンストラクターのプロパティをオーバーライドしています。
overrideキーワードでマークされたメソッドとプロパティは暗黙的に開かれているため、サブクラスでオーバーライドできます。 これを防ぐために、finalでマークを付けることができます。
open class Success(override val value: Any?) : Try() {
final override fun isSuccess(): Boolean = true
}
上に示したように、クラス Success のサブクラスは、 isSuccess()メソッドをオーバーライドできません。
5. フレームワークの相互運用性
SpringやHibernateなどのJVMエコシステムの多くのエンタープライズフレームワークは、動的プロキシを作成するために継承に依存しています。 したがって、 finalKotlinクラスをSpringBeanまたはJPAエンティティとして使用することはできません。
この問題を解決して、すべてのSpringBeanまたはJPAエンティティをopenキーワードでマークする1つの方法。 推奨されるメソッドとプロパティにもマークを付ける必要がある場合があります。
@Service
@Transactional
open class UserService {
open fun register(u: User) {
// omitted
}
}
すべての特別なクラスとそのメンバーにopenキーワードでマークを付けることは、不便で効果のないアプローチです。 この状況を改善するために、JetBrainsはKotlin-allopenプラグインを提供しています。 このプラグインは、KotlinクラスをSpringなどのフレームワークの要件に適合させます。
基本的に、このプラグインは、明示的なopen なしで、特定のクラスとそのメンバーをopenキーワードでマークします。 これを実現するには、これらのクラスに@Serviceや@Entityなどの特別なSpringアノテーションを付ける必要があります。
@Service
@Transactional
class UserService {
fun register(u: User) {
// omitted
}
}
これらのopenキーワードを明示的に追加しなかったとしても、コンパイラプラグインはコンパイル中にそれらを追加します。
6. 結論
このチュートリアルでは、Kotlinで継承がどのように機能するかを確認しました。 また、openキーワードがKotlinのクラス階層にどのように影響するかを学びました。
いつものように、すべての例はGitHubでから入手できます。