目录
4、null 处理 !!. 不能为空 ?.为空不处理 ?:为空处理成
1、基本数据类型(6种,比java少了boolean/char)
5、伴生对象 companion object 相当于Java 静态(修饰符单例)
1、block: () -> T block: (T) -> T block: T.() -> T
十四、Kotlin中let、run、with、apply及also的差别
1、let public inline fun T.let(block: (T) -> R): R ,>
2、run “this”作为上下文对象,且它的调用方式与let一致。
3、with public inline fun with(receiver: T, block: T.() -> R): R ,>
4、apply public inline fun T.apply(block: T.() -> Unit): T
5、also public inline fun T.also(block: (T) -> Unit): T
一、Kotlin 基础语法
1、方法函数
/**
* fun : 方法关键字
* sum :方法名
* a:Int : 传参及参数类型
* b:Int : 传参及参数类型
* :Int : 返回值
*/
fun sum(a:Int,b:Int) :Int{
return a+b
}
2、常量 val 和变量 var
3、${} 字符串模板
示例
object Main {
private val name :String = "小明"
@JvmStatic
fun main(args: Array<String>) {
val newStr = "这是小明"
val newStr2 = "这是${name}"
println(newStr)
print(newStr2)
}
}
打印日志如下:
4、null 处理 !!. 不能为空 ?.为空不处理 ?:为空处理成
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val age1: String? = null// ? 表示可为空
val age2: String? = "23"
// !!. 不能为空
val age3 = age1!!.toInt()//做了非空判断,传入null则抛出null异常
// ?.为空不处理
val age4 = age1?.toInt() // 相当于 age1?.toInt()== null -> age4 = age1?.toInt() -> val age4 : String? = null
// ?:为空处理成
val age5 = age1?.toInt() ?: "666" // 相当 age1?.toInt()== null -> if(null ==age1?.toInt() ) return "666"
}
}
5、is 类型转换 相当于 instanceof
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
changeType("100")
changeType(99)
}
private fun changeType(obj: Any) {
if (obj is String) {
println("${obj}长度是${obj.length}")
} else {
println("非 String 类型")
}
}
}
日志如下:
6、Any 相当于 Java的 Object
二、Kotlin 基本数据类型
1、基本数据类型(6种,比java少了boolean/char)
对比Java
Java基本类型共有八种,基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double。数值类型又可以分为整数类型byte、short、int、long和浮点数类型float、double
2、数据类型转换
3、字符 `c`
示例
4、数组arrayOf、arrayListOf
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val array = arrayOf(1, 2, 3)
val arrayList = arrayListOf<Person>()
arrayList.add(Person("xiaoming"))
arrayList.add(Person("xiaohong"))
for (a in array) {
println(a.toString())
}
//遍历方法1:in
for (p: Person in arrayList) {
println(p.name)
}
//遍历方法2:forEach
arrayList.forEach {
println(it.name)
}
//遍历方法3:withIndex()
for ((index, value) in arrayList.withIndex()) {
println("index:${index}, value:${value.name}")
}
//遍历方法4:indices
for (index in arrayList.indices) {
println(arrayList[index].name)
}
}
class Person(val name: String) {
}
}
日志如下:
5、字符串换行 """xxx"""
以为kotlin不支持加号字符串换行
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val str = "hello " +
"world " +
"!"
println(str)
}
}
日志如下:
所以需要"""xxx"""来实现字符串换行显示
示例: trimIndent() 去除前置空格
object Main {
@JvmStatic
fun main(args: Array<String>) {
val str = """
Hellow
World
!
""".trimIndent()
println(str)
}
}
日志如下:
三、Kotlin 条件控制
1、if语句
kotlin没有Java的三元运算
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val a: Int = 100
val b: Int = 99
val max: Int = if (a > b) a else b
val max2: Int = if (a < b) {
println("a<b")//先执行
a
} else {
println("a>b")//先执行
b
}
println(max)//后执行
println(max2)//后执行
}
}
日志如下
2、判断是否在区间内 in
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val a = 2
val b = 10
if (a in 5..10) {
println("a 在 5到10 区间")
} else {
println("a 不在 5到10 区间")
}
if (b in 5..10) {
println("b 在 5到10 区间")
} else {
println("b 不在 5到10 区间")
}
}
}
日志如下
3、When 表达式(相当于 Java switch语句)
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val a = 3
when(a){
1 -> print("1")
2 -> print("2")
else -> print("0")
}
}
}
如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val a = 3
when(a){
1,2,3,4 -> print("1")
5 -> print("2")
else -> print("0")
}
}
}
四、Kotlin 循环控制
1、for循环 ,withIndex() indices forEach{}
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val arrayList = arrayListOf<String>("1", "2", "3")
for (a in arrayList) {
println(a)
}
//withIndex() 带位置标示
for ((index, value) in arrayList.withIndex()) {
println("index $index value is $value")
}
//indices
for (index in arrayList.indices) {
println("index $index value is ${arrayList[index]}")
}
//
arrayList.forEach {
println(it)
}
}
}
日志如下
五、Kotlin 类和对象
1、类
2、属性
3、自定义set/get方法 field 表示当前属性
有时候会对后台返回的对象属性做键值转换,例如 性别sex返回 1:男 2:女,希望getSex时直接返回其对应值
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val person = Person()
person.name = "xiao ming"
person.age = 18
person.sex = "1"
println(person.name)
println(person.age)
println(person.sex)
}
}
class Person() {
//field 表示当前属性
var name: String? = null
get() = field?.toUpperCase()
var age: Int = 0
set(value) {
if (value >= 18) {
field = value - 10
} else {
field = value + 10
}
}
var sex: String? = null
get() = if ("1" == field) {
"男"
} else {
"女"
}
}
日志如下
4、 lateinit 延迟初始化关键字
非空属性必须在定义的时候初始化
object Main {
@JvmStatic
fun main(args: Array<String>) {
}
class Person(val name:String){
lateinit var bean:String //定义变量不初始化会报错时 则可以使用 lateinit 确定后面一定会初始化
}
}
5、抽象类
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val animalA = Cat()
val animalB = Dog()
animalA.say()
animalB.say()
}
}
class Cat : Animal() {
override fun say() {
println("喵~")
}
}
class Dog : Animal() {
override fun say() {
println("汪~")
}
}
//抽象类
abstract class Animal() {
abstract fun say();//抽象方法
}
日志如下
6、嵌套类
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val man = Person.Man()
man.name = "xiao ming"
println(man.name)
}
}
class Person(){
//嵌套类
class Man(){
var name: String? = null
}
}
日志如下
7、内部类 inner (可使用外部类属性和方法)
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val outer = Outer()
val inner = outer.Inner()
val demo = inner.foo()
println(demo) // 1
val demo2 = inner.innerTest()
println("被修改的outer.v = ${outer.v}") // 内部类可以引用外部类的成员,例如:成员属性
}
}
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
val o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
o.v = "111"
println("内部类可以修改引用外部类的成员,例如:" + o.v)
}
}
}
日志如下
8、类的修饰符
六、Kotlin 继承
1、可以被继承的类都要用 open修饰符
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val p = Person("xiao ming")
println(p.name)
println(p.str)
}
}
class Person(val name: String) : Base("child is Person, child name is $name") {
}
//open 关键词 允许当前类被继承
open class Base(val str: String) {
}
日志如下
2、构造函数
1)子类有构造函数,则要在子类构造初始化父类构造
示例
class Person(name: String, age: Int, sex: String) : Base(name) {
}
//open 关键词 允许当前类被继承
open class Base(name: String) {
}
2)子类无构造函数,则需要使用 constructor():super()
示例
class Person : Base {
constructor():super("person")
}
//open 关键词 允许当前类被继承
open class Base(val name: String) {
}
3、重写 override
1)在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val p = Person()
p.say("xiao ming")
}
}
class Person : Base("Person") {
override fun say(name: String) {// 重写方法
println("person $name")
}
}
//open 关键词 允许当前类被继承/重写
open class Base(val name: String) {
open fun say(name: String) {// 允许重写方法
println("base name")
}
}
日志如下
2)super<A>.f() 如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val c = C()
c.f()
}
}
open class A {
open fun f() { println("A") }
fun a() { println("a") }
}
interface B{
fun f(){ println("B")}
fun b(){ println("b") }
}
class C() :A(),B{
override fun f() {
super<A>.f()//调用 A.f()
super<B>.f()//调用 B.f()
}
}
日志如下
七、Kotlin 接口
1) 接口方法可以提前实现
2) 接口属性被继承时必须赋值
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
val p = Person()
println(p.name)
p.bar()
p.foo()
}
}
class Person : PersonInterface {
override var name: String = "xiao ming"
override fun bar() {
println("bar")
}
}
interface PersonInterface {
var name: String//接口属性,接口属性不赋值,继承接口必须赋值
fun bar()
fun foo() {
println("foo")//接口可以提前实现方法体
}
}
日志如下
八、Kotlin 扩展
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
1、扩展函数
示例
object Main {
@JvmStatic
fun main(args: Array<String>) {
Person("xiao ming").say()//拓展函数调用
}
}
//拓展Person类,提供 say()函数
fun Person.say() {
println("我是拓展函数:$name")
}
class Person(val name: String) {}
日志如下
2、扩展一个空对象
在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。例如:
object Main {
@JvmStatic
fun main(args: Array<String>) {
val t :Any? = null
println(t.toString())
}
}
fun Any?.toString(): String {
if (null == this) {
return "null"
}
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
return toString()
}
日志如下
3、扩展函数是静态解析的(注意)
object Main {
@JvmStatic
fun main(args: Array<String>) {
printFoo(D())
}
}
open class C
class D : C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())//拓展函数是静态,所以不管传值什么,都会调用 fun C.foo() = "c"
}
日志如下:
若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。
object Main {
@JvmStatic
fun main(args: Array<String>) {
D().foo()
}
}
class D{
fun foo(){ println("成员函数") }
}
fun D.foo(){ println("拓展函数") }
日志如下:
4、拓展属性
扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。
object Main {
@JvmStatic
fun main(args: Array<String>) {
val d = D()
d.name = "name"
println(d.name)
println(d.name2)
}
}
class D {
var name2: String? = null
}
//拓展属性
var D.name: String
get() {
return "xiao ming"
}
set(value) {
println("拓展属性")
this.name2 = "name2 :$value"
}
日志如下:
5、伴生对象 companion object 相当于Java 静态(修饰符单例)
object Main {
@JvmStatic
fun main(args: Array<String>) {
D.foo()//拓展函数
println(D.name)//拓展属性
//companion object 演示
D.A.age
D.A.say()
D.age//静态属性,可以直接调用
D.say()//静态方法,可以直接调用
}
}
class D {
object A {
val age = "10"
fun say() {
println("age:10")
}
}
companion object {
val age = "20"
fun say() {
println("age:20")
}
}
}
fun D.Companion.foo() {
println("伴生对象的扩展函数")
}
val D.Companion.name: String
get() = "伴生对象的扩展属性"
日志如下:
九、Kotlin 数据类与密封类
1、数据类 data
copy()函数使用示例(复制数据的同时,可以修改数据内容)
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user1 = User("xiaoming", 18)
val user2 = user1.copy(age = 20)
println("user1: name=${user1.name},age=${user1.age}")
println("user2: name=${user2.name},age=${user2.age}")
}
}
data class User(val name: String, val age: Int)
日志如下:
十、Kotlin 泛型
泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。
与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
1、泛型类
object Main {
@JvmStatic
fun main(args: Array<String>) {
val box01 = Box<String>("xiao ming")
val box02 = Box<Int>(20)
println(box01.value)
println(box02.value)
}
}
//泛型类
class Box<T>(t: T) {
val value = t
}
日志如下:
2、泛型函数
object Main {
@JvmStatic
fun main(args: Array<String>) {
doSomething(100)
doSomething("xiao ming")
doSomething(true)
doSomething(15.55)
}
//泛型函数
fun <T> doSomething(t: T) {
when (t) {
is Int -> println("泛型是Int型")
is String -> println("泛型是String型")
is Boolean -> println("泛型是Boolean型")
else -> println("未判断的类型")
}
}
}
日志如下:
十一、Kotlin 对象表达式和对象声明 object
1、对象表达式
object Main {
@JvmStatic
fun main(args: Array<String>) {
//object: 对象表达式
val listener = object : Listener{
override fun onClick() {
println("onClick")
}
}
listener.onClick()
}
}
interface Listener{
fun onClick()
}
日志如下:
2、对象声明(获取单例)
Kotlin 使用 object 关键字来声明一个对象。
Kotlin 中我们可以方便的通过对象声明来获得一个单例。
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user01 = User
val user02 = User
user01.name = "xiao ming"// object对象是单例,所以 user02 值也会改变
println(user01.name)
println(user02.name)
}
}
object User{
var name = ""
var age = 20
}
日志如下:
十二、kotlin 委托 by
1、委托类
类委托的核心思想在于将一个类的具体实现委托给另一个类去完成。
意义在于让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法。
object Main {
@JvmStatic
fun main(args: Array<String>) {
val a = Dog("wangwang")
BlackDog(a).print()
BlackDog(a).dogPrint()
}
}
interface Animal {
fun print()
}
class Dog(val name: String) : Animal {
override fun print() {
println("name:${name}")
}
}
//委托类 BlackDog 委托给 Animal
class BlackDog(a: Animal) : Animal by a {
//实现自己的方法
fun dogPrint(){
println("dogPrint")
}
}
日志如下
2、属性委托
委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。
import kotlin.reflect.KProperty
object Main {
@JvmStatic
fun main(args: Array<String>) {
val dog = Dog()
dog.name = "dog"
println(dog.name)
}
}
class Dog{
//委托属性
var name: String by Animal()
}
class Animal{
operator fun getValue(dog: Dog, property: KProperty<*>): String {
return "委托属性${property}为wangwang"
}
operator fun setValue(dog: Dog, property: KProperty<*>, s: String) {
println("property:${property}, value:${s}")
}
}
日志如下:
3、懒加载 by lazy{ }
by lazy
并不是连在一起的关键字,只有by
才是Kotlin
中的关键字,lazy
在这里只是一个高阶函数而已。
object Main {
@JvmStatic
fun main(args: Array<String>) {
println(lazyValue) // 第一次执行,执行两次输出表达式
println(lazyValue) // 第二次执行,只输出返回值
}
val lazyValue: String by lazy {
println("computed!") // 第一次调用输出,第二次调用不执行
"Hello"
}
}
日志如下:
4、可观察属性 Observable
observable 可以用于实现观察者模式。
Delegates.observable() 函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。
在属性赋值后会执行事件的响应器(handler),它有三个参数:被赋值的属性、旧值和新值:
import kotlin.properties.Delegates
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user = User()
user.name="第一次赋值"
user.name="第二次赋值"
}
}
class User{
var name:String by Delegates.observable("初始值"){
property, oldValue, newValue ->
println("旧值:$oldValue -> 新值:$newValue")
}
}
日志如下:
5、map/mutableMap
object Main {
@JvmStatic
fun main(args: Array<String>) {
val map = mapOf<String, String>("name" to "xiaoming", "name2" to "xiaoming2")
val map2 = mutableMapOf<String, String>("name3" to "xiaoming3", "name4" to "xiaoming4")
map2.put("name5", "xiaoming5")
println(map["name"])
println(map["name2"])
println(map2["name3"])
println(map2["name4"])
println(map2["name5"])
}
}
日志如下:
十三、kotlin 回调 block:
1、block: () -> T block: (T) -> T block: T.() -> T
object Main {
@JvmStatic
fun main(args: Array<String>) {
"==1==".method1 {
println("==1==")
}
"==2==".method2 {
println("==2==this:$it")
it
}
"==3==".method3 {
println("==3==this:$this")
this
}
}
/**
* 1. block: () -> T
* 01.传入的函数无参
* 02.该函数最后一行需要是调用者对象类型,而且无return
*/
fun <T> T.method1(block: () -> T): T {
return block()
}
/**
* 2. block: (T) -> T
* 01.传入的函数带有自身作为参数
* 02.该函数最后一行需要是调用者对象类型,而且无return
* 03.把调用者作为it,传入定义的lambda表达式函数域中
*/
fun <T> T.method2(block: (T) -> T): T {
return block(this)
}
/**
* 3. block: T.() -> T
* 01.传入的函数无参
* 02.该函数最后一行需要是调用者对象类型,而且无return
* 03.把调用者作为this,传入定义的lambda表达式函数域中
*/
fun <T> T.method3(block: T.() -> T): T {
println("=======3==this:$this")
return block()
}
}
日志如下:
2、Block语法格式
object Main {
@JvmStatic
fun main(args: Array<String>) {
test {
println("test:无返回值")
}
test2 {
"test2:这是返回值"//不需要写 return
}
test3 {
println("test3:${it}")
}
test4 {
println("test4:${it}")
"test4:这是返回值"//不需要写 return
}
test5 { x, y ->
x + y//不需要写 return
}
}
//1、无参 无返回值
fun test(block: () -> Unit) {
return block()
}
//2、无参 有返回值
fun test2(block: () -> String) {
println(block())
}
//3、有参 无返回值
fun test3(block: (s: String) -> Unit) {
block("这是入参")
}
//4、有参 有返回值
fun test4(block: (s: String) -> String) {
println(block("这是入参"))
}
//5、有多个参数,有返回值
fun test5(block: (x: Int, y: Int) -> Int) {
println(block(5, 10))
}
}
日志如下:
十四、Kotlin中let、run、with、apply及also的差别
作用域函数是Kotlin比较重要的一个特性,共分为以下5种:let、run、with、apply 以及 also。它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。
1、let public inline fun <T, R> T.let(block: (T) -> R): R
1)作用域 let 有入参,有返回值it
object Main {
@JvmStatic
fun main(args: Array<String>) {
//let 作用于 ,直接用 it ,有返回值
val user = User().let {
it.name = "xiao ming"
it.age = 20
"这是返回值R"
}
val user2 = User().let {
it.name="zhang san"
}
println(user)
println(user2)
}
}
class User() {
var name: String? = null
var age: Int? = null
fun say() {
println("name :${name}, age :${age}")
}
}
日志如下:
2) 空安全检测 xxx?.let{ }
object Main {
val name: String? = null
@JvmStatic
fun main(args: Array<String>) {
val result = name?.let {
it.length
} ?: "name 空处理"
println(result)
}
}
日志如下:
2、run “this”作为上下文对象,且它的调用方式与let一致。
表达式同时包含对象初始化和返回值的计算时,run更适合
public inline fun <T, R> T.run(block: T.() -> R): R
public inline fun <R> run(block: () -> R): R
无返回值
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user = User()
user.run{
this.name = "xiaoming"
this.age = 20
say()
}
println(user)
}
}
class User() {
var name: String? = null
var age: Int? = null
fun say() {
println("name :${name}, age :${age}")
}
}
日志如下:
3、with public inline fun <T, R> with(receiver: T, block: T.() -> R): R
with属于非扩展函数,直接输入一个对象receiver,当输入receiver后,便可以更改receiver的属性,同时,它也与run做着同样的事情。
with使用的是非null的对象,当函数块中不需要返回值时,可以使用with。
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user = User()
with(user) {
this.name = "xiaoming"
this.age = 20
say()
}
println(user)
}
}
class User() {
var name: String? = null
var age: Int? = null
fun say() {
println("name :${name}, age :${age}")
}
}
日志如下:
4、apply public inline fun <T> T.apply(block: T.() -> Unit): T
apply函数主要用于初始化或更改对象,因为它用于在不使用对象的函数的情况下返回自身。
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user = User()
user?.apply{
this.name = "xiaoming"
this.age = 20
}
println(user)
}
}
class User() {
var name: String? = null
var age: Int? = null
fun say() {
println("name :${name}, age :${age}")
}
}
日志如下:
5、also public inline fun <T> T.also(block: (T) -> Unit): T
also是 T 的扩展函数,返回值与apply一致,直接返回T。also函数的用法类似于let函数,将对象的上下文引用为“it”而不是“this”以及提供空安全检查方面。
object Main {
@JvmStatic
fun main(args: Array<String>) {
val user = User()
user?.also {
it.name = "xiaoming"
it.age = 20
}
println(user)
}
}
class User() {
var name: String? = null
var age: Int? = null
fun say() {
println("name :${name}, age :${age}")
}
}
日志如下: