kotlin入门精简版
类
构造函数
一个类可以有一个主构造函数和一个或多个次构造函数
1 | class Person constructor(firstName: String) { /*……*/ } |
当没有访问修饰符或者注解时可以省略constructor
关键字
1 | class Person (firstName: String) { /*……*/ } |
在成员变量和init
代码块中可以直接使用主构造方法中的参数
1 | class Person (firstName: String) { |
成员变量和init
代码块按定义顺序依次执行。
次级构造函数需要直接或间接委托给柱构造函数
1 | class Person(firstName: String) { |
继承
kotlin
中类默认是final
的,若要允许类被继承,需要手动声明为open
(同理也适用于函数)
1 | open class Base(val p: Int, val q: String) { |
子类是用:
表示继承的类或实现的接口,子类必须要直接或间接调用父类的主构造方法
1 | class BarAdapter(data: MutableList<Person>) : |
属性和字段
var
可变
val
不可变,只读,类似java
final
声明一个字段的完整语法
1 | var <propertyName>[: <PropertyType>] [= <property_initializer>] |
一般地,属性声明为非空类型必须在构造函数中初始化。 然而,这经常不方便。例如:控件一般在Activity
或者Fragment
的onCreate
方法中实例化、利用Bundle
在Activity
和Fragment
传值。 这种情况下,不能在构造函数内提供一个非空初始器。但是任然想在使用该字段时候避免空检测。
这种场景下我们可以使用延迟初始化lateinit var
:
1 | class MainActivity : AppCompatActivity() { |
使用lateinit var personLateInit
就可以避免空检测。
同样可以用this::personLateInit.isInitialized
来判断是否该字段已经实例化
数据类
使用data
关键字标识一个类,该类会变成数据类
1 | data class User(val name: String, val age: Int) |
编译器自动从主构造函数中声明的所有属性生成以下方法:
equals()
/hashCode()
;toString()
格式是"User(name=yaocai, age=27)"
;componentN()
函数按声明顺序对应于所有属性;copy()
函数。
1 | val user1 = User("yaocai", 27) |
密封类
使用sealed
关键字标识一个类,该类会变成数据类
,常用语限定类的继承类型,使用when
就不需要else
分支了,因为所有情况都是已知。
1 | sealed class ItemType |
函数
定义
函数使用 fun 关键字声明,括号后面声明返回值类型:
1 | fun foo(x: Int): Int { |
如果返回值可以推导出来,可以省略返回值类型
1 | override fun getLayoutResourceId() = R.layout.act_foo |
参数
函数的参数是val
隐性修饰的,遵循Effective java
中形参的最佳实践,避免方法内部对实参引用地址的修改
1 | fun foo(user: User){ |
默认参数
函数参数可以有默认值,当省略相应的参数时使用默认值。与其他语言相比,这可以减少重载数量:
1 | class NetWork { |
如果我们需要生成所有重载方法,可以使用@JvmOverloads
注解,譬如在自定义控件时:
1 | class CustomView constructor( |
具名参数
当参数太多时候,为了阅读代码更加便捷,可以使用具名参数,是我们的代码更加有可读性:
1 | fun check(content: String, |
不使用具名参数时,该调用看起来就像:
1 | check(content, true , false ,true) |
阅读起来看很困难,使用具名参数后调用看起来就像:
1 | check(content, isUpperCase = true, needCheckLength = false, isWholeString = false) |
还允许我们更改参数位置。
高阶函数
高阶函数是将函数用作参数或返回值的函数。
1 | fun filter(predicate: (String) -> Boolean) {} |
filter
是高阶函数,函数类型作为参数
1 | fun getFilterFunction(): (String) -> Boolean { |
getFilterFunction
也是高阶函数,函数类型作为返回值。
函数类型
括号中定义形参的类型,箭头后面定义返回值类型:
1 | (String, String) -> Boolean |
可以像其他类型子字段一样使用函数类型字段,对函数类型字段赋值和调用:
1 | class Bar { |
如果函数类型字段可以空,定义时应该像上述示例代码一样,将括号后面使用?
,返回值后的?
表示返回值可空。
有了函数类型,我们就可以替换项目中使用interface
定义的callback
了:
1 | class BaseAdapter(private val listener: (View?, Int) -> Unit) : |
两处实力代码函数类型实例化分别使用::functionName
和lambda表达式
两处实力代码函数类型调用分别使用invoke
和()直接调用
lambda表达式
1 | fun filter(predicate: (String) -> Boolean) { |
函数filter
是高阶函数,它接受一个函数作为参数。该表达式本身是一个函数,相当于有具体名字的函数:
1 | fun filtLength(input: String):Boolean{ |
lambda表达式
始终定义在花括号里面,先定义参数类型,函数体在箭头后面。lambda表达式
一般可以根据规则精简得很短。
如果作为方法的最后一个参数,可以放到方法括号外面,如果方法只这一个参数,则可以省略括号:
1 | filter({ input:String -> input.length > 12 }) |
接着参数类型也可以省略,如果只有一个参数,这参数也可以省略用,使用时用it
代替:
1 | // 省略参数类型 |
在Android
中,监听点击事件使用Lambda表达式
后就类似如下样子了:
1 | view.setOnclickListener{ |
if、when、for、while
基本用法和java
类似,这里说下不同之处。
if
可以作为表达式,他可以返回一个值,默认分支中的最后一行就代表该分支的返回值,因此可以是用if``esle
代替java
中的三元运算
1 | val max = if (a > b) { |
when
类似于java
中的switch
,功能更加强大,每个判断分支可以是表达式:
1 | when(x){ |
for
循环可以对任何提供迭代器(iterator)
的对象进行遍历,即任何实现了Iterable
接口的对象
1 | for (item in collection) print(item) |
对于java
中索引循环,可以使用IntRange
代替
1 | for (int i = 0; i < 100; i++) { |
1 | for(i in 0..99){ |
..
右边是闭区间,until
右边是开区间,还可以使用downTo
降序循环。
return、break、contine
- return。默认从最直接包围它的函数或者匿名函数返回。
- break。终止最直接包围它的循环。
- continue。继续下一次最直接包围它的循环。
return
根据定义,如下代码只会打印1、2,然后返回到foo()
1 | fun foo() { |
若我们想返回到forEach
,只是过滤掉3
,且打印this point is unreachable
,则可以使用标签,使用标签名@
定义标签,使用return@标签名
返回到指定标签。
1 | fun foo() { |
还可以使用隐式标签,默认标签与lambda表达式
同名:
1 | fun foo() { |
当然,还是使用匿名函数:
1 | fun foo() { |
这种场景类似于循环中的contine
,对于break
的情况,若要使用return
,可以在外出嵌套run
方法:
1 | fun foo() { |
break contine
循环时,break
和contine
同样也可以使用标签
1 | fun foo() { |
作用域函数(scope function)
作用域函数一共有五种:let
、run
、with
、apply
以及 also
,是在一个对象的上下文中执行代码块,形如:
1 | val user = User() |
他们间的区别在于
- 引用上下文对象的方式
- 返回值
引用上下文对象的方式
run
、with
、apply
通过this
引用上下文对象,因此可以直接使用该对象的成员变量和函数,例如:
1 | user.apply{ |
let
、also
通过it
引用上下文对象,例如:
1 | user?.let{ |
返回值
also
、apply
返回对象本身
1 | // 返回对象本身 user |
let
、with
、run
返回代码块的返回值
1 | // 返回代码块的返回值,String类型 |
主要区别表
函数 | 对象引用 | 返回值 |
---|---|---|
let |
it |
Lambda 表达式结果 |
run |
this |
Lambda 表达式结果 |
with |
this |
Lambda 表达式结果 |
apply |
this |
上下文对象 |
also |
it |
上下文对象 |
场景
大部分情况5中作用域函数是可以互相替换,对于一些特定场景,可以优选用特定作用域函数
let
通常用于非空值执行代码块
1 | val str: String? = "Hello" |
with
同样用于对接受对象,进行一下操作
1 | override fun convert(holder: BaseViewHolder, item: Person) { |
run
用于代码块同时对上下文对象初始化和返回值时
1 | val user = User() |
apply
通用用于配置上下文对象
1 | val user = User().apply{ |
also
执行一些将上下文对象作为参数的操作
1 | val user = User() |
kotlin入门精简版