1.Kotlin簡介
Kotlin 是一个用于现代多平台应用的静态编程语言,Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。
Kotlin,如前面所说,它是JetBrains开发的基于JVM的语言。JetBrains因为创造了一个强大的Java开发IDE被大家所熟知。Android Studio,官方的Android IDE,就是基于Intellij,作为一个该平台的插件。
Kotlin是使用Java开发者的思维被创建的,Intellij作为它主要的开发IDE。对于Android开发者,有两个有趣的特点:
对Java开发者来说,Kotlin是非常直觉化的,并且非常容易学习。语言的大部分内容都是与我们知道的非常相似,不同的地方,它的基础概念也能迅速地掌握它。
它与我们日常生活使用的IDE无需配置就能完全整合。Android Studio能够非常完美地理解、编译运行Kotlin代码。而且对这门语言的支持来正是自于开发了这个IDE的公司本身.
1.1語言優勢
1.它更加易表现:这是它最重要的优点之一。你可以编写少得多的代码。
2.它更加安全:Kotlin是空安全的,也就是说在我们编译时期就处理了各种null的情况,避免了执行时异常。如果一个对象可以是null,则我们需要明确地指定它,然后在使用它之前检查它是否是null。你可以节约很多调试空指针异常的时间,解决掉null引发的bug。
3.它是函数式的:Kotlin是基于面向对象的语言。但是就如其他很多现代的语言那样,它使用了很多函数式编程的概念,比如,使用lambda表达式来更方便地解决问题。其中一个很棒的特性就是Collections的处理方式。
4.它可以扩展函数:这意味着我们可以扩展类的更多的特性,甚至我们没有权限去访问这个类中的代码
5.它是高度互操作性的:你可以继续使用所有的你用Java写的代码和库,因为两个语言之间的互操作性是完美的。甚至可以在一个项目中使用Kotlin和Java两种语言混合编程。
2.基礎語法
2.1定義函數
(1)带有两个 Int 参数、返回 Int 的函数:
fun sum(a: Int, b: Int): Int {
return a + b
}
(2)将表达式作为函数体、返回值类型自动推断的函数:
fun sum(a: Int, b: Int) = a + b
(3)函数返回无意义的值:
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
(4)Unit
返回类型可以省略:
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}
2.2定义变量
var表示變量
Val表示常量
(1)一次赋值(只读)的局部变量:
val a: Int = 1 // 立即赋值
val b = 2 // 自动推断出 `Int` 类型
val c: Int // 如果没有初始值类型不能省略
c = 3 // 明确赋值
(2)可变变量:
var x = 5 // 自动推断出 `Int` 类型
x += 1
2.3定義變量
(1)使用条件表达式:
fun maxOf(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}
(2)使用 if 作为表达式:
fun maxOf(a: Int, b: Int) = if (a > b) a else b
2.4使用可空值及null檢測
(1)当某个变量的值可以为 null 的时候,必须在声明处的类型后添加 ? 来标识该引用可为空。
如果 str 的内容不是数字返回 null:
fun parseInt(str: String): Int? {
Return (null)
}
2.5使用类型检测及自动类型转换
(1)is 运算符检测一个表达式是否某类型的一个实例。 如果一个不可变的局部变量或属性已经判断出 为某类型,那么检测后的分支中可以直接当作该类型使用,无需显式转换:
fun getString(obj: Any): Int? {
if (obj is String) {
// `obj` 在该条件分支内自动转换成 `String`
return obj.length
}
// 在离开类型检测分支后,`obj` 仍然是 `Any` 类型
return null
}
2.6使用 for 循环
var a:IntArray= intArrayOf(1,2,6,5,4,7)
for (item in a) {
println(item)
}
2.7使用 when 表达式
fun sum(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "two"
is Long -> "Long"
!is String -> "No string"
else -> "No obj"
}
2.8使用区间
(1)使用 in 运算符来检测某个数字是否在指定区间内:
val x = 10
val y = 9
if (x in 1..y+1) {
println("fits in range")
}
(2)区间迭代
for (x in 1..5) {
print(x)
}
2.9使用集合
(1)对集合进行迭代:
val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
println(item)
}
(2)使用 in 运算符来判断集合内是否包含某实例:
val items = setOf("apple", "banana", "kiwifruit")
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
3.習慣用法
一些在 Kotlin 中广泛使用的语法习惯
3.1創建DataClass,關鍵字data
data class user(val name: String, val email: String)
3.2list的過濾
val positives = list.filter { x -> x > 0 }
或者還可以寫成這樣
val positives = list.filter { it > 0 }
3.3Map創建
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
3.4創建單例
使用object關鍵字加函數名就可以聲明一個單例對象,object一樣可以繼承其他類或者實現接口
object Resource {
val name = "Name"
}
3.5單表達式函數
fun theAnswer() = 42
等价于
fun theAnswer(): Int {
return 42
}
单表达式函数与其它惯用法一起使用能简化代码,例如和 when 表达式一起使用:
fun transform(color: String): Int = when (color) {
"Red" -> 0
"white" -> 1
"Blue" -> 2
else -> println("no color ")
}
4.類和繼承
4.1kotlin中使用關鍵字聲明類
Class A {
}
类声明由类名、类头(指定其类型参数、主构造函数等)以及由花括号包围的类体构成。类头与类体 都是可选的; 如果一个类没有类体,可以省略花括号。
Class A
4.2構造函數
在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次构造函数。主构造函数是类头的一部 分:它跟在类名(和可选的类型参数)后.
class Person constructor(firstName: String) {
}
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
class Person(firstName: String) {
}
4.3次构造函数
类也可以声明前缀有 constructor的次构造函数:
class Customer{
constructor(name: String) {
}
constructor(name: String, age: Int) : this(name) {
}
}
4.4创建类的实例
val invoice = Invoice()
val customer = Customer("Joe Smith")
注意 Kotlin 并没有 new 关键字。
4.5类成员
类可以包含:
构造函数和初始化块
函数
属性
嵌套类和内部类
对象声明
4.6繼承
在 Kotlin 中所有类都有一个共同的超类 Any
,这对于没有超类型声明的类是默认超类:
class Example // 从 Any 隐式继承
注意:Any
并不是 java.lang.Object
;尤其是,它除了 equals()
、hashCode()
和toString()
外没有任 何成员。
要声明一个显式的超类型,我们把类型放到类头的冒号之后:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
注意:要添加open關鍵字的類才能被繼承,在默認情況下,Kotlin中所有的類都是final
4.7重寫方法
與繼承一樣,需要重寫的方法也是需要加open的
open class Base {
open fun v() {}
fun nv() {}
}
在子類上重寫的方法上需要加上override
class Derived() : Base() {
override fun v() {}
}
注意:标记为 override 的成员本身是开放的,也就是说,它可以在子类中覆盖。如果你想禁止再次覆 盖,使用 final 关键字:
class Derived() : Base() {
final override fun v() {}
}
4.6覆盖属性
属性覆盖与方法重寫类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并 且它们必须具有兼容的类型。每个声明的属性可以由具有初始化器的属性或者具有 getter 方法的属 性覆盖。
open class Foo {
open val x : Int get() { …… }
}
class Bar1 : Foo() {
override val x : Int = ……
}
也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质 上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法。
注意,你可以在主构造函数中使用 override 关键字作为属性声明的一部分。
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
4.7覆蓋規則
在 Kotlin 中,实现继承由下述规则规定:如果一个类从它的直接超类继承相同成员的多个实现, 它 必须覆盖这个成员并提供其自己的实现(也许用继承来的其中之一)。 为了表示采用从哪个超类型继 承的实现,我们使用由尖括号中超类型名限定的 super,如 super<Base>:
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 {
// 编译器要求覆盖 f():
override fun f() {
super<A>.f() // 调用 A.f()
super<B>.f() // 调用 B.f()
}
}
同时继承 A 和 B 没问题,并且 a() 和 b() 也没问题因为 C 只继承了每个函数的一个实现。 但是 f() 由 C 继承了两个实现,所以我们必须在 C 中覆盖 f() 并且提供我们自己的实现来消除歧义。
4.8抽象類
类和其中的某些成员可以声明为 abstract。 抽象成员在本类中可以不用实现。 需要 注意的是,我 们并不需要用 open 标注一个抽象类或者函数——因为这不言而喻。
我们可以用一个抽象成员覆盖一个非抽象的开放成员
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
5.接口
Kotlin 的接口与 Java 8 类似,既包含抽象方法的声明,也包含实现。与抽象类不同 的是,接口无 法保存状态。它可以有属性但必须声明为抽象或提供访问器实现。
(1)使用关键字 interface 来定义接口
interface A {
fun bar()
fun foo() {
// 可选的方法体
}
}
(2)实现接口
class B : A {
override fun bar() {
// 方法体
}
}
(3)接口中的属性
你可以在接口中定义属性。在接口中声明的属性要么是抽象的,要么提供访问器的实现。
interface A {
val prop: Int // 抽象的
val name: String
get() = "name"
fun foo() {
print(prop)
}
}
class B : A {
override val prop: Int = 29
}
6.數據類
(1)我们经常创建一些只保存数据的类。 在这些类中,一些标准函数往往是从数据机械推导而来的。 在Kotlin 中,这叫做 数据类 并标记为 data:
data class user(val name: String, val email: String)
会为 user 类提供以下功能:
所有属性的 getters (对于 var 定义的还有 setters)
equals()
hashCode()
toString()
copy()……等等
为了确保生成的代码的一致性和有意义的行为,数据类必须满足以下要求:
主构造函数需要至少有一个参数;
主构造函数的所有参数需要标记为 val 或 var;
数据类不能是抽象、开放、密封或者内部的;
(在1.1之前)数据类只能实现接口。
(2)在类体中声明的属性
注意,对于那些自动生成的函数,编译器只使用在主构造函数内部定义的属性。如需在生成的实现中 排出一个属性,请将其声明在类体中:
data class Person(val name: String) {
var age: Int = 0
}
7.泛型
(1)与 Java 类似,Kotlin 中的类也可以有类型参数:
class Box<T>(t: T) {
var value = t
}
(2)一般来说,要创建这样类的实例,我们需要提供类型参数:
val box: Box<Int> = Box<Int>(1)
(3)但是如果类型参数可以推断出来,例如从构造函数的参数或者从其他途径,允许省略类型参数:
val box = Box(1) // 1 具有类型 Int,所以编译器知道我们说的是 Box<Int>。
7.1泛型函數
(1)不仅类可以有类型参数。函数也可以有。类型参数要放在函数名称之前:
fun <T> singletonList(item: T): List<T> {
// ……
}
fun <T> T.basicToString() : String { // 扩展函数
// ……
}
(2)要调用泛型函数,在调用处函数名之后指定类型参数即可:
val l = singletonList<Int>(1)
或者kotlin自動識別可以寫成這樣
val l = singletonList(1)
8.嵌套类与内部类
(1)类可以嵌套在其他类中:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo()
8.1內部類
(1)类可以标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用:
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo()
9.枚举类
(1)枚举类的最基本的用法是实现类型安全的枚举:
enum class Direction {
A, B, C, D
}
每个枚举常量都是一个对象。枚举常量用逗号分隔。
9.1初始化
因为每一个枚举都是枚举类的实例,所以他们可以是这样初始化过的:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
9.2匿名類
(1)枚举常量也可以声明自己的匿名类:
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
及相应的方法、以及覆盖基类的方法。注意,如果枚举类定义任何成员,要使用分号将成员定义中的 枚举常量定义分隔开,就像在 Java 中一样。
PS:kotlin接口