《Kotlin 程序设计》第五章 Kotlin 面向对象编程(OOP)

第五章 Kotlin 面向对象编程(OOP)

1. 面向对象的HelloWorld

开篇我们来看一个OOP版本的HelloWorld:

package com.easy.kotlin
class Greeter(
val name: String//  属性
) {

    //  行为
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter("Jack").greet() // Kotlin创建对象,不再使用`new` keyword
}

Kotlin 同Java、 Scala、Groovy 一样,都使用关键字class 来定义类。

新建一个类的实例无需像 Java 一样使用 new 关键字,直接调用构造函数Greeter("Jack") 即可。

Kotlin 文件名以.kt为后缀,源代码文件中可以定义多个类。Greeter.kt经过编译之后的类文件默认命名是文件名加上Kt结尾,即为GreeterKt.class

2. 面向对象编程思想简述

在20世纪60年代,软件曾出现过严重危机,由软件错误而引起的信息丢失、系统报废事件屡有发生。为此,1968年,荷兰E.W.Dijkstra提出了程序设计中常用的GOTO语句的三大危害:

破坏了程序的动静一致性;
程序不易测试;
限制了代码优化。

此举引起了软件界长达数年的论战,并由此产生了结构化程序设计方法,同时诞生了基于这一设计方法的程序设计语言Pascal。

由瑞士Niklaus Wirth开发的Pascal,具备优秀的数据结构和控制结构,为程序员提供了极大的方便性与灵活性,大受欢迎。笔者中学时候,第一门启蒙语言就是Pascal。至今还清晰记得那台式屏幕上蓝色的Turbo Pascal界面,闪烁着白色的代码的场景。

结构化程序设计思想采用了模块分解与功能抽象和自顶向下、分而治之的方法,从而有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子程序,便于开发和维护。因此,结构化方法迅速走红,并在整个20世纪70年代的软件开发中占绝对统治地位。

但是,到了70年代末期,随着计算机科学的发展和应用领域的不断扩大,对计算机技术的要求越来越高。结构化程序设计语言和结构化分析与设计已无法满足用户需求的变化,于是面向对象编程(OOP)技术随之而来。 面向对象程序设计在未来的软件开发领域引起了大的变革,极大地提高了软件开发的效率。

面向对象语言借鉴了20世纪50年代的人工智能语言LISP,引入了动态绑定的概念和交互式开发环境的思想;始于20世纪60 年代的离散事件模拟语言SIMULA67,引入了类和继承。于20世纪70年代的Smalltalk逐渐发展成熟。Java借鉴了SmallTalk,统治了互联网开发领域的大片江山。

面向对象编程思想,是为了解决现实问题而应运而生的。面向对象编程是一种自顶向下的程序设计方法.万事万物都是对象,对象有其行为(方法), 状态(成员变量,属性)。

所谓“类”和“对象”,对应过程式语言(例如,C语言)里面的结构体(struct),本质是一个逻辑抽象的代码 “映射”(map)(一切皆是映射)。

计算机领域中的所有问题,都可以通过向上一层进行抽象封装来解决.这里的封装的本质概念,其实就是”映射“。

从面向过程到面向对象,再到设计模式,架构设计,面向服务,各种软件理论五花八门,但万变不离其宗——你要解决一个怎样的问题?你对这个世界的本质认知是怎样的?你的业务领域的逻辑问题,流程等等。

Grady Booch:我对OO编程的目标从来就不是复用。相反,对我来说,对象提供了一种处理复杂性的方式。这个问题可以追溯到亚里士多德:您把这个世界视为过程还是对象?在OO兴起运动之前,编程以过程为中心--例如结构化设计方法。然而,系统已经到达了超越其处理能力的复杂性极点。有了对象,我们能够通过提升抽象级别来构建更大的、更复杂的系统--我认为,这才是面向对象编程运动的真正胜利。

人的生命只有一次。生命太短暂,所以不要去做一些重复无聊的事情。能交给计算机做的,就尽量交给计算机去做。此乃编程的滥觞之地。

纵览整个计算机的发展史,最重要的思想非“抽象”莫属。

一层层的抽象封装了实现的细节,计算机开疆扩土,南征北战,发展到了今天蔚为壮观的互联网,云计算,大数据,机器智能的时代。

同时,也使得程序员写代码,从最初的拿着符号表在纸袋上打孔,到使用近似自然语言的高级程序设计语言来编程,以及当今各种库,api,框架,集成开发工具集,智能化的编码提示,代码生成等等技术,使得我们现在程序员,能更多的去关注问题本身以及逻辑的实现。

从只有少数技术人会用的命令行的操作系统unix、dos,到人性化的GUI图形界面操作系统,以及移动互联网时代的智能设备,计算机越来越融入到人类生活的方方面面。

正如解决数学问题通常我们会谈“思想”,诸如反证法、化繁为简等,解决计算机问题也有很多非常出色的思想。思想之所以称为思想,是因为“思想”有拓展性与引导性,可以解决一系列问题。

解决问题的复杂程度直接取决于抽象的种类及质量。过将结构、性质不同的底层实现进行封装,向上提供统一的API接口,让使用者觉得就是在使用一个统一的资源,或者让使用者觉得自己在使用一个本来底层不直接提供、“虚拟”出来的资源。

计算机中的所有问题 , 都可以通过向上抽象封装一层来解决。

《易传·系辞上传》:“易有太极,是生两仪,两仪生四象,四象生八卦。” 如今的互联网世界,其基石却是01(阴阳),不得不佩服我华夏先祖的博大精深的智慧。

就好比通过的电子电路中的电平进行01逻辑映射,布尔代数逻辑体系映射到了数字逻辑电路系统;

我们最早使用机器码01来控制电平高低,进而控制数字电路操作寄存器,CPU等尝试着表达思想中的逻辑, 控制硬件计算和显示, 发现是可行的。

后来,我们把其中的最常用的操作步骤,进一步封装抽象成CPU指令集映射,于是诞生了汇编助记符语言——比机器指令更容易记忆;

再接着, 创造了编译器、解释器和计算机高级语言。通过汇编语言的向上抽象封装一层编译器,于是有了pascal,fortran,C语言;在牺牲少量性能的情况下, 获得比汇编语言更强且更容易使用的语句控制能力:条件、分支、循环, 以及更多的语言特性: 指针、结构体、联合体、枚举等, 还创造了函数, 能够将一系列指令封装成一个独立的逻辑块反复使用;再对核心函数api进行封装形成开发包(Development Kit) 。

逐渐地,产生了面向过程的编程方法;

后来, 人们发现将数据和逻辑封装成对象, 更接近于现实世界, 且更容易维护大型软件, 又出现了面向对象的编程语言和编程方法学, 增加了新的语言特性: 继承、 多态、 模板、 异常错误。

为了不必重复开发常见工具和任务, 人们创造和封装了容器及算法、SDK, 垃圾回收器, 甚至是并发库;

为了让计算机语言更有力更有效率地表达各种现实逻辑, 消解软件开发中遇到的冲突, 还在语言中支持了元编程、 高阶函数, 闭包 等有用特性。

为了更高效率地开发可靠的软件和应用程序, 人们逐渐构建了代码编辑器、 IDE、 代码版本管理工具、公共库、应用框架、 可复用组件、系统规范、网络协议、 语言标准等, 针对遇到的问题提出了许多不同的思路和解决方案, 并总结提炼成特定的技术和设计模式, 还探讨和形成了不少软件开发过程, 用来保证最终发布的软件质量。 尽管编写的这些软件和工具还存在不少 BUG ,但是它们都“奇迹般地存活”, 并共同构建了今天蔚为壮观的互联网时代的电商,互联网金融,云计算,大数据,物联网,机器智能等等的“虚拟世界”。

纵观计算机编程发展史,人类的大脑不断抽象、努力封装一个又一个逻辑体系——使得我们逐渐能够以更加友好、更加自然的方式去编写程序。

我们知道,编程的本质就是在创造世界。当然,这是一个虚拟的世界。是人类大脑对我们真实的世界的逻辑映射。既然是一个世界,就必然会有存在(对象,数据结构), 以及无限可能变化的运动(算法,方法,函数)

不管是面向对象(存在)编程,还是函数式(运动(算法,方法,函数))编程,都是我们人类大脑对我们现实世界的问题的解决方案过程中,所建立的思维模型。模型毕竟还是模型,不可能装下全部的真实的世界。正是有像01阴阳 这种形而上的哲学概念,才有了世界的无数种可能。

以上算是一些关于编程的粗浅的思考。好了,下面言归正传,进入正题。

3.Kotlin 面向对象编程(OOP)

3.1 声明类

Kotlin使用关键字*class *声明类

class Book {
}

这个类声明被花括号包围,包括

  • 类名
  • 类head头(指定其类型参数,主构造函数等)
  • 类body。

类头和主干都是可选的。如果这个类没有body,花括号可以被省略。

class Empty

3.2 类修饰符

open 修饰符

Kotlin 默认会为每个变量和方法添加 final 修饰符。也就是说,在 Kotlin 中默认每个类都是不可被继承的。这么做的目的是为了程序运行的性能。

其实在 Java 程序中,你也应该尽可能为每个类添加final 修饰符( 见 《Effective Java 》第四章 17 条)。

如果你确定这个类是会被继承的,那么你需要给这个类添加 open 修饰符。

internal 修饰符

Java 有三种访问修饰符,public/private/protected。没有修饰符,是默认的包级别访问权限。

在 Kotlin 中,有private、protected、internal以及 public等四种修饰符,它们可用于修饰类、对象、接口、构造器、函数、属性、以及属性的set方法等。默认的访问权限是 public。其中 internal,是模块级别的访问权限。

模块(module)是指一起编译的一组 Kotlin 源代码文件:

  • 一个 IntelliJ IDEA 模块
  • 一个 Maven 工程, 或 Gradle 工程
  • 通过 Ant 任务的一次调用编译的一组文件

3.3 构造函数

在Kotlin中的类可以有主构造函数(Primary Constructor)和一个或多个二级构造函数(Secondary Constructor)。在 Scala 中称为 Main ConstructorSlave Constructor,Kotlin换了个名字,意思基本相同。

主构造函数是类头的一部分, 代码示例如下:

class Book1 constructor(val name:String, val author:String){

}

如果这个主构造函数没有任何注解或者可见的修饰符,这个constructor关键字可以被省略

class Book2 (val name:String, val author:String){

}

主构造方法的参数可以声明为 val 或 var ,使用方法与其声明为成员变量时相同。

这个主构造函数不能包含任何的代码。不过,初始化的代码可以被放置在initializer blocks(初始的语句块),以init为前缀作为关键字。该语句块中的所有可执行语句都属于主构造器,在对象被创建时都会被调用。

class Book2 (val name:String, val author:String){
    init {
        println("Book2 initialized with value (name= ${name}, author=${author})")
        name = name.toUpperCase()
    }
}

完整代码示例:

package com.easy.kotlin

/**
 * Created by jack on 2017/5/30.
 */

class Empty


class Book1 constructor(val name:String, val author:String){
    override fun toString(): String {
        return "Book1(name='$name', author='$author')"
    }
}

class Book2 (var name:String, val author:String){
    init {
        println("Book2 initialized with value (name= ${name}, author=${author})")
        name = name.toUpperCase()
    }

    override fun toString(): String {
        return "Book2(name='$name', author='$author')"
    }


}

fun main(args:Array<String>){
    println(Empty())
    println(Book1("Easy Kotlin", "Jack"))
    println(Book2("Easy Kotlin", "Jack"))
}

运行输出:

com.easy.kotlin.Empty@4b1210ee
Book1(name='Easy Kotlin', author='Jack')
Book2 initialized with value (name= Easy Kotlin, author=Jack)
Book2(name='EASY KOTLIN', author='Jack')

次(扩展)构造函数

类也可以拥有被称为"二级构造函数"(实现多个构造函数),通常被加上前缀"constructor"。

1、次构造函数不能有声明 val 或 var
2、如果类有一个主构造函数(无论有无参数),每个次构造函数需要直接或间接委托给主构造函数(即调用主构造方法或其它次构造函数),用this关键字来调用。

class Person {

    var name: String = ""
    var age: Int = 0

    constructor() {
        println("constructor 1 called!")

    }

    constructor(name: String) : this() {
        println("constructor 2 called!")
        this.name = name

    }

    constructor(name: String, age: Int) : this(name) {
        println("constructor 3 called!")
        this.name = name
        this.age = age
    }

    override fun toString(): String {
        return "Person(name='$name', age=$age)"
    }

}


fun main(args: Array<String>) {
    println(Person())
    println(Person("Jack"))
    println(Person("Jack",29))
}


运行输出:

constructor 1 called!
Person(name='', age=0)
constructor 1 called!
constructor 2 called!
Person(name='Jack', age=0)
constructor 1 called!
constructor 2 called!
constructor 3 called!
Person(name='Jack', age=29)

我们可以看出,调用构造函数Person("Jack",29) 创建对象,编译器通过层层向上委托,最终完成该对象的创建。

构造函数传参

fun main(args: Array<String>) {
    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<K, V>(val first: K, val second: V) {
    operator fun component1(): K {
        return first
    }

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

class Triple<K,V,T>(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

3.4 创建类的实例

要创建一个类的实例,我们只要像普通的函数那样调用其构造函数即可:

val person = Person("Jack",29)

Kotlin中,不再使用new关键字

类成员

类可以包括

  • 构造和初始化模块
  • 函数
  • 属性
  • 匿名类
  • 内部类
  • 对象声明

3.5 继承

在Kotlin所有的类中都有一个共同的父类Any(这跟Scala一样),这是一个默认的open根父类。我们看一下Any类的源码吧:

package kotlin

/**
 * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
 */
public open class Any {
    /**
     * Indicates whether some other object is "equal to" this one. Implementations must fulfil the following
     * requirements:
     *
     * * Reflexive: for any non-null reference value x, x.equals(x) should return true.
     * * Symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
     * * Transitive:  for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true
     * * Consistent:  for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
     *
     * Note that the `==` operator in Kotlin code is translated into a call to [equals] when objects on both sides of the
     * operator are not null.
     */
    public open operator fun equals(other: Any?): Boolean

    /**
     * Returns a hash code value for the object.  The general contract of hashCode is:
     *
     * * Whenever it is invoked on the same object more than once, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
     * * If two objects are equal according to the equals() method, then calling the hashCode method on each of the two objects must produce the same integer result.
     */
    public open fun hashCode(): Int

    /**
     * Returns a string representation of the object.
     */
    public open fun toString(): String
}

仔细阅读注释部分的内容,我们基本就能知道这个Any是啥了。

我们使用Kotlin编程,声明的所有类,都默认继承这个Any类。

class Example // Implicitly inherits from Any

Kotlin中所有的类默认都是不可继承的(final),所以我们只能继承那些明确声明open或者abstract的类。

当我们只有单个构造器时,我们需要在从父类继承下来的构造器中指定需要的参数。

代码示例:


open class Person {

    var name: String = ""
    var age: Int = 0

    constructor() {
        println("constructor 1 called!")

    }

    constructor(name: String) : this() {
        println("constructor 2 called!")
        this.name = name

    }

    constructor(name: String, age: Int) : this(name) {
        println("constructor 3 called!")
        this.name = name
        this.age = age
    }

    override fun toString(): String {
        return "Person(name='$name', age=$age)"
    }

    fun sayHi() {
        val name = this.name
        println("${name} say Hi to u!")
        val b = Book("Easy Kotlin")
        println(b)
    }


    class Book {
        var name: String = ""

        constructor() {

        }

        constructor(name: String) : this() {
            this.name = name
        }

        override fun toString(): String {
            return "Book(name='$name')"
        }


    }

}

class Student : Person {
    var id: String = ""
    var sex: String = ""

    constructor() {
    }

    constructor(id: String, sex: String) : super() {
        this.id = id
        this.sex = sex
    }

    constructor(name: String, id: String, sex: String) : super(name) {
        this.id = id
        this.sex = sex
    }

    constructor(name: String, age: Int, id: String, sex: String) : super(name, age) {
        this.id = id
        this.sex = sex
    }

    override fun toString(): String {
        return "Student(id='$id', sex='$sex', name=${super.name}, age = ${super.age})"
    }


}

我们使用super.name, super.age来调用父类中的属性字段。

3.6 接口和抽象类

Kotlin接口使用interface关键字。Kotlin 的接口类似于 Java 8。可以包含抽象方法,以及方法的实现。和抽象类不同的是,接口不能保存状态;可以有属性但必须是抽象的 或 提供访问实现。

Kotlin抽象类使用abstract关键字声明。

Kotlin中的继承抽象类,实现接口的处理方式,跟Java一样,采用“单继承,多实现”的方式。代码示例如下:


abstract class A {
    abstract fun fa()
    abstract fun f()
}

interface B {
    fun fb() {
        print("FB")
    }

    fun f() {
        print("B")
    }
}

class C : A, B {
    override fun fa() {
    }

    override fun fb() {
    }

    override fun f() {
    }

    constructor() {

    }


}

我们直接使用class C : A, B这样的写法。比Java中使用extends, implements要简洁。

接口和抽象类的函数,默认是open的。我们可以不用标注。

另外,我们可以重写一个 open 非抽象类的open函数,得到一个抽象类的抽象函数。

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

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

3.7 实现接口

package com.easy.kotlin

/**
 * Created by jack on 2017/5/30.
 */

interface Clickable {
    fun click()
}
class Button : Clickable {
    override fun click() = println("I was clicked")
}

fun main(args: Array<String>) {
    Button().click()
}


Kotlin中实现接口,使用冒号:关键字标识。实现类前使用override 关键字修饰。

3.8 override重写覆盖父类函数

Kotlin追求简洁显式的风格。

Kotlin在继承父类并覆盖父类函数时,要求父类必须有open标注,被覆盖的函数必须有open标注,并且子类的函数必须加override标注:

open class Base {
    open fun v() {}
    fun notopenv() {}
}

class Derived : Base() {
    override fun v() {}
//    override fun notopenv(){}// 'notopenv' in 'Base' is final, so it cannot be overridden
//    fun notopenv() {} // 这样写也是不允许的
}

Derived.v()函数上必须加上override标注。如果没写,编译器将会报错。

如果父类的这个函数

open fun v() {}

没有标注open,则子类中不允许定义同名函数,不论加不加override

成员标记为override的本身是开放的,也就是说,它可以在子类中重写。如果你想禁止重写,使用final关键字:

open class AnotherDerived() : Base() {
  final override fun v() {}
}

3.9 使用伴生对象声明静态类和方法

Kotlin中的伴生对象(companion objects),应用的场景就是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;

@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
        }
    }


}

我们使用

//静态类,伴生对象
companion object {}

来声明静态类和方法。

3.10 嵌套类Nested Class

类可以嵌套在其他类中:

class Outer {
    private val bar: Int = 1

    class Nested {
        fun foo1() = 2
        //fun foo11() = bar// 访问不到
    }

}

fun main(args: Array<String>) {
    val nestedDemo = Outer.Nested().foo1() // 2
    println(nestedDemo)
}

但是,这两个类Outer, Nested 对象成员之间不能直接访问。比如说,

    class Nested {
        fun foo1() = 2
        //fun foo11() = bar// 访问不到
    }

Nested里面的函数foo11 是不能直接使用Outer的成员变量bar的。要想访问外部类的成员变量,我们可以使用内部类。

3.11 内部类Inner Class

类可以标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用:

class Outer {
    private val bar: Int = 1

    class Nested {
        fun foo1() = 2
        //fun foo11() = bar// 访问不到
    }

    inner class Inner {
        fun foo2() = bar // inner class 能够访问外部类的成员
    }
}

fun main(args: Array<String>) {
    val nestedDemo = Outer.Nested().foo1() // 2
    val innerDemo = Outer().Inner().foo2() // 1

    println(nestedDemo)
    println(innerDemo)
}

3.12 使用Kotlin的对象表达式创建匿名内部类

Kotlin使用对象表达式创建匿名内部类实例:

window.addMouseListener(object: MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ……
    }
                                                                                                            
    override fun mouseEntered(e: MouseEvent) {
        // ……
    }
})

如果对象是函数式 Java 接口(即具有单个抽象方法的 Java 接口)的实例,
我们可以直接使用lambda表达式创建它:

val listener = ActionListener { println("clicked") }

多个超类型可以由跟在冒号后面的逗号分隔的列表指定:

open class AA(x: Int) {
    open val y: Int = x
}

interface BB {}

val ab: AA = object : AA(1), BB {
    override val y = 100
}

如果我们只需要“一个对象”,那么我们可以简单地写:

    fun adHocf() {
        val adHoc = object {
            var x: Int = 0
            var y: Int = 0
        }

        println("adHoc.x + adHoc.y = " + (adHoc.x + adHoc.y))
    }

完整代码示例:

package com.easy.kotlin

/**
 * Created by jack on 2017/5/30.
 */

class Outer {
    private val bar: Int = 1

    class Nested {
        fun foo1() = 2
        //fun foo11() = bar// 访问不到
    }

    inner class Inner {
        fun foo2() = bar // inner class 能够访问外部类的成员
    }
}


open class AA(x: Int) {
    open val y: Int = x
}

interface BB {}

val ab: AA = object : AA(1), BB {
    override val y = 100
}


class  Hoc{
    fun adHocf() {
        val adHoc = object {
            var x: Int = 0
            var y: Int = 0
        }

        println("adHoc.x + adHoc.y = " + (adHoc.x + adHoc.y))
    }
}



fun main(args: Array<String>) {
    val nestedDemo = Outer.Nested().foo1()
    val innerDemo = Outer().Inner().foo2()

    println(nestedDemo)// 2
    println(innerDemo)// 1

    val h = Hoc()
    println(h.adHocf())
    val a = AA(1)
    println(a.y) //1
    println(ab.y) // 15
}



3.13 数据类data class

data 修饰的类称之为数据类。它通常用在我们写的一些 POJO 类上。

代码示例:

data class User(val name: String, val age: Int)

这个对应我们写的Java的Bean类。这些类有如下基本方法:

  • equals()/ hashCode() 函数
  • toString() 默认格式是: "User(name=John, age=42)",
  • componentN() 函数 corresponding to the properties in their order of declaration,
  • copy() 函数

其中,componentN()函数是通过属性位置来直接访问属性值。

完整代码示例:

package com.easy.kotlin

/**
 * Created by jack on 2017/5/30.
 */

data class User(val name: String, val id: Int, val password: String)


fun main(args: Array<String>) {
    val user = getUser()
    println("name = ${user.name}, id = ${user.id}, password = ${user.password}")

    val (name, id) = getUser()
    println("name = $name, id = $id")
    println("name = ${getUser().component1()}, id = ${getUser().component2()}")
}

fun getUser(): User {
    return User("Alex", 1, "123456")
}

数据库实体类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<Customer>)

data class Customer(val name: String, val city: City, val orders: List<Order>) {
    override fun toString() = "$name from ${city.name}"
}

data class Order(val products: List<Product>, 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
}



3.14 建造者模式:构建一个对象

定义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())
}


3.15 封装一个日期工具类

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)
}



3.16 枚举类

在 Kotlin 中,每个枚举常量都是一个对象。枚举常量用逗号分隔。 代码示例:

package jason.chen.mini_springboot.restful.config

/**
 * Created by jack on 2017/6/1.
 */
enum class KotlinBin(val binPath: String) {
    KOTLINC("src/main/resources/kotlinc/bin/kotlinc "),
    KOTLIN("src/main/resources/kotlinc/bin/kotlin ")
}

代码这样调用

package jason.chen.mini_springboot.restful.service

import jason.chen.mini_springboot.restful.config.KotlinBin
import org.springframework.stereotype.Service
import java.io.File

/**
 * Created by jack on 2017/5/31.
 */

@Service
class KotlincService {

    fun kotlinc(ktFile:String){
        val file = File(".")
        file.listFiles().forEach(::println)
        val kotlinc = KotlinBin.KOTLINC.binPath + ktFile
        val runtime: Runtime = Runtime.getRuntime()
        val process: Process = runtime.exec(kotlinc)
        val exitValue = process.waitFor()
        if (exitValue != 0) {
            println("exit with $exitValue")
            return
        }

        process.inputStream.bufferedReader().lines().forEach {
            println(it)
        }
    }
    fun kotlin(ktFile:String):String{
        kotlinc(ktFile)

        val ktClass = ktFile.substring(0, ktFile.indexOf(".kt")) + "Kt"

        val kotlin = KotlinBin.KOTLIN.binPath + ktClass
        val runtime: Runtime = Runtime.getRuntime()
        val process: Process = runtime.exec(kotlin)
        val exitValue = process.waitFor()
        if (exitValue != 0) {
            println("exit with $exitValue")
            return "exit with $exitValue"
        }

        var result=""
        process.inputStream.bufferedReader().lines().forEach {
            println(it)
            result+=it
        }
        return result
    }
}

3.17 sealed 密封类

sealed 修饰的类称为密封类,用来表示受限的类层次结构。例如当一个值为有限集中的 类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。

小结

参考文档:

https://github.com/kymjs/KotlinDoc-cn/blob/master/unit3/ClassesInheritance.md


Kotlin开发者社区

专注分享 Java、 Kotlin、Spring/Spring Boot、MySQL、redis、neo4j、NoSQL、Android、JavaScript、React、Node、函数式编程、编程思想、"高可用,高性能,高实时"大型分布式系统架构设计主题。

High availability, high performance, high real-time large-scale distributed system architecture design

分布式框架:Zookeeper、分布式中间件框架等
分布式存储:GridFS、FastDFS、TFS、MemCache、redis等
分布式数据库:Cobar、tddl、Amoeba、Mycat
云计算、大数据、AI算法
虚拟化、云原生技术
分布式计算框架:MapReduce、Hadoop、Storm、Flink等
分布式通信机制:Dubbo、RPC调用、共享远程数据、消息队列等
消息队列MQ:Kafka、MetaQ,RocketMQ
怎样打造高可用系统:基于硬件、软件中间件、系统架构等一些典型方案的实现:HAProxy、基于Corosync+Pacemaker的高可用集群套件中间件系统
Mycat架构分布式演进
大数据Join背后的难题:数据、网络、内存和计算能力的矛盾和调和
Java分布式系统中的高性能难题:AIO,NIO,Netty还是自己开发框架?
高性能事件派发机制:线程池模型、Disruptor模型等等。。。

合抱之木,生于毫末;九层之台,起于垒土;千里之行,始于足下。不积跬步,无以至千里;不积小流,无以成江河。

Kotlin 简介

Kotlin是一门非研究性的语言,它是一门非常务实的工业级编程语言,它的使命就是帮助程序员们解决实际工程实践中的问题。使用Kotlin 让 Java程序员们的生活变得更好,Java中的那些空指针错误,浪费时间的冗长的样板代码,啰嗦的语法限制等等,在Kotlin中统统消失。Kotlin 简单务实,语法简洁而强大,安全且表达力强,极富生产力。

Java诞生于1995年,至今已有23年历史。当前最新版本是 Java 9。在 JVM 生态不断发展繁荣的过程中,也诞生了Scala、Groovy、Clojure 等兄弟语言。

Kotlin 也正是 JVM 家族中的优秀一员。Kotlin是一种现代语言(版本1.0于2016年2月发布)。它最初的目的是像Scala那样,优化Java语言的缺陷,提供更加简单实用的编程语言特性,并且解决了性能上的问题,比如编译时间。 JetBrains在这些方面做得非常出色。

Kotlin语言的特性

用 Java 开发多年以后,能够尝试一些新的东西真是太棒了。如果您是 Java 开发人员,使用 Kotlin 将会非常自然流畅。如果你是一个Swift开发者,你将会感到似曾相识,比如可空性(Nullability)。 Kotlin语言的特性有:

1.简洁

大幅减少样板代码量。

2.与Java的100%互操作性

Kotlin可以直接与Java类交互,反之亦然。这个特性使得我们可以直接重用我们的代码库,并将其迁移到 Kotlin中。由于Java的互操作性几乎无处不在。我们可以直接访问平台API以及现有的代码库,同时仍然享受和使用 Kotlin 的所有强大的现代语言功能。

3.扩展函数

Kotlin 类似于 C# 和 Gosu, 它提供了为现有类提供新功能扩展的能力,而不必从该类继承或使用任何类型的设计模式 (如装饰器模式)。

4.函数式编程

Kotlin 语言一等支持函数式编程,就像Scala一样。具备高阶函数、Lambda 表达式等函数式基本特性。

5.默认和命名参数

在Kotlin中,您可以为函数中的参数设置一个默认值,并给每个参数一个名称。这有助于编写易读的代码。

6.强大的开发工具支持

而由于是JetBrains出品,我们拥有很棒的IDE支持。虽然Java到Kotlin的自动转换并不是100% OK 的,但它确实是一个非常好的工具。使用 IDEA 的工具转换Java代码为 Kotlin 代码时,可以轻松地重用60%-70%的结果代码,而且修改成本很小。

Kotlin 除了简洁强大的语法特性外,还有实用性非常强的API以及围绕它构建的生态系统。例如:集合类 API、IO 扩展类、反射API 等。同时 Kotlin 社区也提供了丰富的文档和大量的学习资料,还有在线REPL。

A modern programming language that makes developers happier. Open source forever

图来自《Kotlin从入门到进阶实战》 (陈光剑,清华大学出版社)
图来自《Kotlin从入门到进阶实战》 (陈光剑,清华大学出版社)

https://kotlinlang.org/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容