基础入门
声明变量
1 | val a :String = "hello" |
val:只读; var:可读写
自动类型推导
Long标记
必须大写L
1 | val a = 222L |
类型转换
int转long不能直接转(跟java不同)
1 | val a = 2 |
无符号类型
java没有
UInt ,UShort,ULong
1 | val a = 1u |
String
1 | val content = "hello kotlin" |
- Kotlin 中 “==”比较的是内容,“===” 比较的是引用
- 字符串拼接可以使用“$”
- 格式化string,如上所示,trimIndent()方法的作用是去掉公共的缩进
数组
1 | /** |
区间
1 | /** |
集合框架
增加了不可变的集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* 定义
*/
val listOf = listOf(1, 23) //不可变
val mutableListOf = mutableListOf<Int>()
val mapOf = mapOf("name" to "wzq", "age" to 18)
val mutableMapOf = mutableMapOf("name" to "wzq", "age" to 18)
/**
* 添加
*/
mutableListOf.add(3)
mutableListOf += 4
mutableListOf -= 4
/**
* 访问
*/
listOf[0]
mapOf["name"]
mapOf.get("age")增加了一些语法糖 filter,map等
基于Java,类型别名typealias,给java中的java.util.ArrayList在kotlin中起了一个别名ArrayList
1
2
3
4
5typealias ArrayList<E> = java.util.ArrayList<E>
typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
typealias HashMap<K, V> = java.util.HashMap<K, V>
typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
typealias HashSet<E> = java.util.HashSet<E>新类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* 新类型pair
*/
val pair = "key" to "value"
val pair1 = Pair("key", "value")
//读取内容
pair.first
pair.second
val (x, y) = pair //解构表达式
/**
* 新类型triple
*/
val tt = Triple(1, 2, 3)
val (m, n, z) = tt //解构表达式
tt.first
tt.second
tt.third
函数基本概念
kotlin的函数有自己的类型,所以是“一等公民”。可以赋值,传递并在合适的条件下调用。
方法跟函数(概念性)
定义在类中的函数是方法
函数类型,函数引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 类型 ()->Unit
// 引用 ::foo
fun foo(){}
// (String)->Int
// ::foo
fun foo(str:String):Int{}
class Foo{
// Foo.(String)->Long (Foo,String)->Long
// Foo::bar
fun bar(str: String):Long{}
}
val f: ()->Unit = ::foo
val h: (Foo,String)->Long = Foo::bar
val foo = Foo()
val a: (String)->Long = foo::bar
//通过引用调用
a.invoke("wzq")
//或者
a("wzq")
参数:变长,默认,具名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* 变长参数
*/
fun mutiParams(vararg mutiParam: Int): Int {
return mutiParam.sum()
}
/**
* 默认参数
*/
fun defaltParams(name: String, type: Int = 1) {
print("$name,$type")
}
fun test() {
defaltParams("wzq")
/**
* 具名参数
*/
defaltParams(name = "zpy", type = 2)
print(mutiParams(1, 2))
}多返回值(借助Pair,Triple)
类和接口
kotlin中默认都是public的
kotlin中类的成员变量必须初始化(在构造器中完成初始化,或者定义的时候直接赋值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//默认是public的
class SimpleClass {
/**
* 这里不用初始化是因为在构造器中完成了
**/
private var x: Int
/**
* 这里的y必须初始化,kotlin强调类必须对自己的成员变量负责。
**/
private var y: Int = 0
/**
* 构造器方式(副构造器)
**/
constructor(x: Int) {
this.x = x
}
}构造器,分为主构造器跟副构造器。新建类实例的路径必须经过主构造器(如果有的话)
以上类从副构造器转为主构造器如下:
1
2
3
4
5
6/**
* 主构造器
**/
class SimpleClass(private var x: Int) {
private var y: Int = 0
}在kotlin定义一个属性 = get方法+set方法+属相
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class SimpleClass(x: Int) {
var x = x
/**
* 以下默认生成,需要自定义的话可以改成自己的逻辑
*/
get() {
return field //backing field
}
set(value) {
field = value
}
}
fun test() {
val simpleClass = SimpleClass(1)
/**
* 属相引用
*/
val kProperty1 = simpleClass::x
kProperty1.set(2)
print(kProperty1.get())
}类中的方法默认都是final的,如果子类想要重写,那么需要在类的方法前面加open关键字
接口中可以定义属性
继承类跟接口使用“:”,类的话需要带主构造,具体如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40abstract class AbstractClass {
abstract fun xxx()
open fun canReWrite() {}
/**
* 没有open子类不能重写
*/
fun cannotReWrite() {}
}
interface Interface {
fun yyy()
/**
* 接口可以定义属相
*/
val y: Int
}
class SimpleClass(private val x: Int) : AbstractClass(), Interface {
/**
* 实现接口定义的属相y
*/
override val y: Int
get() = TODO("Not yet implemented")
override fun yyy() {
TODO("Not yet implemented")
}
override fun xxx() {
TODO("Not yet implemented")
}
override fun canReWrite() {
super.canReWrite()
print("xxx")
}
}
空安全类型&平台类型
1 | fun test() { |
扩展方法&属相
kotlin支持在不修改类代码的情况下,动态为类添加属性(扩展属性)和方法(扩展方法)
扩展方法定义
1
2
3
4
5
6
7
8
9
10
11class StringUtils {
var name: String = "StringUtils"
}
fun StringUtils.CustomGetLenth(): Int {
return this.name.length
}
fun main(args: Array<String>) {
val lenth = StringUtils().CustomGetLenth()
}扩展方法本质
1
2
3
4
5
6
7public final class StringUtilsKt
{
public static final int CustomGetLenth(@NotNull StringUtils $receiver)
{
Intrinsics.checkParameterIsNotNull($receiver, "receiver$0");return $receiver.getName().length();
}
}如果父类和子类都扩展了同名的一个扩展方法,引用类型均为父类的情况下,会调用父类的扩展方法。
扩展属相没有backing field,(接口中的属相也没有)
智能类型转换
a as B
a as? B (失败返回null)
a is B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15open class Parent
class Son(val name: String) : Parent() {
}
fun test() {
val x: Parent = Son("wzq")
/**
* x is Son 之后,如果返回true,那么x就已经强转完成了
*/
if (x is Son) {
print(x.name)
}
}
if,when,try catch 是表达式
1 | var b: Boolean = false |
LAMBDA 表达式
1 | fun name() { |
== & hashcode
版本1:
1 | class Person(var name: String, var age: Int) |
版本2:
1 | class Person(var name: String, var age: Int) { |
版本3:
1 | fun main() { |
把Person的name跟age改为 val之后可以避免这种错误,因为val不允许修改
版本4:
1 | class Person(val name: String, val age: Int) { |
函数进阶
高阶函数
概念:参数是函数或者返回值是函数的函数
例子:
1
2// IntArray的构造方法
public inline constructor(size: Int, init: (Int) -> Int)
内联函数
概念:在程序编译时能将程序中内联函数的调用表达式直接替换成内联函数的函数体。
例子:
1
2
3
4
5
6
7
8
9
10
11
12fun doSomething(m: () -> Unit) {
print("doSomething start")
m()
print("doSomething end")
}
fun main() {
print("main start")
doSomething { print("lambda") }
print("main end")
}转为java
1
2
3
4
5
6
7
8
9
10public static final void main() {
String var0 = "main start";
System.out.print(var0);
/**
* 1. 调用了doSomething方法,2. new了一个Function0的实例
**/
doSomething((Function0)null.INSTANCE);
var0 = "main end";
System.out.print(var0);
}doSomething添加inline之后转为java
1
2
3
4
5
6
7
8
9
10
11
12
13
14public static final void main() {
String var0 = "main start";
System.out.print(var0);
// 第一次内联:少了doSomething的调用
String var7 = "doSomething start";
System.out.print(var7);
// 第二次内联,少了lambda函数的调用
String var4 = "lambda";
System.out.print(var4);
var7 = "doSomething end";
System.out.print(var7);
var0 = "main end";
System.out.print(var0);
}相比较
- 少了新建实例的开销
- 少了方法调用(进出方法栈)的开销,一个是doSomething方法的调用,一个是lambda函数的调用
高阶函数跟内联函数更配
内联函数的局部return跟非局部return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16fun main() {
intArrayOf(1,2,3).forEach {
if(it == 2)
/**
* 局部return,这里相当于continue
*/
return@forEach
println(it)
}
println("bbb")
return
}
// 打印结果
1
3
bbb1
2
3
4
5
6
7
8
9
10
11
12
13
14fun main() {
intArrayOf(1,2,3).forEach {
if(it == 2)
/**
* 全局return。直接退出main函数
**/
return
println(it)
}
println("bbb")
return
}
// 打印结果
1因为foreach是内联函数,转为java之后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static final void main() {
int[] $this$forEach$iv = new int[]{1, 2, 3};
int[] var2 = $this$forEach$iv;
int var3 = $this$forEach$iv.length;
for(int var4 = 0; var4 < var3; ++var4) {
int element$iv = var2[var4];
if (element$iv == 2) {
return;
}
System.out.println(element$iv);
}
String var9 = "bbb";
System.out.println(var9);
}noinline关键字可以取消函数参数的内联
1
2
3
4
5
6
7
8inline fun limit( noinline p1:()->Unit){
print("limit")
p1()
}
fun main() {
limit { print("xxx") }
}转为java
1
2
3
4
5
6
7
8
9public static final void main() {
Function0 p1$iv = (Function0)null.INSTANCE;
int $i$f$limit = false;
//函数内联了
String var2 = "limit";
System.out.print(var2);
//函数参数没有内联
p1$iv.invoke();
}crossinline关键字不允许非局部返回
非局部return的时候return的是调用的地方,lambda函数就没有了返回值,这种情况在某些情景下是有问题的,所以就有了crossinline关键字
为什么要有crossinline可以参考:https://blog.csdn.net/u013009899/article/details/78584994,这里有例子。
说白了,就是我们如果直接在lambda参数中结束当前函数,而不给lambda提供一个返回值,这种情况是不被允许的
内联函数的没有被声明为noinline的函数(lambda)参数不能被存储
1
2
3
4inline fun limit(p1:()->Unit){
//编译出错
val a = p1
}
几个有用的高阶函数
let 返回值是block的返回值
比如fragment的oncreate方法中解析bundle的内容
1
2
3
4
5
6
7
8
9override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let { // it
imageId = it.getInt(ARG_IMAGE)
tip = it.getString(ARG_TIP)
bgColor = it.getInt(ARG_BG_COLOR)
smallBgImg = it.getInt(ARG_SMALL_BG_IMAGE)
}
}run 返回值是block的返回值
1
2
3
4
5
6
7
8
9override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.run { //this
imageId = getInt(ARG_IMAGE)
tip = getString(ARG_TIP)
bgColor = getInt(ARG_BG_COLOR)
smallBgImg = getInt(ARG_SMALL_BG_IMAGE)
}
}
also 返回值跟调用者是同一个实例
apply T.apply(block: T.() -> Unit): T 返回值跟调用者是同一个实例(比如下面例子,loadingDialog实例调用的,返回值还是那个实例)
例子1: fragment的newInstance方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17companion object {
fun newInstance(
Int, imageId:
tip: String? = null,
Int = -1, bgColor:
Int = -1 smallBgImg:
) =
LoadingDialog().apply { // this
arguments = Bundle().apply { // this
putInt(ARG_IMAGE, imageId)
putString(ARG_TIP, tip)
putInt(ARG_BG_COLOR, bgColor)
putInt(ARG_SMALL_BG_IMAGE, smallBgImg)
}
}
}例子2: build模式 ( OkHttpClient源码 )
1
2
3
4
5
6
7fun addInterceptor(interceptor: Interceptor) = apply {
interceptors += interceptor
}
fun addNetworkInterceptor(interceptor: Interceptor) = apply {
networkInterceptors += interceptor
}use
不用自己关闭流
1
2
3
4File("build.gradle").inputStream().reader().buffered()
.use {
print(it.readLine())
}
集合变换与序列
filter
过滤集合
map
元素一对一映射之后拼接
flatmap
每个元素映射为一个集合,之后把所有的集合拼接起来
asSequence
懒汉式序列
eg:饿汉式如下
1 | arr.filter { |
添加 asSequence之后变为懒汉,foreach类似水龙头开关,没有的话,啥也不会输出
1 | arr.asSequence() |
sum 求和
reduce (没有初始值,返回值跟item一个类型)
源码:
1
2
3
4
5
6
7
8
9public inline fun IntArray.reduce(operation: (acc: Int, Int) -> Int): Int {
if (isEmpty())
throw UnsupportedOperationException("Empty array can't be reduced.")
var accumulator = this[0]
for (index in 1..lastIndex) {
accumulator = operation(accumulator, this[index])
}
return accumulator
}例子:求和
1
2
3
4val arr = intArrayOf(1, 2, 3, 4)
arr.reduce { acc, i ->
acc + i
}fold(给一个初始值,对每一个item做同样的operation,返回值跟初始值一个类型)
源码:
1
2
3
4
5public inline fun <R> IntArray.fold(initial: R, operation: (acc: R, Int) -> R): R {
var accumulator = initial
for (element in this) accumulator = operation(accumulator, element)
return accumulator
}例子:求和
1
2
3
4
5
6val arr = intArrayOf(1, 2, 3, 4)
val fold = arr.fold(0) { acc, i ->
acc + i
}
print(fold)
//10
sam转换(single abstract method)
eg:匿名内部类Runnable
1 | Thread( |
简写如下:
1 | Thread( |
SAM之后
1 | Thread { print("run") } |
eg:设置点击监听
1 | callWithDialog.setOnClickListener(object:View.OnClickListener{ |
sam之后,可以简写为
1 | callWithDialog.setOnClickListener { println("onclick") } |
调用java的时候可以,如果单方法接口是kotlin,那么不支持。
实例:统计文本中字符(不含空格)出现个数
1 | File("build.gradle") |
tip
this@MainActivity : 内部类获取context的方法