Kotlin クラスとオブジェクト

クラス定義

Kotlin のクラスは、コンストラクタと初期化コードブロック、関数、プロパティ、内部クラス、オブジェクト宣言を含めることができます。

Kotlin ではキーワードを使用しますclassクラスを宣言し、その後にクラス名を続けます:

class サンプル {  // 类名为 サンプル
    // 大括号内是类体构成
}

空のクラスを定義することもできます:

class Empty

クラス内にメンバー関数を定義できます:

class サンプル() {
    fun foo() { print("Foo") } // 成员函数
}

クラスのプロパティ

プロパティ定義

クラスのプロパティはキーワードを使用できますvar可変として宣言し、それ以外の場合は読み取り専用キーワードvalを使用して不変として宣言します。

class サンプル {
    var name: String = ……
    var url: String = ……
    var city: String = ……
}

通常の関数のようにコンストラクタを使用してクラスのインスタンスを作成できます:

val site = サンプル() // Kotlin 中没有 new 关键字

プロパティを使用するには、名前で参照するだけです。

site.name           // 使用 . 号来引用
site.url

Kotlin のクラスは、プライマリコンストラクタと 1 つ以上のセカンダリコンストラクタを持つことができます。プライマリコンストラクタはクラスヘッダーの一部であり、クラス名の後に置かれます:

class Person constructor(firstName: String) {}

プライマリコンストラクタにアノテーションがなく、可視性修飾子もない場合、constructor キーワードは省略できます。

class Person(firstName: String) {
}

getter と setter

プロパティ宣言の完全な構文:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]
 

getter と setter はどちらもオプションです

プロパティの型が初期化文またはクラスのメンバー関数から推論できる場合は、型を省略できます。val は読み取り専用であるため、setter 関数を設定することはできません。

var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1    // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int?       // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1   // 类型为 Int 类型,默认实现 getter

インスタンス

次の例では、Person クラスを定義しています。このクラスには、可変変数 lastName と no の 2 つが含まれ、lastName は getter メソッドを変更し、no は setter メソッドを変更しています。

class Person {

    var lastName: String = "zhang"
        get() = field.toUpperCase()   // 将变量赋值后转换为大写
        set

    var no: Int = 100
        get() = field                // 后端变量
        set(value) {
            if (value < 10) {       // 如果传入的值小于 10 返回该值
                field = value
            } else {
                field = -1         // 如果传入的值大于等于 10 返回 -1
            }
        }

    var heiht: Float = 145.4f
        private set
}

// 测试
fun main(args: Array<String>) {
    var person: Person = Person()

    person.lastName = "wang"

    println("lastName:${person.lastName}")

    person.no = 9
    println("no:${person.no}")

    person.no = 20
    println("no:${person.no}")

}

出力結果は次のとおりです:

lastName:WANG
no:9
no:-1

Kotlin のクラスは field を持つことはできません。しかし、カスタムアクセサを使用する場合には backing field が必要になることがあります。そのために、Kotlin は自動的な backing field を提供し、field 識別子を使用してアクセスできます。

Backing Field は Kotlin における概念です。これはプロパティの値を格納するための自動生成されるフィールドです。アクセサ(getter または setter)内部でのみ使用でき、少なくとも 1 つのアクセサのデフォルト実装を使用する場合、またはカスタムアクセサが field 識別子を介してそれを参照する場合にのみ存在します。つまり、プロパティが backing field を必要とする場合、Kotlin が自動的にそれを提供します。アクセサ内で field 識別子を使用して backing field を参照できます。

var no: Int = 100
        get() = field                // 后端变量
        set(value) {
            if (value < 10) {       // 如果传入的值小于 10 返回该值
                field = value
            } else {
                field = -1         // 如果传入的值大于等于 10 返回 -1
            }
        }

もちろん、backing field の例を次に示します:

class Person {
    var name: String = "initial value"
        set(value) {
            if (value.isNotEmpty()) {
                field = value
            }
        }
}

この例では、name プロパティにはカスタム setter があり、渡された値が空かどうかをチェックします。空でない場合は、field 識別子を使用して値を backing field に割り当てます。これにより、name プロパティに空文字列を割り当てようとしても、その値は変更されません。

val person = Person()
println(person.name) // 输出 "initial value"
person.name = ""
println(person.name) // 仍然输出 "initial value"
person.name = "John"
println(person.name) // 输出 "John"

上記のコードでは、Person クラスのインスタンスを作成し、その name プロパティの値を変更しようとしています。空文字列を割り当てようとすると、カスタム setter のチェックにより、name プロパティの値は変更されません。空でない文字列を割り当てると、その値は新しい値に正常に変更されます。

非 null プロパティは定義時に初期化する必要があります。Kotlin は遅延初期化を可能にする方法を提供しています。lateinitキーワードでプロパティを記述します:

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

プライマリコンストラクタ

プライマリコンストラクタにはコードを含めることはできません。初期化コードは初期化コードブロックに置くことができ、初期化コードブロックは init キーワードをプレフィックスとして使用します。

class Person constructor(firstName: String) {
    init {
        println("FirstName is $firstName")
    }
}

注意:プライマリコンストラクタのパラメータは、初期化コードブロック内で使用できるほか、クラス本体内で定義されたプロパティの初期化コード内でも使用できます。簡潔な構文として、プライマリコンストラクタを使用してプロパティを定義し、プロパティ値を初期化できます(var または val)。

class People(val firstName: String, val lastName: String) {
    //...
}

コンストラクタにアノテーションがある場合、または可視性修飾子がある場合、constructor キーワードが必須です。アノテーションと修飾子はその前に置きます。

インスタンス

サンプル クラスを作成し、コンストラクタ経由でサイト名を渡します:

class サンプル  constructor(name: String) {  // 类名为 サンプル
    // 大括号内是类体构成
    var url: String = "http://www.example.com"
    var country: String = "CN"
    var siteName = name

    init {
        println("初始化网站名: ${name}")
    }

    fun printTest() {
        println("我是类的函数")
    }
}

fun main(args: Array<String>) {
    val サンプル =  サンプル("サンプル")
    println(サンプル.siteName)
    println(サンプル.url)
    println(サンプル.country)
    サンプル.printTest()
}

出力結果は次のとおりです:

初始化网站名: サンプル
サンプル
http://www.example.com
CN
我是类的函数

セカンダリコンストラクタ

クラスはセカンダリコンストラクタを持つこともできます。プレフィックスとして constructor を付ける必要があります:

class Person { 
    constructor(parent: Person) {
        parent.children.add(this) 
    }
}

クラスにプライマリコンストラクタがある場合、各セカンダリコンストラクタは、直接的または別のセカンダリコンストラクタを介して間接的にプライマリコンストラクタに委譲する必要があります。同じクラス内で別のコンストラクタに委譲するには this キーワードを使用します:

class Person(val name: String) {
    constructor (name: String, age:Int) : this(name) {
        // 初始化...
    }
}

非抽象クラスがコンストラクタ(プライマリコンストラクタまたはセカンダリコンストラクタ)を宣言しない場合、引数のないコンストラクタが生成されます。このコンストラクタは public です。クラスに public コンストラクタを持たせたくない場合は、空のプライマリコンストラクタを宣言する必要があります:

class DontCreateMe private constructor () {
}

注意:JVM 仮想マシンでは、プライマリコンストラクタのすべてのパラメータにデフォルト値がある場合、コンパイラは追加の引数なしコンストラクタを生成し、このコンストラクタはデフォルト値を直接使用します。これにより、Jackson や JPA のように引数なしコンストラクタを使用してクラスのインスタンスを作成するライブラリを、Kotlin でより簡単に使用できます。

class Customer(val customerName: String = "")

インスタンス

class サンプル  constructor(name: String) {  // 类名为 サンプル
    // 大括号内是类体构成
    var url: String = "http://www.example.com"
    var country: String = "CN"
    var siteName = name

    init {
        println("初始化网站名: ${name}")
    }
    // 次构造函数
    constructor (name: String, alexa: Int) : this(name) {
        println("Alexa 排名 $alexa")
    }

    fun printTest() {
        println("我是类的函数")
    }
}

fun main(args: Array<String>) {
    val サンプル =  サンプル("サンプル", 10000)
    println(サンプル.siteName)
    println(サンプル.url)
    println(サンプル.country)
    サンプル.printTest()
}

出力結果は次のとおりです:

初始化网站名: サンプル
Alexa 排名 10000
サンプル
http://www.example.com
CN
我是类的函数

抽象クラス

抽象化はオブジェクト指向プログラミングの特徴の 1 つです。クラス自体、またはクラスの一部のメンバーは、abstract として宣言できます。抽象メンバーはクラス内に具体的な実装を持ちません。

注意:抽象クラスや抽象メンバーに open アノテーションを付ける必要はありません。

open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}

ネストしたクラス

クラスを他のクラスの中にネストできます。次の例を見てください:

class Outer {                  // 外部类
    private val bar: Int = 1
    class Nested {             // 嵌套类
        fun foo() = 2
    }
}

fun main(args: Array<String>) {
    val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
    println(demo)    // == 2
}

内部クラス

内部クラスは inner キーワードを使用して表します。

内部クラスは外部クラスのオブジェクトへの参照を持っているため、外部クラスのメンバープロパティとメンバー関数にアクセスできます。

class Outer {
    private val bar: Int = 1
    var v = "成员属性"
    /**嵌套内部类**/
    inner class Inner {
        fun foo() = bar  // 访问外部类成员
        fun innerTest() {
            var o = this@Outer //获取外部类的成员变量
            println("内部类可以引用外部类的成员,例如:" + o.v)
        }
    }
}

fun main(args: Array<String>) {
    val demo = Outer().Inner().foo()
    println(demo) //   1
    val demo2 = Outer().Inner().innerTest()   
    println(demo2)   // 内部类可以引用外部类的成员,例如:成员属性
}

曖昧さをなくすために、外部スコープからの this にアクセスするには、this@label を使用します。ここで @label は this の出自を指すラベルです。


匿名内部クラス

オブジェクト式を使用して匿名内部クラスを作成します:

class Test {
    var v = "成员属性"

    fun setInterFace(test: TestInterFace) {
        test.test()
    }
}

/**
 * 定义接口
 */
interface TestInterFace {
    fun test()
}

fun main(args: Array<String>) {
    var test = Test()

    /**
     * 采用对象表达式来创建接口对象,即匿名内部类的实例。
     */
    test.setInterFace(object : TestInterFace {
        override fun test() {
            println("对象表达式创建匿名内部类的实例")
        }
    })
}

クラスの修飾子

クラスの修飾子には、classModifier と _accessModifier_ が含まれます:

  • classModifier: クラス属性修飾子。クラス自体の特性を示します。

    abstract    // 抽象类  
    final       // 类不可继承,默认属性
    enum        // 枚举类
    open        // 类可继承,类默认是final的
    annotation  // 注解类
    
  • accessModifier: アクセス権限修飾子

    private    // 仅在同一个文件中可见
    protected  // 同一个文件中或子类可见
    public     // 所有调用的地方都可见
    internal   // 同一个模块中可见
    

インスタンス

// 文件名:example.kt
package foo

private fun foo() {} // 在 example.kt 内可见

public var bar: Int = 5 // 该属性随处可见

internal val baz = 6    // 相同模块内可见
その他の拡張