Kotlin基础知识目录
语言 | 基类 |
---|---|
Java | Object |
Kotlin | Any |
Aoko库
项目gradle
buildscript {
……
ext.anko_version = "0.10.8" //AnKo库的版本号
}
--------------------------------------------------------
模块gradle
dependencies {
……
implementation "org.jetbrains.anko:anko-common:$anko_version"
}
一些关键字
lateinit ,延时加载,不支持基本类型,不支持val,属性用的时候再初始化
如果想让val支持延时加载且不支持var
val a: String by lazy { "111" }
as ,强转
by,代理
with、apply、let、run、also
1.with
1.在with里可直接调函数
2.最后一个函数的返回值类型就是with的返回值类型
var s: String = ""
s.equals("111")
s.compareTo("123")
val with: Int = with(s) {
equals("111")
compareTo("123")
}
2.apply
和with差不多,但是返回本对象类型
val apply: String = s.apply {
equals("111")
compareTo("123")
}
3.let(有it)
1.可以判空,如果为空则不会执行
var s2: String? = null
s2?.let {
Log.w("wei", "${111}")
}
2.最后一个函数的返回值类型就是let的返回值类型
val let: Boolean = s.let {
0
equals("111")
}
4.run
最后一个函数的返回值类型就是run的返回值类型
val run:Boolean = s.run {
0
compareTo("123")
equals("111")
}
5.also
只返回本对象类型
val also:String = s.also {
0
equals("111")
}
Kotlin基本类型、数组及容器
1.基本类型
开头字母大写即可
2.数组
Kotlin的基本数组类型 | 数组类型名称 | 数组类型的初始化方法 |
---|---|---|
整型数组 | IntArray | intArrayOf |
长整型数组 | LongArray | longArrayOf |
浮点数组 | FloatArray | floatArrayOf |
双精度数组 | DoubleArray | doubleArrayOf |
布尔型数组 | BooleanArray | booleanArrayOf |
字符数组 | CharArray | charArrayOf |
var array: IntArray = intArrayOf(1, 2, 3)
array[0]
array.get(0)
array.set(0, 5)
array.size //Java使用length,Kotlin使用size
也可以指定类型
var stringArray: Array<String> = arrayOf("aa", "cc")
3.容器
与Java类似,Kotlin也有三类基本的容器,集合Set,队列List,映射Map
如果想要进行增加或删除,需要加上前缀Mutable
Kotlin的容器 | 名称 | 初始化方法 |
---|---|---|
只读集合 | Set | setOf |
只读队列 | List | listOf |
只读映射 | Map | mapOf |
可变集合 | MutableSet | mutableSetOf |
可变队列 | MutableList | mutableListOf |
可变映射 | MutableMap | mutableMapOf |
var list: MutableList<String> = mutableListOf()
list.add("111")
list.add("222")
list.add("333")
list.remove("111")
list.removeAt(0)
循环
for (i in 0 until list.size) {
Log.w("wei", list[i])
}
list.forEach {
Log.i("wei", it)
}
for (i in list.indices) {
Log.w("wei", list[i])
}
for (i in list) {
Log.w("wei", i)
}
step指定步长 downTo表示递减 out@跳出循环
out@ for (i in 100 downTo 0 step 2) {
Log.w("wei", list[i])
out@ break
}
Map也是有了改变,to 和 Pair 都可以实现初始化
var map: MutableMap<String, String> = mutableMapOf("4" to "44", "5" to "55", Pair("6", "66"))
map["1"] = "11" //都是添加 map.put("1","11")
map["2"] = "22"
map["3"] = "33"
map.remove("1")
Kotlin校验字符串空值的几个方法
方法 | 作用 |
---|---|
isNullOrEmpty | 为空指针或者字符串长度为 0 时返回true,非空串与可空串均可调用 |
isNullOrBlank | 为空指针、字符串长度为 0 或者全为空格时返回true,非空串与可空串均可调用 |
isEmpty | 字符串长度为 0 时返回true,只有非空串可调用 |
isBlank | 字符串长度为 0 或者全为空格时返回true,只有非空串可调用 |
isNotEmpty | 字符串长度 >0 返回true,只有非空串可调用 |
isNotBlank | 字符串长度 >0 且不是全空格串时返回true,只有非空串可调用 |
操作符
var string: String? = null 声明变量实例时,在类型名称后面加 ? 表示该变量可空
string?.length 调用变量方法时,在变量名称后面加 ? 表示一旦变量为空就返回null
string?.length ?: 10 新引入运算 ?: ,表示一旦变量为空 返回该运算符右边的表达式
string!!.length 新引入运算 !! ,通知编译器不做非空校验。如果运行时发现变量为空就扔出异常
结构相等
字符串的等值性判断要求 | Java的判断方式 | Kotlin的判断方式 |
---|---|---|
判断两个字符串是否相等 | str1.equals(str2) | str1 == str2 |
判断两个字符串是否不等 | !str1.equals(str2) | str1 != str2 |
引用相等
意思是除了值相等以外,还需要引用的地址(即存储地址)也必须相等(使用 ===/!== 判断)
- 对于基本类型,包括整型、浮点型、布尔型、字符串,结构相等和引用相等没有区别
- 同一个类声明的不同变量,只要有一个属性不等,则其既是结构不等,也是引用不等
- 同一个类声明的不同变量,若equals方法校验的每个属性都相等(如通过clone方法克隆出来的变量复制品),则其结构相等,但引用不等
函数运用
1.泛型函数
/**
* 泛型函数
*
* vararg也就是Java中的Object...object
*/
fun <T> t(a: String, vararg other: T): Unit {
for (item in other) {
Log.w(a, "${item.toString()}")
}
}
2.简化函数
fun add(a: Int = 1, b: Int = 3) = a + b
3.内联函数
fun setArrayNumber(array: Array<Number>) {}
inline fun <reified T : Number> setArrayNumber(array: Array<T>) {}
var intArray:Array<Int> = arrayOf(1,2,3)
setArrayNumber(intArray)
调用如上第一个方法走不通
inline 可以看成是一种替换,编译后setArrayNumber(intArray) 会被第二种方法体替换掉
reified 相当于继承关系
4.尾递归函数
指的是函数末尾的返回值重复调用了自身函数。此时要在fun前面加上关键字tailrec,告诉编译器这是一个尾递归函数,编译器则会进行相应的优化,从而提高程序性能
5.高阶函数
把一个函数指定为另一个函数的入口参数,也就是函数作为参数传递给另一个函数
fun method(a: Int, b: Int, greater: (Int, Int) -> Int): Int {
return greater(a, b)
}
//1
method(2, 3) { a, b -> a + b }
//2
method(2, 3, { a, b -> a - b })
6.拓展函数
fun String.exspand() {}
var str: String = ""
str.exspand()
在外部对String做了一个拓展
你想要拓展的类型.任意名
单例模式
添加object即可
object XXXUtils {
fun test() {
}
}
类的构造函数
Kotlin把函数看成是一种特殊的变量,那么类在某种意义上算是一种特殊的函数。所以构造函数的输入参数得直接加到类名后面,而init方法仅仅表示创建类实例时的初始化动作。
//如果主构造函数没有带@符号的注解说明,类名后面的constructor可以省略
class MyBean constructor(name: String, age: Int) {
init {
Log.w("wei", "MyBean init")
}
//重构造必须继承主构造,在有了主构造参数基础上可以自定义其他参数,可以看成是重载
constructor(name: String, age: Int, birthday: String) : this(name, age) {
}
constructor(name: String, age: Int, birthday: String, hobby: String) : this(name, age) {
}
}
如果主构造有默认参数,如何在Java里省略
//加上@JvmOverloads即可
class MyBean @JvmOverloads constructor(name: String = "wei", age: Int) {
}
在主构造参数前面加上 var 或 val 就会变成成员属性
var 能 get 和 set
val 只能 get
class MyBean @JvmOverloads constructor(var name: String, val age: Int) {
init {
this.name = "wei"
}
override fun toString(): String {
return "MyBean(name='$name', age=$age)"
}
}
伴生对象(Java的static)
Java中,静态成员使用 static 关键字来修饰,且外部是通过"类名.静态成员名称"的形式访问静态成员(包括静态属性和静态方法)的。
然而Kotlin取消了关键字static,为了弥补功能缺陷,Kotlin引入了伴生对象的概念。
class MyBean @JvmOverloads constructor(var name: String, val age: Int) {
……
//companion object后可不加名称
companion object MyBeanCompanion {
var name: String = ""
}
}
相当于是static
MyBean.MyBeanCompanion.name
MyBean.name
开放性修饰符
修饰符 | 说明 |
---|---|
public | 对所有人开放。Kotlin的类、函数、变量不加开放性修饰符的话,默认是public |
internal | 只对本模块内部开放,这是Kotlin新增的关键字。对于App开发来说,本模块是指App自身 |
protected | 只对自己和子类开放 |
private | 只对自己开放,即私有 |
类的继承
要被继承的类
class MyBean @JvmOverloads constructor(var name: String = "wei", val age: Int) {
}
MyBean2不能继承MyBean有两个问题
1.MyBean有主构造函数
2.MyBean没有open修饰符(Kotlin的类默认不能被继承)
解决第一个问题,注意,正常继承的类后面也要有()
class MyBean2(name: String, age: Int) : MyBean(name, age) {
}
解决第二个问题,被继承类加上open修饰符
open class MyBean @JvmOverloads constructor(var name: String = "wei", val age: Int) {
}
另外只有添加了 open 的函数才能被子类重写,open不能与private一起用(报错且自相矛盾)
接口
在Kotlin中定义接口需要注意以下几点:
1.接口不能定义构造函数,否则编译器会报错
2.接口的内部方法通常要被实现它的类进行重写,所以这些方法默认为抽象类型
3.与Java不同的是,Kotlin允许在接口內部实现某个方法,而1.8之前Java接口的所有内部方法都 必须是抽象方法
4.如果接口中定义了一个属性,实现接口的类必须也重写这个属性
interface CallBack {
var status: Boolean
}
这样
class MyBean2(override var status: Boolean) :CallBack{
}
这样
class MyBean2 :CallBack{
override var status: Boolean
get() = true
set(value) {}
}
或者这样
class MyBean2 :CallBack{
override var status: Boolean = true
}
嵌套类和内部类
和Java唯一的区别就是调不到外部类的成员变量
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
//嵌套类
class A {
}
}
而嵌套类加上inner关键字就可变成内部类,同时可以访问外部类成员变量
class MainActivity : AppCompatActivity() {
var name: String = "wei"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
//内部类
inner class A {
fun a() {
name
}
}
}
枚举类
加上enum关键字即可
enum class Day {
Day6,Day7
}
枚举类也存在构造函数
enum class Day( val theName: String) {
Day6("星期六"), Day7("星期天")
}
获取信息
val theName = Day.Day7.theName
通过ordinal获取下标
val index = Day.Day7.ordinal
密封类
密封类就像是一种更加严格的枚举类,它内部有且仅有自身的实例对象,所以是一个有限的自身实例集合。或者说,密封类采用了嵌套类的手段,它的嵌套类全部由自身派生而来。定义密封的时候,需要在该类的class前面加上关键字sealed作为标记。
有了密封类,外部使用when语句便无须指定 else分支了!
sealed class Day {
//密封类的每个嵌套类都必须继承该类
class Day6(var theName: String) : Day()
class Day7(var theName: String) : Day()
}
fun eval(day: Day) =
when (day) {
is Day.Day6 -> day.theName
is Day.Day7 -> day.theName
}
注意,密封的类无法实例化。
var day:Day = Day.Day7("星期天")
eval(day)
数据类
在class前面增加关键字data,并声明拥有完整输入参数的构造函数,即可无缝实现以下功能:
1.自动声明与构造函数入参同名的属性字段
2.自动实现每个属性字段的get/set方法
3.自动提供equals()方法,用于比较两个数据对象是否相等
4.自动提供copy方法,允许完整复制某个数据对象,也可在复制后单独修改某几个字段的值
5.自动提供toString方法,用于打印数据对象中保存的所有字段值
数据类必须有主构造函数,且至少有一个输入参数
要声明与输入参数同名的属性,即输入参数前面添加关键字var/val
数据类不能是基类也不能是子类,不能是抽象类,也不能是内部类,更不能是密封类
data class UserBean(var username: String, var password: String) {
data class Bean(var age: Int)
}
模板类
class MyBean4<T>(name: T, age: T) {
fun method(a: T, b: T) {
}
}
var myBean4: MyBean4<String> = MyBean4("", "")
序列化传值(Parcelable)
主构造添加属性,实现Parcelable后重写方法,不用你自己敲,放心
class MyMessage(var a: String?, var b: Int) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readInt()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(a)
parcel.writeInt(b)
}
override fun describeContents(): Int {
return 0
}
override fun toString(): String {
return "MyMessage(a=$a, b=$b)"
}
companion object CREATOR : Parcelable.Creator<MyMessage> {
override fun createFromParcel(parcel: Parcel): MyMessage {
return MyMessage(parcel)
}
override fun newArray(size: Int): Array<MyMessage?> {
return arrayOfNulls(size)
}
}
}
放心,Kotlin中还有更简单的方式
主构造添加属性,实现Parcelable,再加上@Parcelize注解,简单的令人发指:)
@Parcelize
class MyMessage2(var a: String, var v: Int) : Parcelable {
}
接下来就是跳转传值,还记得Map初始化里的 to 和 Pair 吗?
var myMessage: MyMessage = MyMessage("IU", 1993)
startActivity<Main2Activity>("a" to myMessage,Pair("b","bb"))
跳转+传参+启动模式 这就是Anko库带来的便捷
startActivity(intentFor<Main2Activity>("name" to "wei").singleTop())
获取值
val bundle = intent.extras
val myMessage2 = bundle!!.getParcelable<MyMessage2>("a")
若想正常编译,得打开一个扩展,写上面时开关没设置…
在模块的build.gradle
androidExtensions {
experimental = true
}
提示框
提醒对话框(AlertDialog)
anko库的使用让kotlin更简洁
alert("未来可期", "提示") {
negativeButton("取消") { }
positiveButton("确定") { toast("未来可期") }
}.show()
效果
下拉框(Spinner)
val listMonth: List<String> = listOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12")
selector("请选择月份", listMonth) { dialogInterface, i -> toast("${listMonth[i]}") }
效果