优点
Kotlin 跨平台 技术旨在简化跨平台项目的开发。 它减少了在不同平台编写和维护相同代码所需的时间,同时保持了本地编程的灵活性和优势,使用 Kotlin 构建的应用程序崩溃的可能性减少了 20%。
正如官方所说,主要特点是灵活性,一些语法糖,同时提高代码的健壮性(这个和代码功底有关,好的代码性不于语言是什么)。灵活性意味着代码的可读性质有所下降,总体优点突出,所以kotlin 成为了Android 的第一语音
一,基础语法
1.函数
fun sum(a: Int, b: Int): Int {
return a + b
}
或
fun sum(a: Int, b: Int) = a + b
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}
fun foo(a: Int = 0, b: String = "") { ... }
函数参数类型处于之后,这是和Java 的区别,并可转化为推导函数,
因为kotlin 具有推导功能,所以变量类型在kotlin 并不是必须的,这个和ts 语音很像,目前这种具备强类型和推导类型的语音,都有此特点。
其中Unit 类型相当于Java void 的,并可省略
可以指定参数默认值:可以自定义默认数值,比Java 的扩展性更好
2.变量声明
Kotlin 中,你可以使用关键字 val 或 var 来声明变量,接着是变量的名称。
val x: Int = 5
var x: Int = 5
val x = 5
val x by lazy {
5
}
lateinit var s: String
val 声明既确定相当于Java 的final ,要求属性声明为val
var 正常生成变量,Java中声明变量的方式一样。
没有类型:类型推断
lazy延迟声明:其实是一种设计模式,用到执行,可以提升代码的执行效率,这意味着该变量一旦初始化后就不允许再被修改值了(基本类型是值不能被修改,对象类型是引用不能被修改)。{}内的操作就是返回唯一一次初始化的结果。
lateinit: 只能用来修饰类属性并且不能val(用法冲突),不能用来修饰局部变量,一定也会在后面某个合理的时机将该属性对象初始化的.
3. 惯用语法
val positives = list.filter { it > 0 }
过滤条件并返回符合条件的list
if ("john@example.com" in emailsList) { ... } if ("jane@example.com" !in emailsList) { ... }
检查集合中元素是否存在
println("Name $name")
字符串插值
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
替代Java 的Switch,弱化的类型支持更多内容
val list = listOf("a", "b", "c")
只读list
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
只读映射
println(map["key"]) map["key"] = value
访问映射
val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
println(item)
}
for ((k, v) in map) { println("$k -> $v") }
相当于Java 的增强for 循环
for (i in 1..100) { ... } // 封闭区间:包括 100
for (i in 1..<100) { ... } // 开放区间:不包括 100
for (x in 2..10 step 2) { ... } // 一次减少2
for (x in 10 downTo 1) { ... } // 倒叙访问
(1..10).forEach { ... } // 增强遍历
while 循环
val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
支持Java 的lambda 表达式
val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
.filter { it.startsWith("a") }
.map { it.uppercase() }
.forEach { println(it) }
支持类型转换
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 在此分支中,`obj` 会自动转换为 `String`。
return obj.length
}
// 在类型检查分支外部,`obj` 仍然是 `Any` 类型。
return null
}
快速单利创建:可以看到koltin 的写法确实简洁很多
object Resource { val name = "Name" } // 饿汉式实现
class SingletonDemo private constructor() {// 懒汉式
companion object {
private var instance: SingletonDemo? = null
get() { if (field == null) {
field = SingletonDemo() }
return field }
fun get(): SingletonDemo{return instance!! } } }
// 线程安全的:Synchronized 变成了注解
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
@Synchronized
fun get(): SingletonDemo{
return instance!!
}
}
}
//kotlin实现 双重校验锁式
class SingletonDemo private constructor() {
companion object {
val instance: SingletonDemo by lazy {
SingletonDemo() }
}
}
If-not-null (非空)简写
val files = File("Test").listFiles() println(files?.size) // 如果文件不为空,则打印大小
映射可空值 If-not-null(非空)转换
val value = ... val mapped = value?.let { transformValue(it) } ?: defaultValue // 如果值或转换结果为 null,则返回 defaultValue。
If-not-null-else (非空否则)简写
val files = File("Test").listFiles() // 对于简单的备用值: println(files?.size ?: "empty") // 如果 `files` 为 null,则打印 "empty"。 // 要在代码块中计算更复杂的备用值,请使用 `run`。 val filesSize = files?.size ?: run { val someSize = getSomeSize() someSize * 2 } println(filesSize)
在一个对象实例上调用多个方法(with)
class Turtle { fun penDown() fun penUp() fun turn(degrees: Double) fun forward(pixels: Double) } val myTurtle = Turtle() with(myTurtle) { // 画一个100像素的正方形 penDown() for (i in 1..4) { forward(100.0) turn(90.0) } penUp() }
配置对象的属性(apply)
val myRectangle = Rectangle().apply { length = 4 breadth = 5 color = 0xFAFAFA }
交换两个变量
var a = 1 var b = 2 a = b.also { b = a }
4 创建类和实例
class Shape // 使用关键字 `class`
class Rectangle(val height: Double, val length: Double) { val perimeter = (height + length) * 2 }//默认构造函数,其中参数在类声明中列出,将自动可用
open class Shape
class Rectangle(val height: Double, val length: Double) : Shape() { val perimeter = (height + length) * 2 }
//类之间的继承使用冒号(`:` )来声明。 类默认是 `final` 的;要使一个类可继承,需要将其标记为 `open`:
koltin 的继承和实现为都是使用: 号,Java 中实现和继承用了不同方式,kotlin 的接口和抽象写法却和Java一样
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints $name")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
First property: hello First
initializer block that prints hello
Second property: 5
Second initializer block that prints 5
在实例初始化期间,初始化块按照它们在类体中出现的顺序执行,与属性初始化器顺序执行
- 次构造函数: 一个类有很多次构造函数,Java 中并没有主次之分,有默认函数
class Person(val pets: MutableList<Pet> = mutableListOf())
class Pet {
constructor(owner: Person) {
owner.pets.add(this) // 将此宠物添加到其所有者的宠物列表中
}
}
- 主次共存:如果一个类有一个主构造函数,那么每个次构造函数都需要向主构造函数进行委托。 这可以直接委托,也可以通过其他次构造函数间接委托。 委托给同一类的另一个构造函数可以使用
this关键字来实现:
class Person(val name: String) {
val children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
- init 代码块: 类似Java 的静态代码块
class Constructors {
constructor(i: Int) {
println("Constructor $i")
}
init {
println("Init block")
}
}
Init block
Constructor 1
- 属性定义规则:
var <propertyName>[: <属性类型>] [= <属性初始化值>] [<getter>] [<setter>]
getter 和 setter 都是可选的,根据情况可以适当的省略
class Rectangle(val width: Int, val height: Int) {
val area: Int // 属性类型是可选的,因为它可以从 getter 的返回类型中推断出来
get() = this.width * this.height
}
fun main() {
val rectangle = Rectangle(3, 4)
println("Width=${rectangle.width}, height=${rectangle.height}, area=${rectangle.area}")
}
Width=3, height=4, area=12
- 数据类
Kotlin 中的数据类主要用于保存数据。 对于每个数据类,编译器会自动生成额外的成员函数,这些函数允许你将实例打印为可读输出、比较实例、复制实例等。 数据类使用
data标记:类似Java 的bean 类
data class User(val name: String, val age: Int)
- 密封类
密封 类和接口提供了对类层次结构的控制继承。
所有直接子类在编译时都是已知的。 其他子类不能出现在密封类定义的模块和包之外。 同样的逻辑也适用于密封接口及其实现:一旦包含密封接口的模块被编译,就不能创建新的实现。一个密封类本身总是一个 抽象类 ,因此不能直接实例化
5 返回与跳转
在 Kotlin 中任何表达式都可以用标签来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@、fooBar@。 要为一个表达式加标签,我们只要在其前加标签即可。
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
un foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // 从传入 run 的 lambda 表达式非局部返回
print(it)
}
}
print(" done with nested loop")
}
//sampleEnd
fun main() {
foo()
}
标签限定的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。指定返回或者退出的节点更容易指定退出位置,在代码开发过程去体验强大之处
函数式编程
SAM,全名为 Single Abstract Method,意思为“单一抽象方法”。 它的用法和 JavaScript 中的匿名函数或高阶函数类似。 主要用于将函数当做一个方法的参数进行传递的场景,一个仅包含一个抽象方法的接口被称为函数式接口 ,或者单一抽象方法(SAM)接口。 函数式接口可以拥有多个非抽象成员,但只能有一个抽象成员。
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
val isEven = IntPredicate { it % 2 == 0 }
fun main() {
println("Is 7 even? - ${isEven.accept(7)}")
}
- 扩展函数/属性: 类似于js 的扩展函数
open class Shape
class Rectangle: Shape()
fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"
fun printClassName(s: Shape) {
println(s.getName())
}
函数式编程(SAM)接口
一个仅包含一个抽象方法的接口被称为函数式接口 ,或者单一抽象方法(SAM)接口
fun interface IntPredicate { fun accept(i: Int): Boolean }
// 原始写法
// 创建类的实例 val isEven = object : IntPredicate { override fun accept(i: Int): Boolean { return i % 2 == 0 } }
// 使用 lambda 创建实例
val isEven = IntPredicate { it % 2 == 0 }
扩展(新的装饰)
Kotlin 提供了一种扩展类或接口功能的能力,而无需继承自该类或使用设计模式,比如 装饰者(Decorator)。 这是通过称为 扩展(extensions) 的特殊声明完成的。主要是为了方便点符号调用新的函数
需要注意的是扩展函数会根据具体声明类型调用,不会跟着继承等作用
open class Shape
class Rectangle: Shape()
fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"
fun printClassName(s: Shape) {
println(s.getName())
}
printClassName(Rectangle())
- 支持重载
- 可接受空对象
- 可为伴生对象生成
将扩展声明为成员
你可以在一个类内部为另一个类声明扩展。 在这样的扩展内部,有多个隐式接收者(implicit receivers) - 可以无需限定符访问其成员的对象。 在扩展中声明的类的实例被称为分发接收者(dispatch receiver) ,扩展方法的接收者类型的实例被称为扩展接收者(extension receiver)。
class Host(val hostname: String) {
// 构造函数
fun printHostname() { print(hostname) } // 方法
}
//扩展接收者
class Connection(val host: Host, val port: Int) {
// 构造函数
fun printPort() { print(port) } // 方法
// Host类的扩展函数
fun Host.printConnectionString() {// 多接受者
printHostname() // 调用Host类的printHostname()方法
print(":")
printPort() // 调用Connection类的printPort()方法
}
fun connect() {
/*...*/
分发接收者
host.printConnectionString() // 调用扩展函数
}
}
数据类
Kotlin 中的数据类主要用于保存数据。 对于每个数据类,编译器会自动生成额外的成员函数,这些函数允许你将实例打印为可读输出、比较实例、复制实例等。 数据类使用 data 标记:
data class User(val name: String, val age: Int)
委托
委托模式 已被证明是实现继承的一个良好替代方案,Kotlin 原生支持它,并且不需要任何样板代码。
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val base = BaseImpl(10)
Derived(base).print()
}