美文网首页程序猿
Kotlin学习记录(待续)

Kotlin学习记录(待续)

作者: 听梦者 | 来源:发表于2017-05-18 21:05 被阅读377次

随着2017年的Google I/O 大会,Kotlin 成为 Android 官方编程语言,对于一个Android开发者来说,我滴天,学不完了...
今天抽点时间去看了下Kotlin基本语法,第一印象就是跟Swift太像了。了解过Swift的同学,学习Kotlin应该很快上手。趁现在还有深的印象,赶紧记录一下。此记录参考了大量的博客,便于自己以后查找。。。

Kotlin 学习记录

函数定义

声明一个函数需要加上 fun关键字,如果是重写父类的函数,还必须在前面加上override关键字。基本写法

fun 函数名(参数名: 参数类型,参数名: 参数类型): 返回值类型{}

例如:

fun sum(a: Int , b: Int) : Int{
    return a + b
}

上面的代码还可以简化:

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

又由于kotlin支持自动推测返回类型,又可简化为:

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

当没有返回值时可以使用Unit 标识相当于java中的void,如果是返回Unit类型,则可以省略

默认参数

函数参数可以设置默认值,当参数被忽略时会使用默认值。这样相比其他语言可以减少重载。

fun reformat(str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = false) {}

reformat("123", true, false)

//命名参数
reformat(str = "123", normalizeCase = true, upperCaseFirstLetter = false)
reformat(str = "123", upperCaseFirstLetter = false)

注意,命名参数语法不能够被用于调用Java函数中,因为Java的字节码不能确保方法参数命名的不变性

属性和字段

属性声明

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() //在 kotlin 中没有 new 关键字
    result.name = address.name //accessors are called
    result.street = address.street
}

Getter 和 Setter

声明一个属性的完整语法如下:

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

语法中的初始化语句,getter 和 setter 都是可选的。如果属性类型可以从初始化语句或者类的成员函数中推断出来,那么他的类型也是忽略的。

例如:

var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter

只读属性的声明语法和可变属性的声明语法相比有两点不同: 它以 val 而不是 var 开头,不允许 setter 函数:

例如:

val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter

自定义的setter, getter :

例如:

var stringRepresentation: String
    get() = this.toString()
    set (value) { //value 是瞎写的,可以任选一个自己喜欢的名称
        setDataFormString(value) // 格式化字符串,并且将值重新赋值给其他元素
}

备用字段

var counter = 0
    set(value) {
        if (value >= 0) {
            field = value
            println("set")
        }
    }
    //get() = field 

延迟初始化属性

通常,那些被定义为拥有非空类型的属性,都需要在构造器中初始化,kotlin提供了一种可以延迟初始化的方案,使用lateinit关键字描述属性

修饰符只能够被用在类的 var 类型的可变属性定义中,不能用在构造方法中.并且属性不能有自定义的 getter 和 setter访问器.这个属性的类型必须是非空的,同样也不能为一个基本类型.

class MyTest {
    lateinit var subject: TestSubject

    fun test() {
        //如果不初始化 会报 未初始化错误 kotlin.UninitializedPropertyAccessException: lateinit property subject has not been initialized
       subject = TestSubject()
        subject.method()
    }
}

class TestSubject() {
    fun method() {
        println("TestSubject ---- method()")
    }
}

Kotlin里所有类都继承于父类Any,相当于Object

class Example

接口

接口使用 interface 关键字声明,可以包含抽象方法和方法的实现,这点类似于 Java 8的 Default interface

interface MyInterface {
    fun name()
    fun Study() {}
}

接口继承

class Child : MyInterface{
    override fun name() {}
}

抽象类

使用abstract关键字来描述一个抽象类,抽象类里的方法如果不提供实现则需要用abstract关键字来描述抽象方法.抽象的方法默认是open的

abstract class Myabstract {
    abstract fun age(): Int
}
class Child : Myabstract(), MyInterface {

    override fun age(): Int {
        return 23
    }

    override fun name() {}

}

构造函数

当Kotlin中的类需要构造函数时,可以有一个主构造函数和多个次构造函数可以没有次构造函数。主构造函数在类名后。

//常规用法
class Person{}
class Person1(name: String) {}

当主构造函数有注解或者可见性修饰符,需加 constructor 关键字。

class Person @inject  constructor() {}

主构造函数

主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块中:

当不在主函数中声明时,只能在初始化块以及属性声明中使用

class Person(var name: String) {

    //类似于java 的静态代码块
    init {
        var leng = name.length
    }

    //属性声明
    var lang = name.length

    //当主构函数声明后,可以当全局变量使用 ,当不在主函数中声明时,只能在初始化块以及属性声明中使用
    fun test() {
        var lang = name.length
    }
}

注:
1、函数的声明可以是val也可以是var
2、当不在主构造函数中声明又想当全局变量使用,可在类中声明,主函数中声明是简化了其写法。

class Person(name: String) {
    var name = name
    ...
}

次构造函数

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

class Person {
  
    //没有主构造函数,使用委托this()错误
    constructor(name: String) : this() {}

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

    constructor(name: String, age: Int) : this(name) {}
}

类的继承

Kotlin 里类默认都是final的,如果声明的类需要被继承则需要使用open 关键字来描述类

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

如果类没有主构造函数

open class Base {
    constructor(name: String)
}

class Derived : Base {
    //次构造函数
    constructor(name: String) : super(name)

}

如果父类只存在默认构造方法

open class Base {
}
//形式一
class Derived : Base() {}

//形式二
class Derived1 : Base {
    constructor() : super()
}

重写方法

在子类里重写父类的方法需要使用override关键字描述方法

open class A{
    open fun me(){}
    fun me1(){}
}

class B:A(){
    //override fun me1(){} Error: 'me' in 'A' is final and cannot be overridden
    override fun me(){}
}

如果一个类从它的直接父类继承了同一个成员的多个实现,那么它必须重写这个成员并且提供自己的实现

open class A {
    open fun f () { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } //接口的成员变量默认是 open 的
    fun b() { print("b") }
}

class C() : A() , B{
    override fun f() {
        super<A>.f()//调用 A.f()
        super<B>.f()//调用 B.f()
    }
}

如果类C中没有定义f() 编译器会报如下错误:
Error:(29, 0) Class 'C' must override public open fun f(): kotlin.Unit defined in A because it inherits many implementations of it


2017-5-19 9:22

空安全

Kotlin 类型系统致力于消灭空引用,swift也有(可选类型)。

在 Kotlin 类型系统中可以为空和不可为空的引用是不同的。比如,普通的 String 类型的变量不能为空

var a: String ="abc"
a = null //编译错误

允许为空,我们必须把它声明为可空的变量:

var b: String? = "abc"
b = null

而现在你可以完全放心的调用a的方法,再也不用担心会出现空指针异常。但是如果你想同样方式调用b,可能就会报错了

val leng = a.length()
val leng1 = b.length()//可能报错

在条件中检查 null

首先,你可以检查 b 是否为空,并且分开处理下面选项:

val l = if (b != null) b.length() else -1

注意只有在 b 是不可变时才可以

安全调用

选择使用安全操作符,?

b?.length()

如果b不为空则调用后面的length()方法,否者直接返回空,这个表达式的的类型是 Int?

Elvis 操作符

当我们有一个 r 的可空引用时,我们可以说如果 r 不空则使用它,否则使用使用非空的 x :

上面的if表示可以满足我们,但是kotlin提供了一种更加简洁的写法 Elvis 操作符,?:

val l = b.length()?: -1

如果b.length()不为空则返回,否则返回右边的表达式,注意右边的表带式只有在左边表达式为空是才会执行

!! 操作符

第三个选择是 NPE-lovers。我们可以用 b!! ,这会返回一个非空的 b 或者抛出一个 b 为空的 NPE

val l = b !!.length()

安全转换符

普通的转换可能产生 ClassCastException 异常。另一个选择就是使用安全转换,如果不成功就返回空:

val aInt: Int? = a as? Int

不管 as? 右边的是不是一个非空Int 结果都会转换为可空的。Int?

不安全的转换符

如果转换是不被允许的那么转换符就会抛出一个异常。因此我们称之为不安全的。在kotlin 中 我们用前缀 as 操作符

val x: String = y as String

注意 null 不能被转换为 String 因为它不是 nullable,也就是说如果 y 是空的,则上面的代码会抛出空异常。

为了 java 的转换语句匹配我们得像下面这样:

val x: String?= y as String?

类型检查和转换

is !is 表达式

我们可以在运行是通过上面俩个操作符检查一个对象是否是某个特定类:

if (obj is String) {
    print(obj.length)
}

if (obj !is String) { // same as !(obj is String)
    print("Not a String")
} else {
    print(obj.length)
}

智能转换

在很多情形中,需要使用非明确的类型,因为编译器会跟踪 is 检查静态变量,并在需要的时候自动插入安全转换:

fun demo(x: Any) {
    if (x is String) {
        print(x.length) // x is automatically cast to String
    }
}

编译器足够智能如何转换是安全的,如果不安全将会返回:

if (x !is String) return
print(x.length) //x 自动转换为 String

或者在 || && 操作符的右边的值

  // x is automatically cast to string on the right-hand side of `||`
  if (x !is String || x.length == 0) return

  // x is automatically cast to string on the right-hand side of `&&`
  if (x is String && x.length > 0)
      print(x.length) // x is automatically cast to String 

循环

for循环

fun main(args: Array<String>) {
  val items = listOf("apple", "banana", "kiwi")
  for (item in items) {
    println(item)
  }
}

或者

fun main(args: Array<String>) {
  val items = listOf("apple", "banana", "kiwi")
  for (index in items.indices) {
    println("item at $index is ${items[index]}")
  }
}

如果我们想要把index和value同时取出来,我们还可以使用 withIndex函数

val items = listOf("apple", "banana", "kiwi")
for ((index, value) in items.withIndex()) {
    println("the element at $index is $value")
}

使用 while 循环·

whiledo..while循环跟java的没有啥区别

while (x > 0) {
    x--
}

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

when 表达式

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

一个条件里面还可以用逗号 组合

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

我们还可以使用任意表达式(不仅仅是常量)作为分支条件

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

甚至还可以有返回值

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

//二
fun describe(obj: Any): String =
                        when (obj) {
                          1          -> "One"
                          "Hello"    -> "Greeting"
                          is Long    -> "Long"
                          !is String -> "Not a string"
                          else       -> "Unknown"
                        }

println(describe("Hello"))

也可以用来代替if-else

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

使用ranges

范围表达式

在Kotlin中,范围创建只需要..操作符,例如:

val r1 = 1..5
//该范围包含数值1,2,3,4,5

如果创建一个降序范围,则需要使用downTo函数 ,downTo 后面的 数值 表示 在哪个值截止

例如:5 downTo 2 ,表示 2..5 降序

val r2 = 5 downTo 1
//该范围包含数值5,4,3,2,1

val r2 = 5 downTo 2
//该范围包含数值5,4,3,2

默认范围的步长是1,可使用step方法修改步长:

val r3 = 9 downTo 2 step 2
//该范围包含数值9,7,5,3

检查 in 操作符检查数值是否在某个范围内:

fun main(args: Array<String>) {
  val x = 10
  val y = 9
  if (x in 1..y+1) {
      println("fits in range")
  }
}

范围外:

if (x !in 0..array.lastIndex)
    print("Out")

相关文章

网友评论

    本文标题:Kotlin学习记录(待续)

    本文链接:https://www.haomeiwen.com/subject/oohexxtx.html