Kotlin学习总结文档

第一章 数据类型

1.基础数据类型

Kotlin的基本数值类型包括Byte、Short、Int、Long、Float、Double等。不同于Java的是,字符不属于数值类型。

Alt text

每个数据类型都有如下转换函数

toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

字面值常量
主要是以下几种字面值常量:

--十进制数值: 123
--长整型要加大写 L : 123L
--16进制:0x0f
--二进制:0b00001011

注意不支持8进制
Kotlin 也支持传统的浮点数表示:

-- 默认双精度浮点数(Double) : 123.5 , 123.5e10
-- 单精度浮点数(Float)要添加 f 或 F :123.5f

数值常量中可以添加下划线分割(1.1版本新特性)
您可以使用下划线增加数值常量的可读性:

--  val oneMillion = 1_000_000
--  val creditCardNumber = 1234_5678_9012_3456L
--  val socialSecurityNumber = 999_99_9999L
--  val hexBytes = 0xFF_EC_DE_5E
--  val bytes = 0b11010010_01101001_10010100_10010010

值相等与物理值相比较

val a: Int = 10000
print (a === a ) // 打印 'true' ---注意区别是在这个三元等号
val boxedA: Int? =a
val anotherBoxedA: Int? = a
print (boxedA === anotherBoxedA ) // 注意这里打印的是 'false'

另一方面,它们是值相等的:

val a: Int = 10000
print(a == a) // 打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 打印 'true'

2.变量和常量

1).变量


   //定义一个字符变量,自动推导为字符串
   var name ="张三丰"   //也可以这样写  var name:String = "张三丰"
   //或者
   var addr:String="深圳"
   //或者
   var addr:String?="深圳"

2).常量

  //定义一个常量
   val company ="小牛在线"  
   //定义一个可以为空的量
   val friend:String="张三"

3.字符串

1).字会串拼接

  var name = "XiaoMin"
  var age = 23
  var xiaomin = "小明的名字是$name,年纪是$age"
  println(xiaomin)

2).复杂字符拼接

//定义一个类
class Animal{
    var name:String="Animal"
    var age:Int = 10
}
//主函数
fun main(args: Array<String>) {
    var animal = Animal()
    println("animal name is ${animal.name}") //拼接字符
}

3).多行字符串

val text = """
    Tell me and I forget
    Teach me and I remember
    Involve me and I learn
    (Benjamin Franklin)
"""

//如果要去掉空字符,可以这样. 类似java的trim()
var text ="""  Tell me and I forget """.trim-margin()

4.Any数据类型

Any是Kotlin所有数据的基类,类似于Java的Object. 但是它没有hashCode,toString等方法, 这是和java的区别.


var pig:Any = Animal("pig")
var dog:Any = Animal("dog")

class Animal(name:String) {}

5.集合数据类型

1). List集合的用法

     var list = listOf<Pig>(
            Pig("大猪", 10, "F"),
            Pig("黑猪", 20, "M"),
            Pig("白猪", 30, "F"),
            Pig("小猪", 40, "M")
    )

    var list2 = listOf<Int>(
            100, 1, 2, 3, 4, 5
    )

    //直接设用java的接口对集合排序
    Collections.sort(list2)

    //调用Kotlin接口排序
    list.sortedBy({
        it.age
    })

    //输出集合中的数据
    for ((i, item) in list2.withIndex()) {
        println("$i is $item")
    }

    for ((i, item) in list.withIndex()) {
        println("${item.name} is ${item.age}")
    }

2). Set集合的用法

    //显示定义一个无序集合
    var addr:Set<String> = setOf("深圳","上海")
    //输出集合数据内容
    for(item in addr.iterator()){
        println(item)
    }

    //定义有序的Set
    var friends:SortedSet<Int> = sortedSetOf(10,5,30,20,40)
    //输出集合数据内容
    for(item in friends.iterator()){
        println(item)
    }

3).Map集合的用法

    //显示定义一个集合
    var addr:Map<Int,String> = mapOf<Int,String>(1 to "深圳", 2 to "上海")
    //或者
    var friends = mapOf<Int,String>(1 to "小平",2 to "毛泽东")

    //输出集合内容
    for( (K,V) in addr.entries){
        println("集合数据Key=$K,Value=$V")
    }

    for( K in addr.keys ){
        println("集合数据Key=$K,Value=${addr.get(K)}")
    }

    //定义有序的map
    var sortMap:SortedMap<Int,String> = sortedMapOf(2 to "深圳", 3 to "上海",1 to "南京")
    for( K in sortMap.keys ){
        println("有序Map Key=$K,Value=${addr.get(K)}")
    }

6.数组

数组的用法简单好用,如下例子

fun main(args:Array<String>){

    var intArray = intArrayOf(1,2,3,4,5)

    var longArray = longArrayOf(1L,2L,3L,4L,5L)

    var floatArray = floatArrayOf(10f,20f,30f,40f,50f)

    var doubleArray = doubleArrayOf(10.1,10.2,20.3,30.3,40.4e10)

    var byteArray = byteArrayOf(1,2,3,4)

    var shortArray = shortArrayOf(1,23,3,4)

    var animals:Array<Animal> = arrayOf(Animal("狗"), Animal("猪"))
}

class Animal(name:String){
    var name:String
    init {
        this.name = name
    }
}

第二章 操作符

1.判空操作符

1.判空操作符 ?

   var name:String?="小平同志" //表示该变量可以为空

2.如果为空 ?:

   var name:String?="小平同志" //表示该变量可以为空
   var rename = name?:"习近平同志" //如果name不为null就把值给rename,否则rename就重新赋值

3.如果不为空才做一件事 ?.

    var addr:String?="深圳"
    //addr不为空才执行let中的任务
    addr?.let {
        println("to do something")
    }

4.函数返值可以为空

fun request():Response?{
    return null
}
class Response

2.等号操作符

相等,在 kotlin 中有两种相等:

1).===表示: 参照相等(指向相同的对象):内存单元相同
2).== 表示: 结构相等

1).参照相等

参照相等是通过 === 操作符判断的(不等是!== ) a===b 只有 a b 指向同一个对象是判别才成立。

另外,你可以使用内联函数 identityEquals() 判断参照相等:

a.identityEquals(b)
a identityEquals b

2).结构相等

结构相等是通过 == 判断的。像 a == b 将会翻译成:

a?.equals(b) ?: b === null

如果 a 不是 null 则调用 equals(Any?) 函数,否则检查 b 是否参照等于 null

注意完全没有必要为优化你的代码而将 a == null 写成 a === null 编译器会自动帮你做的。

注意: 上面两种等号,字面意思有所不同,但实际效果没有区别.

    var x1 ="a"
    var x2 ="a"
    println(x1 == x2)
    println(x1 === x2)
    println(x1.equals(x2))
    //结果都是输出 true

3. in 和downTo

for (i in 20 downTo 15) 
{
     println(i)
}

for (i in 1..10) {
     println(i)
}

第三章 控制流

1.IF表达式

在Kotlin中, 没有三元表达式,传统用法:

var max = a 
if (a < b) 
  max = b 

// With else 
var max: Int
if (a > b) 
  max = a 
else 
  max = b 

IF 作为表达式的用法:

var a:Int = 1
var b:Int = 2
val max:Int = if (a > b) a else b
val min = if (a > b) a else b

IF作为返回值的用法:

 fun asRetunExpression1(a:Int,b:Int):Int =  if (a > b) a else b

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

2.When表达式

替代Switch的用法

var x = 1
when (x) {
  1 -> print("x == 1")
  2 -> print("x == 2")
  else -> { // Note the block
    print("x is neither 1 nor 2")
  }
}

常规的When用法

var x ="2"
var i = 2
when (i) {
   parseInt(x) -> print("s encodes x")
   else -> print("s does not encode x")
}

我们可以在判断分支条件的地方使用任何表达式,而不仅仅是常量(和switch不同):

var x = 3
var validNumbers = 100..200
when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

in , !in在When中的使用

when (x) {
  in 1..10 -> print("x is in the range")
  in validNumbers -> print("x is valid")
  !in 10..20 -> print("x is outside the range")
  else -> print("none of the above")
}

有参数When,可以用 is来检测类型

val hasPrefix = when(x) {
  is String -> x.startsWith("prefix")
  else -> false
}

无参数When的使用

when {
  x.isOdd() -> print("x is odd")
  x.isEven() -> print("x is even")
  else -> print("x is funny")
}

查看 grammar for when{: .keyword }.

3.For循环

For循环主要有以下用法

//输出一个范围内的值
fun forExample1() {

    for (i in 1..10) {
        println(i)
    }

}
//从20输出到位15
fun forExample2() {
    for (i in 20 downTo 15) {
        println(i)
    }
}

//输出集全元素
fun forExample3() {
    val arrayInts = arrayListOf("one", "two", "three")
    for ((i, item) in arrayInts.withIndex()) {
        println("index = $i, item value is = $item")
    }
}
//in关键字在表达式中的用法
fun forExample4() {
    var char = 'A'
    var result = char in 'A'..'Z' || char in 'a'..'z'
    println(result)
}

4.While循环

while{: .keyword } 和 do{: .keyword }..while{: .keyword } 的使用方法和其他语言一致

while (x > 0) {
  x--
}
do {
  val y = retrieveData()
} while (y != null) // y is visible here!

第四章 类和继承

1.类

类声明Kotlin使用关键字class

class Invoice {}
或者
clas Invoice

这个类声明被花括号包围,包括类名、类头(指定其类型参数,主构造函数等)和这个类的主干。类头和主干都是可选的; 如果这个类没有主干,花括号可以被省略。

class Empty

2.构造

在Kotlin中的类可以有主构造函数和一个或多个二级构造函数。主构造函数是类头的一部分:它跟在这个类名后面(和可选的类型参数)

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

3.扩展构造函数

类也可以拥有被称为”二级构造函数”(为了实现Kotlin向Java一样拥有多个构造函数),通常被加上前缀”constructor”

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

如果类有一个主构造函数,每个二级构造函数需要委托给主构造函数,直接或间接地通过另一个二级函数。 委托到另一个使用同一个类的构造函数用this关键字

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

如果一个非抽象类没有声明任何构造函数(原发性或继发性),这将有一个生成的主构造函数不带参数。构造函数的可见性是public。

如果你不希望你的类有一个公共构造函数,你需要声明与非缺省可见一个空的主构造函数:

class DontCreateMe private constructor () {
}

注意在JVM上,如果所有的主构造函数的参数有默认值,编译器会产生一个额外的参数的构造函数,将使用默认值。

class Customer(val customerName: String = "")

4.创建类的实例

要创建一个类的实例,我们调用构造函数,就好像它是普通的函数:

val invoice = Invoice()

val customer = Customer("Joe Smith")

注意Kotlin不能有“new”关键字

5.类成员

类可以包括

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

在Kotlin所有的类中都有一个共同的父类Any,这是一个默认的父类且没有父类型声明:

class Example // Implicitly inherits from Any

Any不属于java.lang.Object;特别是,它并没有任何其他任何成员,甚至连equals(),hashCode()和toString()都没有。

要声明一个明确的父类,我们把类型放到类头冒号之后:

open class Base(p: Int)

class Derived(p: Int) : Base(p)

如上所见,父类可以(并且必须)在声明继承的地方,用原始构造函数初始化。

如果类没有主构造,那么每个次级构造函数初始化基本类型 使用super关键字,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的二级构造函数可以调用基类型的不同的构造:

class MyView : View {
    constructor(ctx: Context) : super(ctx) {
    }

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
    }
}

父类上的open标注可以理解为Java中final的反面,它允许其他他类 从这个类中继承。
默认情况下,在Kotlin所有的类都是final,.

对应于 Effective Java 书中的17条:设计并显示标注继承,否则就禁止它。

伴随对象实现单例,静态变量的效果

class Student(name: String) {

    var name: String? = null
    init {
        this.name = name
    }

    companion object {
         @Volatile private var student: Student?= null
        fun getSingleton():Student? {
            if (this.student == null) {
                synchronized(this) {
                    this.student = Student("文忠湖")
                }
            }
            return this.student
        }
    }

    fun walk(){
        println("walk")
    }
}

fun main(args:Array<String>){
    var student:Student? = Student.getSingleton()
    student?.walk()
}

6.覆盖成员

我们之前提到过,Kotlin力求清晰显式。不像Java中,Kotlin需要明确的 标注覆盖的成员(我们称之为open)和重写的函数。(继承父类并覆盖父类函数时,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 的本身是开放的,也就是说,它可以在子类中重写。如果你想禁止重写的,使用final 关键字:

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

在Kotlin中,实现继承的调用通过以下规则:
如果一个类继承父类成员的多种实现方法,可以直接在子类中引用, 它必须重写这个成员,并提供其自己的实现(当然也可以使用父类的)。

为了表示从中继承的实现而采取的父类型,我们使用super 在尖括号,如规范的父名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<A>.f() // call to A.f()
    super<B>.f() // call to B.f()
  }
}

类C同时继承A和B是可以的,而且我们在调用a()和b()函数时没有任何问题,因为他们在C的基类中只有一个实现。 但是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()
}

需要注意的是,我们并不需要标注一个抽象类或者函数为open

我们可以重写一个open非抽象成员使之为抽象的。

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

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

第五章 属性和字段

1.声明属性

Kotlin的属性. 这些声明是可变的,用关键字var 或者使用只读关键字val .

public class Address { 
  public var name: String = ...
  public var street: String = ...
  public var city: String = ...
  public var state: String? = ...
  public var zip: String = ...
}

要使用一个属性,只需要使用名称引用即可,就相当于Java中的公共字段:

fun copyAddress(address: Address): Address {
  val result = Address() // there's no 'new' keyword in Kotlin
  result.name = address.name // accessors are called
  result.street = address.street
  // ...
  return result
}

2.延迟属性

Kotlin声明一个属性一定要初始化, 当你不想初始化的时候就可以用lazy关键字来处理了

val lazyValue: String by lazy {
    println("computed!")  //第一次初始化,会输出这一行内容
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)
    println(lazyValue)
}

3.getters和setters

声明一个属性的完整语法

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

上面的定义中,初始器(initializer)、getter和setter都是可选的。 属性类型(PropertyType)如果可以从初始器或者父类中推导出来,也可以省略。

例如:

var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter

注意公有的API(即public和protected)的属性,类型是不做推导的。 这么设计是为了防止改变初始化器时不小心改变了公有API。比如:

public val example = 1 // error: a public property must have a type specified explicitly

一个只读属性的语法和一个可变的语法有两方面的不同:
- 1·只读属性的用val开始代替var
- 2·只读属性不许setter

val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter

我们可以编写自定义的访问器,非常像普通函数,对内部属性声明。这里有一个定义的getter的例子:

val isEmpty: Boolean
  get() = this.size == 0

一个定义setter的例子:

var stringRepresentation: String
  get() = this.toString()
  set(value) {
    setDataFromString(value) // parses the string and assigns values to other properties
  }

按照惯例,setter参数的名称是“value”,但是如果你喜欢你可以选择一个不同的名称。

如果你需要改变一个访问器或注释的可见性,但是不需要改变默认的实现, 您可以定义访问器而不定义它的实例:

var setterVisibility: String = "abc" // Initializer required, not a nullable type
private set // the setter is private and has the default implementation

var setterWithAnnotation: Any?
  @Inject set // annotate the setter with Inject

第六章 接口

Kotlin 的接口很像 java 8。它们都可以包含抽象方法,以及方法的实现。和抽象类不同的是,接口不能保存状态。可以有属性但必须是抽象的。

接口是通过关键字 interface 来定义的:

interface MyInterface {
    fun bar()
    fun foo() {
    //函数体是可选的-和java8类似
    }
}

1.接口的实现

一个类或对象可以实现一个或多个接口

class Child : MyInterface {
    fun bar () {
        //函数体
    }
}
interface MyInterface {
    val property: Int //抽象属性
    fun foo() {
        print(property)
    }
}

class Child : MyInterface {
    override val property: Int = 29
}

2.抽象类

用关键字abstract 定义抽象类 ,抽象方法也需要用abstract .


interface Biology{
    fun walk()
    fun eat()
}
abstract class Animal(name: String, age: Int):Biology {

    open fun sleep(){
        println("sleep()")
    }

    abstract fun laugh()
}

3.继承

注意构造方法的传参,Kotlin也只支持单继承.

class Pig(name:String,age:Int,sex:String): Animal(name,age,sex){
    override fun laugh() {
        println(" $name 都笑了.")
    }

    override fun walk() {
        println("$sex $name  $age years old : walk. ")
    }

    override fun eat() {
        println("$sex $name $age years old : eat.")
    }

    override fun sleep() {
        println("$sex $name $age years old : sleep.")
    }
}

4.解决冲突

当我们在父类中声明了许多类型,有可能出现一个方法的多种实现。比如:

interdace A {
    fun foo() { print("A") }
    fun bar()
}

interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A {
    override fun bar() { print("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
}

A B 接口都有声明了 foo() bar() 函数。它们都实现了 foo() 方法,但只有 B 实现了 bar() ,bar() 在 A 中并没有声明它是抽象的,这是因为在接口中如果函数没有函数体,那么默认是抽像的。

现在,如果我们从 A 中派生一个 C 实体类,显然我们需要重写 bar() ,并实现它。而我们从 A 和 B 派生一个 D ,我们不用重写 bar() 方法,因为我们的一个继承中有一个已经实现了它。

但我们继承了俩个 foo() 的实现,因此编译器不知道应该选哪个,并强制我们重写 foo() 并且明确指出我们想怎么实现。

第七章 函数与属性扩展

Kotlin 与 C# Gosu 类似,提供了不用从父类继承,或者使用像装饰模式这样的设计模式来给某个类进行扩展。这是通过叫扩展的特殊声明来达到的。现在, Kotlin 支持扩展函数和属性。

1.扩展函数

声明一个扩展函数,我们需要添加一个接收者类型的的前缀。
下面是给MutableList添加一个 swap 函数的例子:

fun MutableList<Int>.swap(x: Int, y: Int) {
    val temp = this[x] // this 对应 list
    this[x] = this[y]
    this[y] = tmp
}

在扩展函数中的 this 关键字对应接收者对象。现在我们可以在任何 MutableList 中使用这个函数了:

fun <T> MutableList<T>.swap(x: Int, y: Int) {
  val tmp = this[x] // 'this' corresponds to the list
  this[x] = this[y]
  this[y] = tmp
}

2.可空的接受者

值得注意的是扩展可以定义一个可空的接受者。这样的扩展可以算作对象的变量,即使它是空的,你可以在函数体内检查 this == null 。这样你就可以在 Kotlin 中不进行空检查就可以调用 toString() 方法:这样的检查是在扩展函数中做的。

fun Any?.toString(): String {
    if (this == null) return "null"
    return toString()
}

3.扩展属性

和函数类似, Kotlin 也支持属性扩展:

val <T> List<T>.lastIndex:  Int
    get() = size-1

注意,扩展并不是真正给类添加了成员,没有比给扩展添加备用字段更有效的办法了/这就是为什么初始化函数是不允许扩展属性。它们只能通过明确提供 getter setter 来作用。

4.伴随对象扩展

如果一个对象定义了伴随对象,你也可以给伴随对象添加扩展函数或扩展属性:

class MyClass {
    companion object {} 
}
fun Myclass.Companion.foo() {

}

和普通伴随对象的成员一样,它们可以只用类的名字就调用:

MyClass.foo()

5.扩展的范围

大多数时候我们在 top level 定义扩展,就在包下面直接定义:

package foo.bar
fun Baz.goo() { ... }

在声明的包外面使用这样的扩展,我们需要在 import 时导入:

package com.example,usage
import foo.bar.goo//导入所有名字叫 "goo" 的扩展
import foo.bar.*
fun usage(baz: Baz) {
    baz.goo()
}

6.可变参数Varargs

fun request(vararg  params:String){
    for( (i,item) in params.withIndex() ){
        println("$i,$item")
    }
}
fun main(args:Array<String>){
    request("fruit","pig","plant")
}

第八章 数据类

我们经常创建一些只是处理数据的类。在这些类里的功能经常是衍生自他们所持有的数据。在Kotlin中,这样的类可以被称为data:

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

这被叫做一个 数据类。编译器自动从在主构造函数定义的全部特性中得到以下成员:

equals()/hashCode(),
toString() 格式是 "User(name=John, age=42)",
componentN() functions 对应按声明顺序出现的所有属性,
copy() 方法 .

如果有某个函数被明确地定义在类里或者被继承,编译器就不会生成这个函数。

NOTE如果一个构造参数没有val或者var在前面,它将不会被包括进这些成员里;也不会在类里声明成属性或者继承自父类

在JVM中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须有默认值。

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

1.复制

在很多情况下,我们我们需要对一些属性做修改而其他的不变。这就是copy()这个方法的来源。对于上文的User类,应该是这么实现这个方法的

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)     

也可以这么写

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

2.数据类和多重声明

成员方法用于使数据类可以多声明:

val jane = User("Jane", 35) 
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"

3.标准数据类

在标准库提供了Pair和Triple。在很多情况下,即使命名数据类是一个更好的设计选择,因为这能让代码可读性更强。

第九章 内部类

1.静态内部类

不可以访外部类的属性

class Outer {
  private val bar: Int = 1
  class Nested {
    fun foo() = 2
  }
}

val nested = Outer.Nested()
nested.foo()

2.非静态内部类

加上Inner关键字,就是非静态内部类. 内部类会带有一个来自外部类的对象的引用,且可以访问外部类的属性方法.

class Outer {
  private val bar: Int = 1
  inner class Inner {
    fun foo() = bar
  }
}

val inner= Outer.Inner()
inner.foo()

第十章 安全构建

请参考官网: 

http://kotlinlang.org/docs/reference/type-safe-builders.html#full-definition-of-the-comexamplehtml-package

第十一章 其它

1.异常

1). try作为表达式

fun tryException(){
    var result = try{
        Integer.parseInt("2"); //如果转换成功,结果会赋值给result
    }catch (a: Exception){
        null  //如果转换失败,null会赋值给result
    }finally{
        println("not as a result") // 这里不会作为结果赋值给result
    }
    println(result)  
}

2).自定义异常
类似于java一样的写法, 实际上它就是调用的java的异常基类

class ExceptionExtend(message:String):Exception(message){

}

我们可以查看Kotlin源码,他作了一个映射, Kotlin映射java里面的类

@SinceKotlin("1.1") public typealias Error = java.lang.Error
@SinceKotlin("1.1") public typealias Exception = java.lang.Exception

2.反射

直接看例子, 网上所有教程都只对 ::简单说明了一下,在这里给出例子更容易懂.

fun main(args:Array<String>){
    var tmp = Teacher()
    println(tmp::class)
    println(tmp::class.simpleName)
    println(tmp::class.qualifiedName)

    var members = tmp::class.members
    for( (i,item) in members.withIndex()){
        if(item.name == "walk")
         item.call(tmp) //类似java的invoke方法.
    }

}

class Student{
    var name:String?="student"
    var age:Int=1

    fun walk(){
        println("walk")
    }
}

3.对象解构

下面代码不能正常运行,暂时找不到足够资料解,先当作了解.

    var teacher = Teacher("习近平",50)
    var (name:String?,age:Int) = teacher

4.this表达式

这是官网给出的例子:

class A { // implicit label @A
    inner class B { // implicit label @B
        fun Int.foo() { // implicit label @foo
            val a = this@A // A's this
            val b = this@B // B's this
            val c = this // foo()'s receiver, an Int
            val c1 = this@foo // foo()'s receiver, an Int

            val funLit = lambda@ fun String.() {
                val d = this // funLit's receiver
            }
            val funLit2 = { s: String ->
                // foo()'s receiver, since enclosing lambda expression
                // doesn't have any receiver
                val d1 = this
            }
        }
    }
}

5.类的代理

使用by关键代理一个类, 它是替换继承的一个不错方案.

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}
// 一定要是接口才可以被代理, 这里的Base是接口.
class Derived(b: Base) : Base by b  

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // prints 10
}

7.属性代理

有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们, 但是如果能够为大
家把他们只实现一次并放入一个库会更好。例如包括
- 延迟属性(lazy properties): 其值只在首次访问时计算,
- 可观察属性(observable properties): 监听器会收到有关此属性变更的通知,
- 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。

为了涵盖这些(以及其他)情况,Kotlin 支持 委托属性:

class Example {
var p: String by Delegate()
}

属性代理不必实现任何的接口,但是需要提供一个 getValue() 函数(和 setValue()

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.'")
    }
}

当我们从委托到一个 Delegate 实例的 p 读取时,将调用 Delegate 中的 getValue() 函
数, 所以它第一个参数是读出 p 的对象、第二个参数保存了对 p 自身的描述.

可观察属性 Observable
Delegates.observable() 它有三个 参数:被赋值的属性、旧值和新值:

import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<default>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
//这个例子输出:
<default> -> first
first -> second

如果你想能够截获一个赋值并“否决”它,就使用 vetoable() 取代 observable() 。 在属性被
赋新值生效之前会调用传递给 vetoable 的处理程序。

import kotlin.properties.Delegates
class User {
    var name: String by Delegates.vetoable(""){
        prop, old, new ->
        if(new =="not allow value")
            false
        else 
            true
    }
}

把属性储存在映射中
一个常见的用例是在一个映射(map)里存储属性的值。 这经常出现在像解析 JSON 或者做
其他“动态”事情的应用中。 在这种情况下,你可以使用映射实例自身作为委托来实现委托属
性。

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}

在这个例子中,构造函数接受一个映射参数:

    val user = User(mapOf(
        "name" to "John Doe",
        "age" to 25
    ))

委托属性会从这个映射中取值(通过字符串键——属性的名称):

println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
//这也适用于 var 属性,如果把只读的 Map 换成 MutableMap 的话:

class MutableUser(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int by map
}

8.注解

    annotation class Document

9.别名

1).当一个类型特别长的时候,就非常有用.

typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>

2).你也可以为函数起别名

typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean

3).也可以为内部类起别名

class A {
    inner class Inner
}
class B {
    inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
发布了45 篇原创文章 · 获赞 9 · 访问量 11万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章