Kotlin -- Kotlin初探

Kotlin -- Kotlin初探

 

最近Kotlin从之前的默默无闻,到现在着实火了一把。跟着潮流,也从中文站上下了一份文档来看了一下。之后感觉现在Kotlin系统性的学习资料还比较缺乏,就照着那份指导文档,大致看了下Kotlin的语法等基础内容。现把该过程中的一些示例代码记录下来,供后续有机会再深入学习时回顾。

 

示例代码:

package xzm.kotlin.main

/**
 * Created by Fassbender on 2017/6/28.
 */

import com.xzm.test.SingleFun
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.lang.IllegalArgumentException
import javax.swing.JComponent
import kotlin.reflect.KProperty

fun main(args: Array<String>) {
    println("hello kotlin!")

    println("add1: " + add1(1, 2))
    println("add2: " + add2(2, 2))
    println("add3: " + add3(3, 4))

    add4(5, 6)
    add5(7, 8)

    varIntPrint()
    varStrPrint()

    var s1 = "I'm s1"
    var s2 = "I'm s2"
    println("handleString: " + handleString(s1, s2))

    val str = "abcdefg"
    val check = 1
    typeAutoCheck(str)
    typeAutoCheck(check)

    loopCheck()

    describe(1)

    defaultParam()

    defaultParam(1)

    defaultParam(2, "aaa")

    handleStringWithNull(null, "bb")

    objCallMethods()

    handleClassPart()

    handleInheritPart()
}

/**
 *两个Int类型的参数,返回值为Int;普通形式
 *关键字:fun
 *fun methodName(varName : Type, varName : Type) : returnType
 *
 */
fun add1(a: Int, b: Int): Int {
    return a + b
}

/**
 *两个Int类型的参数,返回值为Int
 *关键字:fun
 *fun methodName(var1 : Type, var2 : Type) : returnType = var1 + var2
 *
 *fun methodName(var1 : Type, var2 : Type) = var1 + var2
 *
 *将一个表达式作为函数体,可以自动推断返回类型;可略去{}
 */
fun add2(a: Int, b: Int): Int = a + b

fun add3(a: Int, b: Int) = a + b

/**
 *函数返回无意义的值:Unit(Java -> void)
 *
 *$可以获取变量值
 *
 */
fun add4(a: Int, b: Int): Unit {
    println("here $a and $b is ${a + b}")
}

/**
 *返回无意义的值时,返回类型可以不写
 *
 */
fun add5(a: Int, b: Int) {
    println("here $a and $b is ${a + b}")
}


/**
 * val 是不可变赋值,过后不能改变它的值;本质上是只声明了getter()方法
 * var 是可变赋值,过后可以再改变变量的值;本质上同时声明了getter()/setter()方法
 * 声明一个属性的完整语法是:
 * var <propertyName>[: <PropertyType] [=<property_initializer]
 *  [<getter>]
 *  [<setter>]
 * 其中initializer/getter/setter都是可选的.如果属性类型可以从initializer或者getter推断出,也PropertyType声明可省略
 */
fun varIntPrint() /*: Unit*/ {

    val a: Int = 1 //有明确类型、初始值
    val b = 2 //自动推断类型为Int,并赋初值2
    val c: Int //如果没有明确初始值, Int不能省略
    c = 3 //此时初始化,没有初始化的变量都无法使用

    var d: Int
    d = 4

    var e: Int //必须有初始值,不然无法使用
    e = 5 //注掉初始化, $e读参失败

    println("here a is $a ,b is $b, c is $c, d is $d, e is $e")
}

/**
 *使用字符串模板,即可以包含一小段代码
 */
fun varStrPrint() /*: Unit*/ {
    var a = 1

    var s1 = "a is $a"

    val s2 = "but it does't matter"

    var s3 = "${s1.replace("is", "was")}, $s2"

    var s4 = "${s3.isNotBlank()}"

    var bool = s3.isNullOrBlank()

    println("$s3, is Blank : $s4, bool: $bool")
}

/**
 *条件表达式
 */
fun maxOf(a: Int, b: Int) = if (a > b) a else b

fun maxOf1(a: Int, b: Int): Int {
    if (a > b)
        return a
    else
        return b
}

/**
 *当函数中某个变量的值可能为null的时候,需要在它的声明类型后加入'?'标识,否则调用的时候不能传入null作为参数;
 */
fun handleString(str1: String?, str2: String): String? {
    if (str1 == null)
        return "arg is null, return directly"
    else
        return "str1 is $str1, str2 is $str2"
}

/**
 *对象调用时,?会确保不出现空指针调用
 *expression ?: **** -> expression为null时,执行****
 */
fun handleStringWithNull(str1: String?, str2: String): String? {
    val s1 = str1?.toUpperCase() ?: "str1 is null, this is default vavlue"//这样声明如果去掉了此时的?,编译会报错;kotlin是空安全的语言,因为str1可能为null,这里必须加上?,表明str1不为null时,才会调用到该函数.
    val s2 = str2.toUpperCase() //str2不需要这样,因为str2参数本来就不能为null,如果函数调用处传入null,编译也会出错;当然加上?,也没有问题,只不过多此一举
    println("str1: $s1, str2: $s2")
    return null;
}

/**
 *类型检测/自动类型转换
 *is运算符用于检测一个表达式是否是某个类型的一个实例;如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接将表达式当做该类型使用,而无需显示转换
 */
fun typeAutoCheck(obj: Any = "") /*: Unit*/ {
    if (obj is String) { // is 关键字
        println("the str length: ${obj.length}")
    } else
        println("type error, length is invalid")
}

/**
 *使用for or while
 */
fun loopCheck() {
    val items = listOf("aa", "bb", 1) //创建通用list时,此处看出不要求类型一致

    for (it in items) { // in 关键字
        println("for list content: $it")
    }

    for (index in items.indices) {//库字段:0...length-1
        println("for use indices list content:${items[index]}")
    }

    for ((index, value) in items.withIndex()) {//库函数withIndex(),使用(index, value) -> (下标,值)的形式遍历
        println("for use withIndex list content current index: $index, the value: $value")
    }

    var index: Int = 0
    while (index < items.size) {
        println("while list content: ${items[index]}")
        index++
    }

    var maps = mapOf(1 to "a", "b" to 2, 3 to "3") //创建通用maps时,此处看出不要求类型一致
    for ((k, v) in maps)
        println("maps loop current key: $k, value: $v")

    val arrays = arrayOf(1, 2, 3, "aa") //创建通用数组时,可以看出对数组元素类型不要求一致
    val a = Array(5, { i -> (i * i).toString() }) //接受数组大小和一个函数参数的工厂函数来创建数组,函数参数给定每个索引的值
    val b = intArrayOf(1, 2, 3) //创建特定类型,如Int时,则必须保持类型一致;对应数组的还有Byte/Short等

    for (value in b) {
        println("loop intArrayOf value: $value")
    }
    for (value in arrays)
        println("array loop current value: $value")

    for (value in a)
        println("constructor array current value: $value")

    val str = "abcdefg" //字符串值是不可变的,可以使用索引来访问字符串中的某个字符值:str[i]
    "bbb".toUpperCase()
    for (c in str)
        println("get value use index c: $c")

    val escapestr = "Hello World! \n" //转义字符串
    val rawstr = """for (c in str) {
    |println("value: $\n")
}""" //原始字符串

    println(escapestr)
    println(rawstr)

    //$打印
    val escapestr2 = "$9.99"
    val rawstr2 = """$9.99"""
    val rawstr3 = """${'$'}9.99"""

    println(escapestr2)
    println(rawstr2)
    println(rawstr3)

    val aa: Byte? = 1
    val bb: Int? = aa?.toInt() //aa不能直接转为为Int,需要显示提升类型;Kotlin中,较小类型并不是较大类型的子类型
}

/**
 *使用when代替传统的switch...case语句
 *when既可以当表达式使用,也是当语句使用.如果单做表达式使用,则符合条件的分支的值即是表达式的值;如果当做语句使用,则忽略个别表达式的值
 *when作为表达式使用时,必须有else分支,除非编译器能检测出所有的可能情况都已经覆盖了;
 *我们可以使用任意表达式作为分支条件
 *
 *另外,变种的when语句可以起到跟某些if()结构类似的效果,比如if - else/if - else if - else
 */
fun describe(obj: Any) {
    val items = listOf("aa", "bb", "cc", 1)
    when (obj) { //条件顺序比较,直到找到合适的分支匹配;否则,进入else分支

        "Hello" -> println("current value is $obj")
        is String -> println("current type is String: $obj")
        in items -> println("current obj $obj in the items") //该项内容是不是在某个集合中
        2, 3 -> println("match this branch, current value is: $obj") //多个分支需要使用相同的处理
        in 8..10 -> println("current value is inside Range") //可以判断某个值是否在一个区间内
        !in 20..22 -> println("current value is out of Range") //可以判断某个值是否在一个区间内
        else
        -> println("Not support all branch") //else 分支相当于 Java中的default
    }
}

/**作为表达式使用,when()写成单表达式*/
fun describeExpression(obj: Any) = when (obj) {
    "aa" -> println("match aa")
    "bb" -> println("match bb")
    else
    -> {
        println("Not match any of them")
    }
}

fun describeExpressionReturnInt(obj: Any): Int = when (obj) {
    1 -> 1
    "Red" -> 2
    obj is Test -> 3
    else
    -> throw IllegalArgumentException("Arguments is error") //throw表达式的类型是特殊类型	Nothing。该类型没有值,而是用于标记永远不能达到的代码位置
}

/**
 *缺省参数函数
 */
fun defaultParam(a: Int = 0, str: String = "defaultParam") {
    println("defaultParam a: $a, str: $str")
}

/**
 *单表达式函数
 */

fun theAnswer() = 42

fun theAnswer1(): Int {
    return 42
}

/*
 *单表达式函数变形
 */
fun theAnswer2(value: Any) = when (value) {
    1 -> println("current value is $value")
    2 -> println("current value is $value")
    else
    -> println("None of support value")
}

/**
 *with()
 *一个对象调用多个函数
 *使用可空Boolean
 */
class Test {
    fun penDown() {
        println("call penDown")
    }

    fun penUp() {
        println("call pendUp")
    }

    fun turn() {
        println("call turn")
    }

    fun forward() {
        println("call forward")
    }

    fun isNull() {
        println("call obj is null")
    }
}

fun objCallMethods() {
    val test = Test()
    with(test) {
        penDown()
        turn()
        forward()
        penUp()
    }

    var t: Test? = null

    println(t?.penDown() ?: "call obj is null")

    val bool: Boolean? = null
    if (bool == true)
        println("bool is true")
    else
        println("bool in false or null")
}

/**
 *kotlin中的类默认是final的(java语义),不可继承;要设计可继承的类,在class前添加open关键字,标识该类可继承
 *kotlin没有new关键字
 *主构造函数和次构造函数
 *次构造函数可以委托给主构造函数
 *如果主构造函数的所有参数都有默认值,在JVM上,Kotlin会生成一个额外的无参构造函数,此时所有的属性将采用默认值;
 *但如果存在有限个参数全有默认值的次构造函数,该无参构造含会被覆盖(或者说我们相当于显示声明了无参构造和函数)
 *
 * 一般地,属性声明为飞控类型必须在构造函数中初始化;lateinit关键字在这种情景下有些特殊
 */
class ClassA /*private*/ constructor(var a: Int = -1, var str: String = "default") {

    init {
        this.a = a
        this.str = str
    }

    constructor(a: Int) : this(a, "a pararm")

    constructor(str: String) : this(3, str)
}

fun handleClassPart(): Unit {
    val classA = ClassA(2)
    val classB = ClassA("cc")
    val classC = ClassA()
    println("classA a: ${classA.a}, str: ${classA.str} -- classB a: ${classB.a}, str: ${classB.str} -- classC a: ${classC.a}, str: ${classC.str}")
}

/**
 *kotlin中的类默认是不可继承的,使用open(意为开发)关键字可将类声明为可继承类;这里只是类可继承,但不代表子类可以覆盖它的成员
 *kotlin需要使用open显示声明可覆盖的成员(基类中),使用override显示声明覆盖后的成员(子类中)
 *使用final关键字可以再次禁止覆盖成员(override),它不能与open关键字同时修饰
 *属性覆盖与接口覆盖类似.我们可以使用var/val属性覆盖一个val属性,但不能用val属性覆盖var属性.
 * kotlin中类不能字段,但可以有属性
 */
open class Base constructor(var aBase: Int = -1, var bBase: Int = -2) {

    init { //主构造函数的初始化模块

    }

    open var x: Int? = 5

    open val y: Int = 23

    open val z: Int = 3

    open fun f(): String {
        return "f() in Base"
    }

    open fun g(): String {
        return "g() in Base"
    }

    open fun h() {
        println("h() int Base")
    }

    open fun i() {
        println("i() in Base")
    }

    open fun j() {
        println("j() in Base")
    }

    open fun k() {
        println("k() in Base")
    }
}

//无需声明成员为open,因为这是显而易见的;接口的方法体是可选的,因为子类必须覆盖这些方法
interface Inter {
    fun h() {
        println("h() in Inter")
    }

    fun m()
}

/**
 * 接口中定义的属性要么是抽象的,不能提供初始化;要么提供访问器的实现.
 * 在接口中定义的属性不能用有幕后字段(backing field),因此接口中的访问器不能引用它们
 */
interface Inter1 {

    var a: Int //抽象的属性

    //var a: Int // 编译错误,var的变量get()会访问幕后字段? val为什么可行
    //    get() = 2

    val str: Int
        get() = 1

    val propertyWithImplementation: String
        get() = "foo"


    fun h() {
        println("h() int Inter1")
    }

    fun g()
}

/**
 * 没有必要用open标注一个抽象类或抽象成员,因为这是显而易见的
 * kotlin可以声明抽象类,抽象类的成员覆盖与普通类似;我们可以用一个abstract成员覆盖一个非抽象类的open成员;抽象成员在本类中可以不用实现
 */
abstract class Abs : Base() {
    override fun i() {
        println("i() in Abs")
    }

    override abstract fun j()
}

/**
 * 子类必须实现抽象类父类的抽象成员
 */
class AbsImpl : Abs() {
    override fun j() {
        println("j() override in BasImpl")
    }
}

open class Sub constructor(var aSub: Int = 0, override var x: Int? = -1/*主构造函数中覆盖属性*/) : Base(aSub), Inter {

    override val y: Int = 2 //也可以在主构造函数中覆盖属性

    override val z: Int = 4

    override fun f(): String {
        return "f() in Sub"
    }

    final override fun g(): String { //使用final关键字可以再次禁止覆盖,即Sub的子类无法覆盖g()
        return "g() in Sub"
    }

    override fun h() { //从基类和接口同时继承了h()的两份实现,这里必须要覆盖h()以消除歧义;同时,我们可以使用super<ClassName>.h()的方式来显示表明需要调用哪一份实现
        super<Inter>.h()
    }

    override fun m() {
        println("m() override in Sub")
    }

    /**
     * 声明一个属性的完整语法是:
     * var <propertyName>[: <PropertyType] [=<property_initializer]
     *  [<getter>]
     *  [<setter>]
     * 其中initializer/getter/setter都是可选的.如果属性类型可以从initializer或者getter推断出,也可省略
     */
    var s: Int = 3 //显示声明属性的getter/setter
        get() {
            return field
        }
        set(value) { //value就是sub.s = xx的xx值
            field = value
        }

    val m: Int get() = 3

    val n get() = 6

    //var j : Int get() = 3 //编译错误,'Property must be initialized'

}

fun handleInheritPart() {

    val sub = Sub()

    sub.h()
    println("call Inherit method ${sub.f()} -- ${sub.g()}")

    var base = Base()

    println("attribute override sub.y: ${sub.y},sub.x: ${sub.x},sub.z: ${sub.z},sub.m: ${sub.m},sub.n: ${sub.n} --> base.y: ${base.y}, base.x: ${base.x}, base.z: ${base.z}")

    val impl: AbsImpl = AbsImpl()
    impl.i()
    impl.j()
    impl.k()

    println("const str: $str")

    println("Sub s: ${sub.s}")

    sub.s = 22

    println("Sub s: ${sub.s}")

    val exten = ExtensionClass()
    exten.h()
    exten.g()

    printFoo(D())

    val c = C()
    c.b = 3
    println("Extension property in C a: ${c.a}, b: ${c.b}")

    F().caller(E())

    test()

    testAnon()

}

/**
 * 已知值的属性可以使用const修饰符声明为编译常量,这些属性需满足以下条件
 * 1、位于顶层或者Object的一个成员
 * 2、用String或原生类型初始化
 * 3、没有自定义getter
 */
private const val str: String = "this is const str"

/**
 * 顶层声明情况:
 * 1、如果不指定任何可见性标识符,默认为public;此时该声明将随处可见
 * 2、如果声明为private,则它将在声明它的文件内可见
 * 3、如果声明为internal,则它会在相同模块内可见
 * 4、protected不适用于顶层声明
 *
 * 3中的模块是指编译在一起的一套Kotlin文件:
 * --⼀个 IntelliJ IDEA 模块;
 * --⼀个 Maven 或者 Gradle 项⽬;
 * --⼀次 <kotlinc> Ant 任务执⾏所编译的⼀套⽂件。
 *
 * 类内部声明情况:
 * private意味着只在这个类内部(包含其所有可见成员)可见
 * protected和private一样 + 在子类中可见
 * internal能见到类声明的本模块内的任何客户端都可见其internal成员
 * public能见到类声明的任何客户端都可见其public成员
 * Kotlin中外部类没法访问内部类的private成员
 * 如果你覆盖一个protected成员而未显示指定其可见性,该成员还将是protected可见性
 *
 * 局部声明情况:
 * 局部变量、函数和类不能有可见性修饰符
 *
 */


//函数拓展
class ExtensionClass {
    fun g() {
        println("g() in ExtensionClass")
    }
}

/**
 * 声明拓展函数,需要用它的接收者(被扩展的类型)类型作为前缀,即类型名;特殊地,可以为可空的接收者类型定义扩展,即使其值为null
 *
 * 扩展并不能真正的修改它所扩展的类.通过扩展,你并没有在类中插入一个新成员;仅仅是可以通过该类型的变量用点表达式去调用这个新函数
 */
fun ExtensionClass.h() {
    if (this == null) return //this对象指向当前调用该扩展函数的实例对象
    println("Extension fun h() for ExtensionClass")
}

/**
 * 如果一个定义有一个扩展函数和成员函数,而这两个函数有相同的接收者类型、函数名,并且都适用于给定的参数,这种情况总是取成员函数
 *
 * 当然扩展函数重载同样名字但不同签名的成员函数同样可以,编译器此时总是可以找到的合适的函数去调用
 */
fun ExtensionClass.g() {
    println("Extension fun g() for ExtensionClass")
}

/**
 * 扩展函数是静态分发的
 *
 * 调用的扩展函数由函数调用所在的表达式的类型来决定,而不是由表达式运行时结果决定
 */
open class C

class D : C()

fun C.foo() = "C"
fun D.foo() = "D"

fun printFoo(c: C) {
    println(c.foo()) //调用的扩展函数只取决于参数c的类型,该类型是C
}

// printFoo(D()) -> C

//由于扩展并没有真正插入到类中,因此对扩展属性来说幕后字段是无效的;因此扩展属性不能有初始化器,它们的行为只能由显示提供的setter/getter定义
val C.a: Int
    get() = 1

var C.b: Int
    get() = 2
    set(value) {
        2 * value
    }

/**
 * 扩展声明为成员
 *
 * 在一个类内部,你可以为另一个类声明扩展.在这样的扩展内部,有多个隐式接收者--其中的成员可以无需通过限定符访问.
 *
 * 扩展声明所在的类的实例称为分发接收者,扩展方法调用所在的接收者类型的实例称为扩展接收者
 *
 * 对于分发接收者和扩展接收者的成员名字冲突的情况,扩展接收者优先.引用分发接收者的成员你可以使用限定的this语法(eg: this@F)
 */
class E {
    fun bar() {
        println("bar() in E")
    }

    override fun toString(): String {
        return "toString() in E"
    }
}

class F {
    fun baz() {
        println("baz() in F")
    }

    fun E.foo() {
        bar() //调用E.bar
        baz() //调用F.baz

        println(toString()) //调用E.toString
        println(this@F.toString()) //调用F.toString
    }

    override fun toString(): String {
        return "toString() in F"
    }

    fun caller(e: E) {
        e.foo()
    }
}

/**
 * 声明为成员的扩展可以声明为open并在子类中覆盖.这意味着这些函数的分发对于分发接收者类型是虚拟的(动态分发/多态),但对于扩展接收者类型是静态的.
 */
open class G {
}

class G1 : G() {
}

open class H {
    open fun G.foo() {
        println("G.foo in H")
    }

    open fun G1.foo() {
        println("G1.foo in H")
    }

    fun caller(g: G) {
        g.foo()   // 调⽤扩展函数
    }
}

class H1 : H() {
    override fun G.foo() {
        println("G.foo in H1")
    }

    override fun G1.foo() {
        println("G1.foo in H1")
    }
}

fun test() {
    //函数扩展
    H().caller(G())   // 输出 "G.foo in H"
    H1().caller(G())  // 输出 "G.foo in H1" —— 分发接收者虚拟解析
    H().caller(G1())  // 输出 "G.foo in H" —— 扩展接收者静态解析
    H1().caller(G1()) // 输出 "G.foo in H1"

    //数据类测试
    println("Parcel name: ${p2.name}, age: ${p2.age}")

    val (name, age) = p1 //解构
    println("Parcel name: $name, age: $age")
}

/**
 * 我们经常需要创建一些只保存数据的类.一些标准函数往往是从数据机械推导而来的.kotlin中,这叫做数据类,标记为data
 *
 * 编译器自动从主构造函数中声明的所有属性导出以下成员:
 * equals()/hashCode()对
 * toString() 格式是'User(name = aa,age = 42)'
 * componentN()解构函数按声明顺序对应于所有属性
 * copy()函数
 *
 * 上述的函数如果类中有显示定义或有从基类继承,则不会生成该函数
 *
 *
 * 为了确保生成代码的一致性和行为有意义,数据类必须满足如下条件:
 * 主构造函数至少有一个参数
 * 主构造函数的所有参数必需声明为val或var
 * 数据类不能使抽象、开放、密封或者内部的
 * 1.1版本之后,kotlin不仅可以实现接口,也可以扩展其他类
 *
 * 如果数据类需要生成一个无参的默认构造函数,需要保证主构造函数的所有参数都有默认值
 */
data class Paracel(val name: String, val age: Int)

val p1 = Paracel("aa", 12)
val p2 = p1.copy(age = 8) //在很多情况下,我们需要复制⼀个对象改变它的⼀些属性,但其余部分保持不变。copy()函数就是为此⽽⽣成

//'out T' 类似于Java '? extends T' -> 协变类型
//'in T' 类似于Java '? super T' -> 逆变类型
//'out T(类型为T或者T的子类)'标注确保它仅从Source<T>成员中返回(作为返回值类型),不能把它当做函数参数使用;
// in与out相反,'in T(类型为T或者T的父类)'表明类型T只能作为函数参数使用,而不能在成员中返回
abstract class source<out T> {
    abstract fun next(): T
}

//冒号之后指定的类型是上界:只有Comparable<T>的子类型可以代替T;如果没有这样显示声明类型上界,则默认是Any.
fun <T : Comparable<T>> sort(list: List<T>) {

}

//如果需要指定多个上界,则需要一个单独的where语句
fun <T> cloneWhenGrater(list: List<T>, threshold: T): List<T> where T : Comparable<T>, T : Cloneable {

    return list

}

/**
 * 星投影
 *
 *有时你想说,你对类型参数一无所知,但仍然希望以安全的方式使用它。
 *这里的安全方式是定义泛型类型的这种投影,该泛型类型的每个具体实例化将是该投影的子类型。
 *
 *Kotlin 为此提供了所谓的星投影语法:
 *
 *对于 Foo <out T>,其中 T 是一个具有上界 TUpper 的协变类型参数,Foo <*> 等价于 Foo <out TUpper>。 这意味着当 T 未知时,你可以安全地从 Foo <*> 读取 TUpper 的值。
 *对于 Foo <in T>,其中 T 是一个逆变类型参数,Foo <*> 等价于 Foo <in Nothing>。 这意味着当 T 未知时,没有什么可以以安全的方式写入 Foo <*>。
 *对于 Foo <T>,其中 T 是一个具有上界 TUpper 的不型变类型参数,Foo<*> 对于读取值时等价于 Foo<out TUpper> 而对于写值时等价于 Foo<in Nothing>。
 *
 *如果泛型类型具有多个类型参数,则每个类型参数都可以单独投影。
 *例如,如果类型被声明为 interface Function <in T, out U>,我们可以想象以下星投影:
 *
 *Function<*, String> 表示 Function<in Nothing, String>;
 *Function<Int, *> 表示 Function<Int, out Any?>;
 *Function<*, *> 表示 Function<in Nothing, out Any?>。
 *
 *注意:星投影非常像 Java 的原始类型,但是安全。
 */


/**
 * 嵌套类/内部类/匿名内部类
 */

interface Anonymous {
    fun foo()
    fun goo()
}

open class Tt constructor(val age: Int) {
    open val y: Int = 15
    open fun f() {
        println("f() int Tt")
    }
}

interface Ts {
    fun h()
}

class Outer {

    val bar: Int = 2

    class Nested { //嵌套类,不能访问外部类成员
        fun foo() = 2
    }

    inner class Inner { //内部类,inner标注,可以访问外部类成员;内部类会带有一个对外部类对象的引用
        fun foo() = 3
        fun goo() = this@Outer.bar
    }
}

class Listener {
    fun addListener(obj: Anonymous) {
        obj.foo()
        obj.goo()
    }

    fun addListener(obj: SingleFun) {
        obj.hoo()
    }
}

/**
 * 匿名对象可以用作只在本地或私有作用域中声明的类型。如果你使用匿名对象作为公有函数的返回值类型或用作公有属性的类型,
 * 那么该函数或属性的实际类型将会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是Any;在匿名对象中添加的成员将无法访问。
 */
class Cc {

    //私有函数,所以其返回类型是匿名对象类型
    private fun foo() = object {
        val x: String = "x"
    }

    //公有函数,所以其返回类型是Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x         //没问题
        //val x2 = publicFoo().x  //错误:未能解析的引用“x”
    }
}

/**
 * 就像Java匿名内部类一样,对象表达式中的代码可以访问来自包含它的作用域的变量。(与Java不同的是,这不仅限于final变量。)
 */
fun countClicks(window: JComponent) {

    var clickCount = 0
    var enterCount = 0

    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++
        }

        override fun mouseEntered(e: MouseEvent) {
            enterCount++
        }
    })
//	……
}

/**
 * 单例模式是一种非常有用的模式,而Kotlin(继Scala之后)使单例声明变得很容易
 * 这称为对象声明。并且它总是在object关键字后跟一个名称。就像变量声明一样,对象声
 * 明不是一个表达式,不能用在赋值语句的右边。
 */
class DataProvider

object DataProviderManager {

    val list: Collection<DataProvider>? = null

    fun registerDataProvider(provider: DataProvider) {
        //	……
    }

    val allDataProviders: Collection<DataProvider>?
        get() = list//	……
}


/**
 * 要引用该对象,我们直接使用其名称即可:
 * DataProviderManager.registerDataProvider(……)
 * 这些对象可以有超类型
 */
object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        //	……
    }

    override fun mouseEntered(e: MouseEvent) {
        //	……
    }
}

/**
 * 注意:对象声明不能在局部作用域(即直接嵌套在函数内部),但是它们可以嵌套到其他对
 * 象声明或非内部类中。
 *
 * 对象表达式和对象声明有一个重要的语义差异:
 *
 * 1、对象表达式是在使用它们的地方立即执行(及初始化)的
 *
 * 2、对象声明是在第一次被访问到时延迟初始化的
 *
 * 3、伴生对象的初始化是在相应的类被加载(解析)时,与java静态初始化器的语义相匹配
 *
 */


fun testAnon() {

    //嵌套类
    val netsted = Outer.Nested()
    println("netsted.foo(): " + netsted.foo())

    //内部类
    val iner = Outer().Inner()
    println("inner.foo(): " + iner.foo() + " goo(): " + iner.goo())

    //单基类实现的匿名内部类
    val listener = Listener()
    listener.addListener(object : Anonymous { //必须是'object',应该是关键字
        override fun foo() {
            println("匿名内部类实例 foo()")
        }

        override fun goo() {
            println("匿名内部类实例 goo()")
        }
    })

    //如果对象时函数式Java接口,即只包含一个函数的Java接口;可以使用带接口类型前缀的lambda表达式创建它
    val anonymousSingleFun = SingleFun { println("hoo() in 函数式Java接口(只拥有单个方法的java接口)") }
    listener.addListener(anonymousSingleFun)

    //多基类实现的匿名内部类实例 -> 创建一个继承自某个/某些类型的匿名类的对象
    val useBin = object : Tt(12), Ts { //对象表达式

        override val y: Int = 5

        //多个超类由跟在':'后面的','隔开的列表指定;如果超类有构造函数,则必须传递一个合适的参数
        override fun h() {
            println("h() in 匿名类实例")
        }

        override fun f() {
            println("f() in 匿名类实例")
        }
    }

    useBin.f()
    println("useBin y: ${useBin.y}")
    useBin.h()

    //任何时候,如果我们只是需要一个“对象而已”,并不需要特殊超类型,可以这样写
    val objNormal = object { //对象表达式
        val x = 2
        val y = 3
        fun g() {
            println("对象声明之方法调用")
        }
    }

    println("objNormal x: ${objNormal.x}, y:${objNormal.y}")
    objNormal.g()

    for (protocol in ProtocolState.values())
        println("ProtocolState: $protocol, protocol: ${protocol.protocol}, name:${protocol.name}, ordinal:${protocol.ordinal}")

    printAllValues<ProtocolState>()
    println("value by Name: " + getEnumObjByName<ProtocolState>("UDP"))

    testAll()
}

/**
 * 每个枚举常量都是一个对象;枚举常量之间用','隔开
 *
 * 每个枚举常量都具有在枚举类声明中获取其名称和位置的属性:
 * val	name:	String
 * val	ordinal:	Int
 * 枚举常量还实现了Comparable接口,其中自然顺序是它们在枚举类中定义的顺序
 */
enum class Direction {
    SOUTH, WEST, EAST, NORTH
}

/**
 * 因为每个枚举常量都是枚举类的实例,所以它们可以进行初始化
 */
enum class Color constructor(val rgb: Int = 0x0001) { //枚举类型构造函数默认是private,也只能是private
    RED(0x0003),
    WHITE(0x0006),
    BLACK(0x0009)
}

/**
 * 枚举常量也可以声明自己的匿名类及相应的方法、以及覆盖基类的方法。注意,如果枚举类定义任何成员,要使用分号将成员与定义中的枚举常量定义分隔开,就像Java中一样
 *
 * EnumClass.valueOf(value:	String): EnumClass 根据名称获取枚举常量
 * EnumClass.values(): Array<EnumClass>       获取枚举常量的集合
 */
enum class ProtocolState(val protocol: String = "TFTP") {
    UDP("udp") {
        override fun protocol(): ProtocolState {
            return UDP
        }
    },

    TCP("tcp") {
        override fun protocol(): ProtocolState {
            return TCP
        }
    },

    HTTP("http") {
        override fun protocol(): ProtocolState {
            return HTTP
        }
    };

    abstract fun protocol(): ProtocolState
}

/**
 * 自Kotlin	1.1	起,可以使用enumValues<T>()和enumValueOf<T>()库函数以泛型的方式访问枚举类中的常量
 *
 * reified类型
 */
inline fun <reified T : Enum<T>> printAllValues() {
    println("printAllValues: " + enumValues<T>().joinToString { it.name })
}

inline fun <reified T : Enum<T>> getEnumObjByName(name: String): T {
    return enumValueOf(name)
}


/**
 * 类委托,Kotlin中的委托相当于Java中的Proxy设计模式;Kotlin中以关键字by实现
 */

interface ISubjetc { //抽象主题接口
    fun g()
}

class SubjetcImpl : ISubjetc { //实际主题实现类,委托类
    override fun g() {
        println("g() in SubjetcImpl")
    }
}

/**
 * Derived相当于ProxySubject,代理类
 *
 * by 子句表示sub将在Derived中内部存储,并且编译器会生成转发给sub中的所有实现自ISubject接口的覆写方法
 * (从Java角度来看,就是它内部会覆写ISubject接口的所有的方法,内部会通过保存的sub对象转而调用到SubjectImpl中的真实实现中)
 *
 *特殊地,覆盖会以我们期望的方式进行;如果在Derived中覆盖了抽象主题类的接口,则编译器会使用Derived的实现代替委托对象中的实现
 *
 * 在这里,如果Derived中覆写了g(),则会输出"g() int Derived",而不是"g() in SubjectImpl"
 *
 */
class Derived(sub: ISubjetc) : ISubjetc by sub { //主题实现代理类
    //override fun g() {
    //   println("g() int Derived")
    //}
}

/**
 * 属性委托,就是对其属性值的操作不再依赖于其自身的getter()/setter()方法,而是将其托付给一个代理类,从而每个使用类中的该属性
 * 可以通过代理类统一管理,再也不用在每个类中,对其声明重复的getter()/setter()操作方法。
 *
 * 当我们使用属性的get或者set的时候,属性委托的getValue和setValue就会被调用。
 */
class Example {
    var y: String by Delegate()
}

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank	you	for	delegating '${property.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value	has	been assigned to '${property.name} in $thisRef.'")
    }
}

fun testAll() {

    val sub = SubjetcImpl()
    Derived(sub).g()

    var example = Example()

    println(example.y)

    example.y = "Aaa"
}
package com.xzm.test;

/**
 * Created by Fassbender on 2017/6/30.
 */
public interface SingleFun {
    void hoo();
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值