kotlin官方学习网站:https://developer.android.google.cn/kotlin/add-kotlin
.kt编译成.java
tools--->kotlin--->Show Kotlin Bytecode-->Decompile
.java转换成.kt
code--->Convert JavaFile to Kotlin File
Kotlin 基础语法
1.Var 与 Val
var 关键字: 可变变量
var <标识符> : <类型> = <初始化值>
// 可以改,可以读 get set 如:var info1 : String = "A"
val 关键字: 不可变变量,只能赋值一次的变量(有一点点类似Java中final修饰的变量)
val <标识符> : <类型> = <初始化值>
// 只能读, 只有 get 如:val info2 : String = "B"
2.函数
//普通函数 返回类型 == 类型推导 Int
fun add2(number1: Int, number2: Int) = number1 + number2
// 可变参数 (可变长 参数函数)vararg关键字
fun lenMethod(vararg value: Int) {
for (i in value) {
println(i)
}
}
3.字符串模板
// $ 表示一个变量名或者变量值
// $varName 表示变量值
// ${varName.fun()} 表示变量的方法返回值
4.NULL检查机制
// Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,
// 有两种处理方式,字段后加!!像Java一样抛出空异常,
如:var info: String? = null println(info!!.length)//!! 我自己负责info 不会为null == (不管null不null必须执行) == Java
另一种字段后加?
如:var info: String? = null println(info?.length) // ? 如果info是null,就不执行 .length
// ?: 如果值为null 就输出设置的字符串 “你很牛逼”
如: var info: String? = null
println(info?.length ?: "你很牛逼")//打印值为:你很牛逼
5.区间
// 1 到 9
for (i in 1..9) {
// println(i)
}
// 不会输出
for (i in 9..1) {
// println(i)
}
// 大 到 小
for (i in 9 downTo 1) {
// println(i)
}
// 用区间做判断
val value = 88
if (value in 1..100) {
// println("包了 1 到 100")
}
// 步长指定
for (i in 1..20 step 2) {
// 1 3 5 7 ...
// println(i)
}
// 排除 最后元素
for (i in 1 until 10) {
println(i)
}
6.内联函数
参考:Kotlin之inline(内联)_kotlin inline-CSDN博客
- inline
在使用高阶函数(特别是函数的参数类型为函数类型)的时候一般加上inline,因为我们定义一个高阶函数后 没有办法去全面考虑其调用处是否会出现在循环当中
- noinline
在内联函数中,某个函数参数不需要被内联时,使用noinline
- crossinline
集合
1.List集合
val list = ArrayList<String>()
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Pear")
list.add("Grape")
列表初始化:
val list2 = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape")
遍历操作:
for (fruit in list2) {
println(fruit)
}
内置函数**listOf() & mutableListOf() **
listOf() 函数: 创建的是一个不可变集合(即,只能用于读取,无法进行添加、修改、删除操作)
mutableListOf() : 创建的是一个可变集合(可添加删除和修改)
2. Set集合
val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
for (fruit in set){
println(fruit)
}
和List集合基本一致,不同的是:Set是不可以放重复元素(会去重)
3. Map集合
val map = HashMap<String, Int>()
map.put("Apple", 1)
map.put("Banana", 2)
map.put("Orange", 3)
map.put("Orange", 4)
map.put("Orange", 5)
println(map["Orange"])
map出现重复的键时,以最新的为准
遍历:
for((fruit, map) in map) {
println("fruit: $fruit $map")
}
并且只允许存在最新的那个键值对
推荐使用以下方法来初始化Map元素:
val map = HashMap<String, Int>()
map["Apple"] = 1
map["Banana"] = 2
读取:
val number = map["Apple"]
mapOf()和 mutableMapOf()
val map2 = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
for((fruit, num) in map2) {
println("fruit: $fruit $num")
}
二者区别同listOf() & mutableListOf() ,mapOf()是不可变的,mutableMapOf()是可变的
而mapOf() 的to并不是关键字,而是一个infix函数
4. 集合的函数式API语法结构
需求:找到list集合中单词长度最长的水果
val list4 = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
var maxLengthFruit = ""
for(fruit in list4) {
if(fruit.length > maxLengthFruit.length) {
maxLengthFruit = fruit
}
}
println("max length fruit is $maxLengthFruit")
使用函数式API
val list4 = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
// 使用函数式API
var maxLengthFruit = list4.maxBy { it.length }
println("max length fruit is $maxLengthFruit")
{参数名1: 参数类型, 参数名2:参数类型 -> 函数体}
val list4 = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
var lambda = {fruit : String -> fruit.length}
var maxLengthFruit = list4.maxBy(lambda)
以上是未简化的lambda表达式
简化1:Kotlin规定,Lambda参数是函数最后一个参数时, 可以把Lambda表达式移到函数括号外面:
var maxLengthFruit = list4.maxBy(){fruit : String -> fruit.length}
简化2:Lambda参数时函数的唯一一个参数的话,可以省略括号:
var maxLengthFruit = list4.maxBy{fruit : String -> fruit.length}
简化3:Kotlin的类型推导机制,可以不用声明参数类型
var maxLengthFruit = list4.maxBy{fruit -> fruit.length}
简化4:当Lambda表达式的参数列表只有一个参数时,不必声明参数名,可以用it关键字代替
var maxLengthFruit = list4.maxBy{it.length}
完毕
同样Java的函数式API(单抽象方法接口为参数)也可以有以上的简化过程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running");
}
}).start();
转Kotlin:
Thread(object : Runnable {
override fun run() {
println("Thread is running")
}
}).start()
简化1:省略run方法(因为Runnable接口只有一个方法)和参数类型
Thread(Runnable {
println("Thread is running")
}).start()
简化2:省略接口名
Thread({
println("Thread is running")
}).start()
简化3:Lambda方法是最后一个参数,可以提到括号外面,同时如果该参数数唯一参数,省略括号
Thread{
println("Thread is running")
}.start()
同理,控件的监听事件也可以如此法简化,最终结果:
button.setOnClickListener {
TODO("sfsfs")
}
比较与数组
1.比较两个值
//比较值本身 == 等价 Java的equals
// 比较对象地址 ===
2.数组
// 第一种形式
val numbers = arrayOf(1, 2, 3, 4, 5, 6, 7, 8)
println(numbers[0])
println(numbers[7])
for (number in numbers) {
println(number)
}
// 第二种形式 value=0
val numbers2 = Array(6, {value: Int -> (value + 200) })
for (value in numbers2) {
println(value) //打印值:200 201 202 203 204 205
}
条件与控制
1.条件
// 表达式 比 大小 最大值
val maxValue = if (number1 > number2) number1 else number2
2.when运用
/* val number = 5
when(number) {
1 -> println("一")
2 -> println("二")
3 -> println("三")
4 -> println("四")
5 -> println("五")
else -> println("其他")
}*/
/*val number = 745
when(number) {
in 1..100 -> println("1..100")
in 200..500 -> println("200..500")
else -> println("其他")
}*/
循环与标签
1.for循环操作
var items = listOf("apple", "banana", "kiwifruit")
for (item in items.indices) {
println("item at $index is ${items[index]}")
}
/*打印
item at 0 is apple
item at 1 is banana
item at 2 is kiwifruit
*/
//或者
for(item in items){
println(item)
}
/*打印
apple
banana
kiwifruit
*/
2.自带标签与系统标签
类与对象
1.父类子类 与 构造函数等
类: public final class Person 默认就是这样的,不能被继承, 可以加open就可以被继承了
Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名后。主构造函数可以省略constructor
open class Person(id: Int) // 主构造
{
// 次构造
constructor(id: Int, name: String) : this(id) {
}
// 次构造
constructor(id: Int, sex: Char) : this(id) {
}
// 次构造
constructor() : this(787) {
}
}
如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在它前面:
public class Customer @Inject constructor(name: String) { }
2.接口和抽象类
接口默认是 public abstract interface
interface Callback {
fun callbackMethod() : Boolean
}
抽象类默认是 public abstract class. // 抽象类,不能创建实例
abstract class Person : Callback , Callback2 {
// 抽象方法 默认就是open(此处省略) 不能实例,必须继承实现
abstract fun getLayoutID() : Int
//抽象类中的普通方法默认是public final的,所以需要标明
open fun initView(){}
fun initData(){}
}
3.data 与 object
不会再修改了,可以是使用val
get set 构造 equals hashCode toString, copy
data class User(var id: Int, val name: String, val sex: Char)
object只实例一次 相当于 单例
object MyEngine {
fun m() {
println("M run")
}
}
4.单例
派生 和 object 尽量选择 object,当需要传参数时用派生(companion)
单例写法1
class APIClient {
private object Holder {
val INSTANCE = APIClient()
}
//派生
companion object {
val instance = Holder.INSTANCE
}
}
//调用
APIClient.instance
单例写法2
class NetManager2 {
companion object {
private var instance: NetManager2? = null
// 返回值:允许你返回null
fun getInstance(): NetManager2? {
if (instance == null) {
instance = NetManager2()
}
// 如果是null,也返回回去了
return instance
// 第二种补救: 我来负责 instance 肯定不为null
// return instance!!
}
}
fun show(name: String) {
println("show:$name")
}
}
fun main() {
// 客户端 来做补救措施
val netManager = NetManager2.getInstance()
// 如果 netManager == null ,就不执行 .show("AAA")
netManager?.show("AAA")
}
5.内部类和嵌套类
嵌套类:无法获取到外部类的引用
open class TestDemo {
private var a:Int = 2
class A{
//这里报错了,嵌套类无法获取到外部类的引用
var b:Int = a
}
}
内部类:添加上 inner 后是内部类,可以获取到外部类的引用
open class TestDemo {
private var a:Int = 2
inner class A{
var b:Int = a
}
}
6.Kotlin中的密封类与常规接口
参考链接:Kotlin中的密封类与常规接口
-
使用密封类:当你想表示一组固定的类型,并希望在编译时确保所有情况都被处理时(例如,用于在状态机中建模不同的状态)。
-
使用接口:当你想定义多个类必须遵守的约定,而不强制执行严格的层次结构时(例如,用于定义不同类之间的共同行为)
-
密封类和枚举类似,但每个都有自己的用例。密封类让我们定义一组带有自定义属性和方法的实例。另一方面,当处理不需要额外状态的类型时,枚举很有用。
7.重载函数
由于在Java中是没有默认值参数的概念,当我们需要从Java中调用Kotlin中的默认值重载函数的时候,必须显示的指定所有参数值。但是这个绝对不是我们想要,否则Kotlin就失去了重载的意义了不能和Java完全互操作。所以在Kotlin给出了另一个方案就是使用**@JvmOverloads**注解这样就会自动生成多个重载方法供Java调用。
///kotlin类
open class TestKotlinDemo {
@JvmOverloads
fun tt(int: Int = 0, string: String = "", string2: String = "") {
}
}
///Java类
public class TestJavaDemo extends TestKotlinDemo{
private void ttt(){
//下面是调用重载方法tt
tt(2);
tt(2,"4");
tt(2,"4","5");
}
}
8.修饰符
和 java 的大多数修饰符是一样的,但是java 默认为 private ,但是kotlin 中默认为 public ,并且 kotlin中没有 包可见,新添加的修饰符为 :internal ,表示只在模块内部可用。
- 在kotlin 中 public 是不可以访问 低可见性 internal 的。
- 另外还要注意的是 protected ,在java 中他的访问范围是 包中,但是在kotlin 中则是 局限到 类 或者子类中,
- 类的扩展函数 是访问不到 private 和 protected 的。
9.初始化 init
初始化的代码可以放到以 init 关键字作为前缀的初始化块中。
open class InitTestDemo(name: String) {
private val customerKey = name
init {
println("打印name:$name")
println("打印customerKey:$customerKey")
}
}
扩展函数
扩展函数允许你向现有类型添加函数而不修改它们。这可以减少样板代码并使代码更直观。
例如,1.考虑一个用于检查字符串是否为强密码的扩展函数:
fun String.isStrongPassword(): Boolean {
// 检查长度
// 检查符号和数字
// 检查强度
// 检查其他内容
// 返回结果
}
//当我们需要使用它时,代码变得更加清晰:
val isPasswordStrongEnough = userPasswordInput.isStrongPassword()
2.withNotNull — 判空扩展函数
inline fun <T : Any, R> T?.withNotNull(block: (T) -> R): R? {
return this?.let(block)
}
//用法示例:
val nullableValue: String? = null
nullableValue.withNotNull { value ->
// 只有在nullableValue不为空时才会执行此处的代码
}
3.notEmpty — 判断集合是否非空:简化了检查集合是否非空的过程
fun <T> Collection<T>?.notEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
//更简洁的写法:
fun <T> Collection<T>?.notEmpty(): Boolean {
return this?.isNotEmpty() == true
}
//用法示例:
val list: List<Int> = emptyList()
if (list.notEmpty()) {
// 只有在list不为空时才会执行此处的代码
}
4. toFormattedString — 格式化数字和日期:简化了格式化数字和日期的过程
fun Int.toFormattedString(): String {
return NumberFormat.getInstance().format(this)
}
fun Long.toFormattedString(): String {
return NumberFormat.getInstance().format(this)
}
fun Date.toFormattedString(): String {
return SimpleDateFormat.getDateInstance().format(this)
}
//用法示例:
val number = 1000000
val formattedNumber = number.toFormattedString()
5.debounce — 延迟执行代码块
debounce扩展函数简化了延迟执行代码块的过程,直到距离上次执行代码块的时间超过指定的时间间隔
fun View.onClick(debounceDuration: Long = 300L, action: (View) -> Unit) {
setOnClickListener(DebouncedOnClickListener(debounceDuration) {
action(it)
})
}
private class DebouncedOnClickListener(
private val debounceDuration: Long,
private val clickAction: (View) -> Unit
) : View.OnClickListener {
private var lastClickTime: Long = 0
override fun onClick(v: View) {
val now = SystemClock.elapsedRealtime()
if (now - lastClickTime >= debounceDuration) {
lastClickTime = now
clickAction(v)
}
}
}
//用法示例:
button.onClick(debounceDuration = 500L) {
// 只有在距离上次点击已经过去500毫秒后才会执行此处的代码
}
6. toBitmap — 将Drawable转换为Bitmap
fun Drawable.toBitmap(): Bitmap {
if (this is BitmapDrawable) {
return bitmap
}
val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
setBounds(0, 0, canvas.width, canvas.height)
draw(canvas)
return bitmap
}
//用法示例:
val drawable = ContextCompat.getDrawable(context, R.drawable.my_drawable)
val bitmap = drawable.toBitmap()
7.applyIf — 根据条件应用代码块:简化了仅在满足条件时将代码块应用于对象的过程
inline fun <T> T.applyIf(condition: Boolean, block: T.() -> Unit): T {
return if (condition) {
this.apply(block)
} else {
this
}
}
//用法示例
val number = 5
val formattedNumber = number.applyIf(number > 10) {
toFormattedString()
}
lambda表达式
fun main() {
// () -> Unit 空参函数 并且 没有返回值 函数名=method01
// TODO 定义没有问题,调用不行
/* var method01 : () -> Unit
method01() // 不能调用 没有具体的实现
var method02 : (Int, Int) -> Int//不能调用
method02(9, 9)
var method03: (String, Double) -> Any?//不能调用
method03("Derry", 543354.4)
var method04 : (Int, Double, Long, String ? ) -> Boolean//不能调用
method04(1, 545, 3543, null)
var method05 : (Int, Int) -> Int//不能调用
method05(9, 9)*/
// : (形参类型)
// = {具体详情}
// TODO 定义没有问题,调用OK ,因为有实现了
var m06 : (Int, Int) -> Int = {number1, number2 -> number1 + number2}
println("m06:${m06(9, 9)}")
var m07 = { number1: Int , number2: Int -> number1.toDouble() + number2.toDouble()}
println("m07:${m07(100, 100)}")
var m08 : (String, String) -> Unit = {aString, bString -> println("a:$aString, b:$bString")}
m08("李元霸", "王五")
var m09 : (String) -> String = {str -> str}
println("m09:${m09("降龙十八掌")}")
var m10 : (Int) -> Unit = {
when(it) {
1 -> println("你是一")
in 20..30 -> println("你是 二十 到 三十")
else -> println("其他的数字")
}
}
m10(29)
var m11 : (Int, Int, Int) -> Unit = { n1, n2, n3 ->
println("n1:$n1, n2:$n2, n3:$n3")
}
m11(29,22,33)
var m12 = { println("我就是m12函数,我就是我") }
m12()
val m13 = {sex: Char -> if (sex == 'M') "代表是男的" else "代表是女的"}
println("m13:${m13('M')}")
// 覆盖操作
var m14 = {number: Int -> println("我就是m14 我的值: $number")}
m14 = {println("覆盖 我的值: $it")}
m14(99)
// 需求:我想打印, 并且,我还想返回值,就是 我什么都想要
var m15 = { number: Int -> println("我想打印: $number")
number + 1000000//注意这里要换行
}
println("m15:${m15(88)}")
}
///下面是打印结果
m06:18
m07:200.0
a:李元霸, b:王五
m09:降龙十八掌
你是 二十 到 三十
n1:29, n2:22, n3:33
我就是m12函数,我就是我
m13:代表是男的
覆盖 我的值: 99
我想打印: 88
m15:1000088
Process finished with exit code 0