『Kotlin極簡教程』第5章Kotlinオブジェクト向けプログラミング(OOP)

20242 ワード

OOPバージョンのHelloWorld
正式に棚に上がる:《Kotlin極簡教程》Official on shelves:Kotlin Programming minimalist tutorial京東JD:https://item.jd.com/12181725.html天猫Tmall:https://detail.tmall.com/item.htm?id=558540170670
/**
 * Here we have a class with a primary constructor and a member function.
 * Note that there's no `new` keyword used to create an object.
 * See http://kotlinlang.org/docs/reference/classes.html#classes
 */

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array) {
    Greeter(args[0]).greet()
}

コンストラクタパラメータ
/**
 * This example introduces a concept that we call destructuring declarations.
 * It creates multiple variable at once. Anything can be on the right-hand
 * side of a destructuring declaration, as long as the required number of component
 * functions can be called on it.
 * See http://kotlinlang.org/docs/reference/multi-declarations.html#multi-declarations
 */

fun main(args: Array) {
    val pair = Pair(1, "one")

    val (num, name) = pair

    println("num = $num, name = $name")
    
    val triple = Triple(10,"B",10.0)
    val (a,b,c) = triple
    println("a=$a, b=$b, c=$c")
}

class Pair(val first: K, val second: V) {
    operator fun component1(): K {
        return first
    }

    operator fun component2(): V {
        return second
    }
}

class Triple(val first: K,val second:V,val third:T){
    operator fun component1():K{return first}
    operator fun component2():V{return second}
    operator fun component3():T{return third}
}





実行結果:
num = 1, name = one
a=10, b=B, c=10.0

Data Class
We frequently create a class to do nothing but hold data. In such a class some standard functionality is often mechanically derivable from the data. In Kotlin, this is called a data class and is marked as data
data class User(val name: String, val age: Int)

The compiler automatically derives the following members from all properties declared in the primary constructor:
  • equals()/hashCode() pair
  • toString() of the form "User(name=John, age=42)",
  • componentN() functions corresponding to the properties in their order of declaration,
  • copy() function
  • /**
     *  Data class gets component functions, one for each property declared
     *  in the primary constructor, generated automatically, same for all the
     *  other goodies common for data: toString(), equals(), hashCode() and copy().
     *  See http://kotlinlang.org/docs/reference/data-classes.html#data-classes
     */
    
    data class User(val name: String, val id: Int)
    
    fun getUser(): User {
        return User("Alex", 1)
    }
    
    fun main(args: Array) {
        val user = getUser()
        println("name = ${user.name}, id = ${user.id}")
    
        // or
    
        val (name, id) = getUser()
        println("name = $name, id = $id")
    
        // or
    
        println("name = ${getUser().component1()}, id = ${getUser().component2()}")
    }
    
    

    インタフェースの定義&実装
    package ch04.ex1_1_1_InterfacesInKotlin
    
    interface Clickable {
        fun click()
    }
    class Button : Clickable {
        override fun click() = println("I was clicked")
    }
    
    fun main(args: Array) {
        Button().click()
    }
    
    

    pojo beanを書く
    package jason.chen.mini_springboot.restful.entity
    
    import java.util.*
    import javax.persistence.Entity
    import javax.persistence.GeneratedValue
    import javax.persistence.GenerationType
    import javax.persistence.Id
    
    @Entity
    class Customer(
            val firstName: String,
            val lastName: String,
            val gmtCreated: Date,
            val gmtModified: Date,
            val isDeleted: Int, //1 Yes 0 No
            val deletedDate:Date,
            @Id @GeneratedValue(strategy = GenerationType.AUTO)
            val id: Long = -1) {
        override fun toString(): String {
            return "Customer(firstName='$firstName', lastName='$lastName', gmtCreated=$gmtCreated, gmtModified=$gmtModified, isDeleted=$isDeleted, deletedDate=$deletedDate, id=$id)"
        }
    }
    
    
    
    
    data class Shop(val name: String, val customers: List)
    
    data class Customer(val name: String, val city: City, val orders: List) {
        override fun toString() = "$name from ${city.name}"
    }
    
    data class Order(val products: List, val isDelivered: Boolean)
    
    data class Product(val name: String, val price: Double) {
        override fun toString() = "'$name' for $price"
    }
    
    data class City(val name: String) {
        override fun toString() = name
    }
    
    
    
    

    Rectangleオブジェクトを指定
    package geometry.shapes
    
    import java.util.Random
    
    class Rectangle(val height: Int, val width: Int) {
        val isSquare: Boolean
            get() = height == width
    }
    
    fun createRandomRectangle(): Rectangle {
        val random = Random()
        return Rectangle(random.nextInt(), random.nextInt())
    }
    
    
    

    日付ツールクラスをカプセル化
    package jason.chen.mini_springboot.restful.utils
    
    import java.text.SimpleDateFormat
    import java.util.*
    
    /**
     * Created by jack on 2017/3/11.
     * @author jack
     * @date 2017/03/11
     *
     *  val date = Date()
        date + 1 //   
        date - 1 //   
        date + Month(2) // 2 
        date - Year(3) // 3 
        date++  //       
        date--  //      
                date[0]  date[1] date[2] 。。。
    
        //    
        if( date1 > date2){
    
        }
    
     */
    
    
    enum class DateOptUnit {
        YEAR,MONTH,DATE;
        fun parseType():Int{
            var value = Calendar.DATE
            when(this){
                YEAR -> value = Calendar.DATE
                MONTH -> value = Calendar.MONTH
                DATE ->  value = Calendar.DATE
            }
            return value
        }
    }
    
    data class DateOperator(val unit :DateOptUnit,val value: Int)
    
    fun Any.year(value:Int):DateOperator {
        return DateOperator(DateOptUnit.YEAR,value)
    }
    
    fun Any.month(value:Int):DateOperator {
        return DateOperator(DateOptUnit.MONTH,value)
    }
    
    fun Any.day(value:Int):DateOperator {
        return DateOperator(DateOptUnit.DATE,value)
    }
    
    /**
     * date+1
     *      
     */
    operator fun Date.plus(nextVal:Int):Date{
        val calendar = GregorianCalendar()
        calendar.time = this
        calendar.add(Calendar.DATE, nextVal)
        return calendar.time
    }
    
    /**
     * date-1
     */
    operator fun Date.minus(nextVal:Int):Date{
        val calendar = GregorianCalendar()
        calendar.time = this
        calendar.add(Calendar.DATE, nextVal*-1)
        return calendar.time
    }
    
    /**
     * date+year(3)
     *      
     */
    operator fun Date.plus(nextVal:DateOperator):Date{
        val calendar = GregorianCalendar()
        calendar.time = this
        calendar.add(nextVal.unit.parseType(), nextVal.value)
        return calendar.time
    }
    
    /**
     * date-month(4)
     */
    operator fun Date.minus(nextVal:DateOperator):Date{
        val calendar = GregorianCalendar()
        calendar.time = this
        calendar.add(nextVal.unit.parseType(), nextVal.value*-1)
        return calendar.time
    }
    
    /**
     *     
     */
    operator fun Date.inc():Date {
        val calendar = GregorianCalendar()
        calendar.time = this
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, 0);
        return calendar.time
    }
    
    /**
     *     
     */
    operator fun Date.dec():Date {
        val calendar = GregorianCalendar()
        calendar.time = this
        calendar.set(Calendar.DAY_OF_MONTH, 1)
        return calendar.time
    }
    
    /**
     *          0 - 5
     *    2015-12-21 22:15:56
     * date[0]:2015  date[1]:12 date[2]:21
     */
    operator fun Date.get(position:Int):Int {
        val calendar = GregorianCalendar()
        calendar.time = this
        var value = 0
        when(position) {
            0 -> value = calendar.get(Calendar.YEAR)
            1 -> value = calendar.get(Calendar.MONTH)+1
            2 -> value = calendar.get(Calendar.DAY_OF_MONTH)
            3 -> value = calendar.get(Calendar.HOUR)
            4 -> value = calendar.get(Calendar.MINUTE)
            5 -> value = calendar.get(Calendar.SECOND)
        }
        return value
    }
    
    /**
     *   2   
     * if(date1 > date2) {
     * }
     */
    
    operator fun Date.compareTo(compareDate : Date):Int {
        return (time - compareDate.time).toInt()
    }
    
    /**
     *         
     */
    fun Date.stringFormat(formatType:String):String{
        return SimpleDateFormat(formatType).format(this)
    }
    
    
    
    

    サンプルコード1
    /**
     * This example introduces a concept that we call destructuring declarations.
     * It creates multiple variable at once. Anything can be on the right-hand
     * side of a destructuring declaration, as long as the required number of component
     * functions can be called on it.
     * See http://kotlinlang.org/docs/reference/multi-declarations.html#multi-declarations
     */
    
    fun main(args: Array) {
        val pair = Pair(1, "one")
    
        val (num, name) = pair
    
        println("num = $num, name = $name")
    }
    
    class Pair(val first: K, val second: V) {
        operator fun component1(): K {
            return first
        }
    
        operator fun component2(): V {
            return second
        }
    }
    
    

    サンプルコード2
    
    /**
     *  Data class gets component functions, one for each property declared
     *  in the primary constructor, generated automatically, same for all the
     *  other goodies common for data: toString(), equals(), hashCode() and copy().
     *  See http://kotlinlang.org/docs/reference/data-classes.html#data-classes
     */
    
    data class User(val name: String, val id: Int)
    
    fun getUser(): User {
        return User("Alex", 1)
    }
    
    fun main(args: Array) {
        val user = getUser()
        println("name = ${user.name}, id = ${user.id}")
    
        // or
    
        val (name, id) = getUser()
        println("name = $name, id = $id")
    
        // or
    
        println("name = ${getUser().component1()}, id = ${getUser().component2()}")
    }
    

    クラスと継承
    クラス#クラス#
    クラス宣言Kotlin使用キーワード*class*{:.keyword}
    class Invoice {
    }
    

    このクラス宣言は、クラス名、クラスヘッダ(そのタイプパラメータ、プライマリコンストラクション関数などを指定する)、およびクラスのホストを含むカッコで囲まれています.クラスヘッダとメインはオプションです.このクラスに主幹がなければ、カッコは省略できます.
    class Empty
    

    こうぞう
    Kotlinのクラスには、主構造関数と1つ以上の2次構造関数があり得る.プライマリコンストラクション関数はクラスヘッダの一部です.このクラス名の後に続く(オプションのタイプパラメータ)
    class Person constructor(firstName: String) {
    }
    

    このプライマリコンストラクション関数に注釈や表示可能な修飾子がない場合、このconstructor{:.keyword}キーワードは省略できます.
    class Person(firstName: String) {
    }
    

    このマスターコンストラクション関数には、コードは含まれません.初期化されたコードはinitializer blocks(初期のモジュール)に配置され、initを接頭辞としてキーワード{:.keyword}とすることができる.
    class Customer(name: String) {
        init {
            logger.info("Customer initialized with value ${name}")
        }
    }
    

    マスター構造のパラメータは、初期化モジュールで使用できます.クラス内で初期化のプロパティを宣言することもできます.
    class Customer(name: String) {
        val customerKey = name.toUpperCase()
    }
    

    実際、宣言属性と初期化主構造関数、Kotlinには簡潔な構文があります.
    class Person(val firstName: String, val lastName: String, var age: Int) {
      // ...
    }
    

    通常のプロパティと同様に、プライマリコンストラクション関数で宣言されるプロパティは可変または読み取り専用です.
    If the constructor has annotations or visibility modifiers,the constructor{:.keyword}keyword is required,and the modifiers go before it:コンストラクション関数に注釈または可視修飾子がある場合、このconstructor{:.keyword}はキーワードで修飾する必要があります.
    class Customer public inject constructor(name: String) { ... }
    

    詳細はVisibility Modifiersを参照してください
    拡張コンストラクタ
    クラスは「二次構造関数」と呼ばれるもの(KotlinがJavaのように複数の構造関数を持つことを実現するため)を持つこともでき、通常は接頭辞「constructor」を付けられる.
    class Person {
        constructor(parent: Person) {
            parent.children.add(this)
        }
    }
    

    クラスにプライマリコンストラクション関数がある場合、各2次コンストラクション関数はプライマリコンストラクション関数に委任され、直接または間接的に別の2次関数を通過する必要があります.同じクラスのコンストラクション関数を使用してthis{:.keyword}キーを使用するように別のクラスに委任
    class Person(val name: String) {
        constructor(name: String, parent: Person) : this(name) {
            parent.children.add(this)
        }
    }
    

    非抽象クラスがコンストラクション関数(原発性または再発性)を宣言していない場合、生成されたプライマリコンストラクション関数にはパラメータがありません.コンストラクション関数の可視性はpublicです.クラスに共通のコンストラクション関数があることを望んでいない場合は、デフォルトで表示されない空のプライマリコンストラクション関数を宣言する必要があります.
    class DontCreateMe private constructor () {
    }
    

    なお、JVMでは、すべてのマスターコンストラクタのパラメータにデフォルト値がある場合、コンパイラは追加のパラメータのコンストラクタを生成し、デフォルト値が使用されます.これにより、JacksonやJPAライブラリを使用する場合など、kotlinとパラメータ構築関数によってクラスを作成するインスタンスの使用が容易になります.
    class Customer(val customerName: String = "")
    

    {:.info}
    クラスのインスタンスの作成
    クラスのインスタンスを作成するには、通常の関数のようにコンストラクション関数を呼び出します.
    val invoice = Invoice()
    
    val customer = Customer("Joe Smith")
    

    注意Kotlinには「new」キーワードはありません
    クラスメンバー
    クラスには
  • 構築と初期化モジュール
  • 関数
  • 属性
  • 匿名および内部クラス
  • オブジェクト宣言
  • 継承
    Kotlinのすべてのクラスに共通の親Anyがあります.これはデフォルトの親であり、親タイプ宣言はありません.
    class Example // Implicitly inherits from Any
    
    Anyjava.lang.Objectに属さない.特に、他のメンバーは存在せず、equals()hashCode()およびtoString()さえ存在しない.
    Javaの相互運用性の詳細を参照してください.
    明確な親を宣言するには、タイプをクラスヘッダの後に配置します.
    open class Base(p: Int)
    
    class Derived(p: Int) : Base(p)
    

    上記のように、親クラスは、継承を宣言する場所で、元のコンストラクション関数で初期化することができます(および必要です).
    クラスにプライマリ構造がない場合、各サブコンストラクション関数の初期化基本タイプはsuper{:.keyword}キーワードを使用するか、別のコンストラクション関数に委任します.この場合、異なる2次構造関数は、ベースタイプの異なる構造を呼び出すことができることに注意してください.
    class MyView : View {
        constructor(ctx: Context) : super(ctx) {
        }
    
        constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
        }
    }
    

    親クラスのopen{:.keyword}寸法は、Javaのfinal{:.keyword}の反対側として理解され、他の他のクラスがこのクラスから継承できるようにします.デフォルトでは、Kotlinのすべてのクラスはfinalで、Effective Java書の17条に対応しています.マークアップの継承を設計して表示します.そうしないと禁止されます.
    メンバーの上書き
    私たちは前に述べたように、Kotlinは明確な表現を求めています.Javaのように、Kotlinはオープンと書き換えの関数を明確に表記する必要があります.(親クラスを継承して親クラスの関数を上書きする場合、Kotlinは親クラスにopen寸法が必要であり、上書きされた関数にはopen寸法が必要であり、子クラスの関数にはoverride寸法が必要である.):
    open class Base {
      open fun v() {}
      fun nv() {}
    }
    class Derived() : Base() {
      override fun v() {}
    }
    

    Derived.v()関数にoverride寸法を付ける必要があります.書かないと、コンパイラがエラーを報告します.親クラスのこの関数にopenがマークされていない場合、overrideを追加するかどうかにかかわらず、子クラスでは同じ名前の関数を定義できません.finalクラス(openを宣言していないクラス)では、関数にopen寸法を付けることはできません.
    メンバーがoverride{:.keyword}とマークされている自体はオープンです.つまり、サブクラスで書き換えることができます.書き換えを禁止したい場合はfinal{:.keyword}キーワードを使用します.
    open class AnotherDerived() : Base() {
      final override fun v() {}
    }
    

    待って!どうやって俺の倉庫をロックしたんだ?
    このように継承と上書きの方法(クラスとメンバーのデフォルトfinal)を設計すると、サードパーティのクラスを継承することが難しくなり、hackを行うことが難しくなります.
    私たちはこれが劣勢ではないと考えています.原因は以下の通りです.
  • ベストプラクティスは、これらのhacks
  • を使用するべきではないことを示しています.
  • 他の類似のメカニズムを持つ言語(C+,C#)が成功したことを証明した
  • もし人々が本当にhackを考えているならば、依然として方法があります:例えばいくつかの情況の下でJavaを使ってhackを行うことができて、更にKotlinで呼び出します;または、断面に面したフレーム(Aspect)を使用します.(Javaの相互運用を参照)
  • 書き換えルール
    Kotlinでは、継承を実装する呼び出しは、1つのクラスが親メンバーの複数の実装方法を継承する場合、子クラスで直接参照することができ、そのメンバーを書き換え、独自の実装を提供する必要があります(もちろん親を使用することもできます).継承された実装の親タイプを表すには、super{:.keyword}を使用して、仕様の親名super:
    open class A {
      open fun f() { print("A") }
      fun a() { print("a") }
    }
    
    interface B {
      fun f() { print("B") } // interface members are 'open' by default
      fun b() { print("b") }
    }
    
    class C() : A(), B {
      // The compiler requires f() to be overridden:
      override fun f() {
        super.f() // call to A.f()
    super.f() // call to B.f()
    }
    }

    クラスCがAとBを同時に継承することは可能であり、Cのベースクラスに1つの実装しかないため、a()とb()関数を呼び出すときに問題はありません.しかし,f()関数はA,Bともに実装されているので,Cでf()を上書きし,曖昧さを解消するために実装を提供しなければならない.
    抽象クラス
    クラスおよびその中のいくつかのインプリメンテーションはabstract{:.keyword}として宣言できます.抽象メンバーは、このクラスで実装する必要はありません.したがって、一部のサブクラスが抽象的なメンバーを継承する場合、それは実装ではありません.
    abstract class A {
      abstract fun f()
    }
    
    interface B {
      open fun f() { print("B") }
    }
    
    class C() : A(), B {
      // We are not required to override f()
    }
    

    Note that we do not need to annotate an abstract class or function with open – it goes without saying.
    We can override a non-abstract open member with an abstract one
    抽象クラスや関数をopenとしてマークする必要はありません.これは言うまでもありません.
    Open非抽象メンバーを抽象化するように書き換えることができます.
    open class Base {
      open fun f() {}
    }
    
    abstract class Derived : Base() {
      override abstract fun f()
    }
    

    同伴オブジェクト
    Kotlinでは,JavaやC#のようにクラスに静的メソッドはない.ほとんどの場合、パッケージレベル関数を簡単に使用することをお勧めします.
    クラスのインスタンスに依存せずに呼び出せる関数を書く必要があるが、アクセスする内部のクラス(たとえば、ファクトリメソッド)が必要な場合は、[オブジェクト宣言](object_declarations.html)のメンバーのクラスとして書くことができます.
    より具体的には、クラスに仲間オブジェクトを宣言すると、Java/C#でメンバーメソッドと同じ構文の静的メソッドを呼び出し、クラス名のみを修飾語として使用することができます.
    コードの例
    JAvaコード:
    package com.restfeel.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Profile;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    
    /**
     * Created by santoshm1 on 04/06/14.
     *
     * Adds support for runtime property files. Run with -Dspring.profiles.active={production,default,development,test}
     * defaults to development.
     */
    
    @Configuration
    @PropertySource(value = {"classpath:common.properties"})
    public class PropertyConfig {
    
        public PropertyConfig() {}
    
        @Bean
        public static PropertySourcesPlaceholderConfigurer myPropertySourcesPlaceholderConfigurer() {
            return new PropertySourcesPlaceholderConfigurer();
        }
    
        /**
         * Properties to support the 'test' mode of operation.
         */
        @Configuration
        @Profile({"devlopment", "default"})
        @PropertySource(value = {"classpath:env-development.properties"})
        static class Dev {
        }
    
        /**
         * Properties to support the 'test' mode of operation.
         */
        @Configuration
        @Profile("test")
        @PropertySource(value = {"classpath:env-test.properties"})
        static class Test {
        }
    
        /**
         * Properties to support the 'production' mode of operation.
         */
        @Configuration
        @Profile("production")
        @PropertySource(value = {"classpath:env-production.properties"})
        static class Production {
            // Define additional beans for this profile here
        }
    
    }
    
    

    対応するkotlinコード:
    package com.restfeel.config
    
    import org.springframework.context.annotation.Bean
    import org.springframework.context.annotation.Configuration
    import org.springframework.context.annotation.Profile
    import org.springframework.context.annotation.PropertySource
    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer
    
    /**
     * Created by jack on 2017/3/29.
     */
    
    @Configuration
    @PropertySource(value = *arrayOf("classpath:common.properties"))
    class ApplicationConfig {
    
        @Bean
        fun myPropertySourcesPlaceholderConfigurer(): PropertySourcesPlaceholderConfigurer {
            return PropertySourcesPlaceholderConfigurer();
        }
    
        //   ,    
        companion object {
            /**
             * Properties to support the 'test' mode of operation.
             */
            @Configuration
            @Profile(*arrayOf("devlopment", "default"))
            @PropertySource(value = *arrayOf("classpath:env-development.properties"))
            class Dev {
            }
    
            /**
             * Properties to support the 'test' mode of operation.
             */
            @Configuration
            @Profile("test")
            @PropertySource(value = *arrayOf("classpath:env-test.properties"))
            class Test {
            }
    
            /**
             * Properties to support the 'production' mode of operation.
             */
            @Configuration
            @Profile("production")
            @PropertySource(value = *arrayOf("classpath:env-production.properties"))
            class Production {
                // Define additional beans for this profile here
            }
        }
    
    
    }
    

    参照ドキュメント:
    https://github.com/kymjs/KotlinDoc-cn/blob/master/unit3/ClassesInheritance.md