改不完的 Bug,写不完的矫情。公众号 杨正友 现在专注移动基础平台开发 ,涵盖音视频, APM和信息安全等各个知识领域;只做全网最 Geek 的公众号,欢迎您的关注!精彩内容不容错过~
一. kotlin 简介
Kotlin 拥有强大的生态,因为 Kotlin 是一门跑在 Java 虚拟机上的函数式语言,完全符合 JVM 的设计规范,如: 类型擦除,装箱和拆箱等等。所以也可以像 Java 一样支持 Android 原生环境开发,服务端,甚至大前端方向。那么 Kotlin 和 Java 有什么共同点呢?
二. kotlin 和 java8 的相同点
kotlin 和 java8 的相同点我总结了三个,第一个是: 它们都是面向对象和函数式编程语言,第二个是: 它们都是强类型静态语言,最后一个就是它们都符合 JVM 设计规范
![55d112705c0326e87bf009cbaf0a36de.png](https://i-blog.csdnimg.cn/blog_migrate/b2bf946b0dc63a4ac0ec1aaeef0bdc5c.jpeg)
说完共同点我们再聊聊他们的不同点吧~
三. kotlin 和 java8 的不同点
kotlin 和 java8 的不同挺多的,我简单的归纳了一下:
- Kotlin 可以用操作符简洁的判空
- Kotlin 不需要 findViewById
- Kotlin 默认给你写好 setter 和 getter 方法了
- Kotlin 去掉了 static 关键字
- Kotlin 进行 自动强制类型转换
- Kotlin 省去了 类型推断
- Kotlin 不支持类型检查
- Kotlin 有协程支持
![57fb2b6fed5a503129a03873bc010f29.png](https://i-blog.csdnimg.cn/blog_migrate/4f4f2a6a33bf6747301590c34aaecd21.jpeg)
四. kotlin 和 java 互转
4.1 kotlin 转 java
选中导航栏: 右键 -> Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile
![577b8f40266633ec0450e2db7ac9a134.png](https://i-blog.csdnimg.cn/blog_migrate/b85045cd5cccd869786184a0945b7848.jpeg)
![5baf1acad31d2d6bfcf79d1522df6787.png](https://i-blog.csdnimg.cn/blog_migrate/10703f0c33b6d118584a03b0cf58fa7d.jpeg)
4.2 java 转 kotlin
选中文件标签: 右键 -> Convert Java File To Kotlin File
![f03aded645122282a4a5765b2ce499c2.png](https://i-blog.csdnimg.cn/blog_migrate/e954ddc906696b61096e7e5e8865d25d.jpeg)
查看 Kotlin 字节码
![9c53302f5449df66b2611efb207523a9.png](https://i-blog.csdnimg.cn/blog_migrate/f86e8ee35973887f84c7c11df43bba75.jpeg)
五. Kotlin 基础语法
5.1 kotlin 方法
5.1.1 kotlin 方法声明
Kotlin 定义方式比较简单,括号中是参数,格式: "函数名(参数名称: 参数): 返回类型",返回类型定义在括号外 如:T
fun <T> reflectField(instance: Any?, name: String): T? {
if (instance == null) return null
try {
val field = instance.javaClass.getDeclaredField(name)
field.isAccessible = true
return field.get(instance) as T
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
5.1.2 kotlin 方法调用
![3d5a79891d51584fd01f66c378e89772.png](https://i-blog.csdnimg.cn/blog_migrate/c259f2cb29d076d1d1e0602b8923d346.png)
![15c3589d8e2d2f07143a41cfd73a3f34.png](https://i-blog.csdnimg.cn/blog_migrate/3c7a56491c1d39275b438df892c42072.jpeg)
5.1.3 kotlin 方法自动传参
Kotlin 可以在 构造方法或者相关函数大括号里面设置默认参数,这样传参就可以使用默认参数了,我们具体看看怎么使用的吧~
![04c51e117ac0479c4b3643d4757edfa1.png](https://i-blog.csdnimg.cn/blog_migrate/19b92979c784e79e7d2b9ee203c240cd.jpeg)
第二个参数可以省略,此时会使用默认值
![4850e0c69d9ab73e1afb99dc478bb055.png](https://i-blog.csdnimg.cn/blog_migrate/b4eead3f4651e4d1ea8f4532e384731f.jpeg)
![1cf4f7c045dab9ff6f468f4d525b0587.png](https://i-blog.csdnimg.cn/blog_migrate/f19467dc4352fc9d8a468791b45e35f1.jpeg)
不省略时跟 Java 无异
![75beb182a5dce2a3e7935382eed86f1e.png](https://i-blog.csdnimg.cn/blog_migrate/392b7d68a89d10903da262dc8e9071f0.png)
5.1.4 kotlin named argument
named argument 的中文含义是 可以根据 参数名 进行传递参数,传参顺序不需要严格按照函数定义顺序,这样就可以避免参数误传的问题了
![8c0ccd37f7338ba86b2eac652faeb6bb.png](https://i-blog.csdnimg.cn/blog_migrate/cfd79c434c2afc8f30b588f4eb08982f.jpeg)
![01ca6b558650c61893a18de2c65b0417.png](https://i-blog.csdnimg.cn/blog_migrate/1c81bca5e464cad800726207c346ede1.png)
5.2 kotlin 变量和常量
varible(变量) 通过 var 关键字来声明,格式为 var 变量名[:类型] = [初始值]
private var mConfig = RabbitConfig()
lateinit var application: Application
private var isInit = false
- val 声明的变量是只读变量,它的引用不可更改,事实上我们依然可以更改其中引用对象的可变成员
- const 相当于 java 里面的 final,表示一旦初始化便立即被主内存看到,不能被二次赋值
当然 kotlin 也有自动拆箱和装箱将 Int 翻译成 Java 的 int 或 Integer 以提高性能
private var Int = 8
Kotlin 会自动给属性加上 setter 和 getter 方法
class RabbitConfig(
var enable: Boolean = true,
var enableLog: Boolean = true,
@Transient var uiConfig: RabbitUiConfig = RabbitUiConfig(),
var storageConfig: RabbitStorageConfig = RabbitStorageConfig(),
var monitorConfig: RabbitMonitorConfig = RabbitMonitorConfig(),
var reportConfig: RabbitReportConfig = RabbitReportConfig()
)
用 Show Kotlin Bytecode 翻译一下结果如下:
![a01cb1eda85d0819fb4dc7c1798011a3.png](https://i-blog.csdnimg.cn/blog_migrate/1ed1ecb432251ac14038ec45d6c72add.jpeg)
如果想重写 set() 或 get() 方法 可以这么做
data class RabbitConfig(
var enable: Boolean = true,
var enableLog: Boolean = true,
@Transient var uiConfig: RabbitUiConfig = RabbitUiConfig(),
var storageConfig: RabbitStorageConfig = RabbitStorageConfig(),
var monitorConfig: RabbitMonitorConfig = RabbitMonitorConfig(),
var reportConfig: RabbitReportConfig = RabbitReportConfig()
) {
var age: Int = 10
set(value) {
println("setter $value")
field = value
}
val createTimeShow: () -> String
get() = { this.toString() }
}
5.3 kotlin null 判断
以上声明的变量皆为非空变量,即不允许为空值。如果需要声明一个变量可为空,仅需在变量类型后面加上?
val n: String?=null
声明一个变量可为空,仅需在变量类型后面加上?
// 在使用n的时候,由于n可能为null,直接使用编译器会报错
n.length
// 正确的使用方式是,在使用前,做判空处理
// 如果变量为空,只需在变量类型后面加上?,这样就不会访问length属性了
n?.length
// 如果确定一个可空变量不为空
// 可以加!!告诉编译器它不为空
n!!.length
5.4 kotlin 字符串模板
val i=10
val s="i=$i"//求值结果为:“i=10”
val s="adb"
val str="$s.length is ${s.length}"//执行结果为“adb.length is 3”
5.5 kotlin 双冒号操作符
双冒号操作符 表示把一个方法(变量)当做一个参数,传递到另一个方法(变量)中进行使用,和 Java8 类似
/**
* 把功能的名字转换为对应存储数据对象
* */
fun nameToInfoClass(name: String): Class<*> {
return when (name) {
RabbitMonitorProtocol.BLOCK.name -> RabbitBlockFrameInfo::class.java
RabbitMonitorProtocol.APP_SPEED.name -> RabbitAppStartSpeedInfo::class.java
RabbitMonitorProtocol.FPS.name -> RabbitFPSInfo::class.java
RabbitMonitorProtocol.MEMORY.name -> RabbitMemoryInfo::class.java
RabbitMonitorProtocol.EXCEPTION.name -> RabbitExceptionInfo::class.java
RabbitMonitorProtocol.NET.name -> RabbitHttpLogInfo::class.java
RabbitMonitorProtocol.SLOW_METHOD.name -> RabbitSlowMethodInfo::class.java
RabbitMonitorProtocol.BLOCK_CALL.name -> RabbitBlockFrameInfo::class.java
RabbitMonitorProtocol.GLOBAL_MONITOR.name -> RabbitAppPerformanceInfo::class.java
RabbitMonitorProtocol.ANR.name -> RabbitAnrInfo::class.java
else -> RabbitFPSInfo::class.java
}
}
5.6 kotlin 匿名函数
kotlin 正常函数
//---------------------案例一--------------------------
a(fun b(param: Int): String {
return param.toString()
});
val d = fun b(param: Int): String {
return param.toString()
}
//---------------------案例二--------------------------
view.setOnClickListener(new OnClickListener() {
@Override
void onClick(View v) {
switchToNextPage();
}
});
kotlin 匿名函数
//-----------------------案例一------------------------
a(fun(param: Int): String {
return param.toString()
});
val d = fun(param: Int): String {
return param.toString()
}
//-----------------------案例二------------------------
fun setOnClickListener(onClick: (View) -> Unit) {
this.onClick = onClick
}
view.setOnClickListener(fun(v: View): Unit) {
switchToNextPage()
})
5.7 kotlin lambda 语法糖
view.setOnClickListener() { v: View ->
switchToNextPage()
}
//-----------------------------------------------
view.setOnClickListener { v: View ->
switchToNextPage()
}
//-----------------------------------------------
mSimpleListRv.setOnClickListener {
startActivity(Intent(this, SimpleListActivity::class.java))
}
//-----------------------------------------------
view.setOnClickListener {
switchToNextPage()
it.setVisibility(GONE)
}
5.8 kotlin 表达式
5.8.1 kotlin for 循环 和 范围 表达式
5.8.1.1 kotlin for 循环
// -----------------------正序遍历-----------------------
for (index in 1..100){
print(index)
}
5.8.1.2 范围 表达式
// -----------------------倒序遍历-----------------------
for (index in 100 downTo 1){
print(index)
}
// -----------------------不使用1作为遍历的步长-----------------------
for (index in 1..100 step 2){
print(index)//会输出1..3..5......
}
// -----------------------创建一个不包含末尾元素的区间-----------------------
for (index in 1 until 10){
println(index)//输出0..9
}
// -----------------------遍历一个数组/列表,想同时取出下标和元素-----------------------
val array = arrayOf("a", "b", "c")
for ((index,e) in array.withIndex()){
println("下标=$index----元素=$e")
}
// -----------------------遍历一个数组/列表,只取出下标-----------------------
val array = arrayOf("a", "b", "c")
for (index in array.indices){
println("index=$index")//输出0,1,2
}
// -----------------------遍历取元素-----------------------
val array = arrayOf("a", "b", "c")
for (element in array){
println("element=$element")//输出a,b,c
}
5.8.2 kotlin 枚举和 when 表达式
5.8.2.1 kotlin 枚举
Kotlin 中,枚举类型以类的形式存在,因此叫做枚举类,详细代码如下:
enum class Color {
RED, GREEN, BLUE
}
//---------------使用-------------------
var color: Color = Color.BLUE
var color2 = Color.GREEN
// 比较两个枚举类型变量
var bool: Boolean = color == color2
//---------------为枚举值指定数值-------------------
enum class Direction private constructor(var value: Int) {
NORTH(1), WEST(2), EAST(3), SOUTH(4)
}
5.8.2.2 kotlin when 表达式
when 表达式 可以支持自动转型(Auto-casting),详细代码如下:
when (view) {
is TextView -> toast(view.text)
is RecyclerView -> toast("Item count = ${view.adapter.itemCount}")
is SearchView -> toast("Current query: ${view.query}")
else -> toast("View type not supported")
}
5.8.3 kotlin 中缀表达式
kotlin 中缀表达式只有一个参数,且用 infix 修饰的函数,这就是我们所说的自定义运算符的中缀表达式,详细代码如下:
// 书
class Book{
// 传入任意类型,返回一个Boolean类型的参数
infix fun on(any: Any): Boolean{
return true
}
}
// 桌子
class Desk
fun main(args: Array<String>) {
if(Book() on Desk()){
println("书在桌上")
}
}
5.8.3 kotlin 复合表达式
5.8.3.1 '?.'
![e714c972530164464338a6bbba58a61a.png](https://i-blog.csdnimg.cn/blog_migrate/c01922ecacae07dd24da015cec81b4db.jpeg)
?.表示,如果 foo 这时候是一个空的,则返回 null,否则就返回 foo.bar(),详细代码如下:
if (foo != null){
return foo.bar()
}else{
return null
}
5.8.3.2 '?:'
![a5ab80914dd9e231ac4c65fe1afe79e9.png](https://i-blog.csdnimg.cn/blog_migrate/3bf32df37769e5556637dc77f9b795d1.jpeg)
?: 作用是当数据非空时,直接返回数据,而当数据为空时,返回合并到的数据。利用该运算符,可以很容易的把可空类型转换为非空类型,详细代码如下:
if(foo!=null)
{
foo
}
else
{
bar
}
foo?.length?:-1
5.8.3.3 '!!'
![6f1948550681a8542d56afa91120007b.png](https://i-blog.csdnimg.cn/blog_migrate/d7d324e32c4a427a328717d8778c188c.jpeg)
Kotlin 中 !! 表示非空断言运算符,详细代码如下:
if(foo!=null)
{
foo
}else
{
return NullPointerException
}
5.8.3.4 'as?'
![6f1948550681a8542d56afa91120007b.png](https://i-blog.csdnimg.cn/blog_migrate/d7d324e32c4a427a328717d8778c188c.jpeg)
Kotlin 可以使用安全转换操作符 as?,它可以在失败时返回 null,详细代码如下:
foo as? type
if(foo is Stype)
{
foo as Type
}else
{
null
}
5.8.3.5 '?'
![bbe22b451fe4b4479349e5f9597db103.png](https://i-blog.csdnimg.cn/blog_migrate/ab097ce532cd9ab9f3dd814f52b70066.jpeg)
?表示可空类型与非空类型,Kotlin 的类型系统旨在消除来自代码空引用的危险,详细代码如下:
foo?
var foo?="abc"
foo=null
// 编译成功 foo可为空
var foo="abc"
foo=null
// 编译失败 foo不可以为空
六. Kotlin 面向对象
6.1 kotlin 继承和构造
kotlin 中继承全部使用“:”,不区分 extend 或 implement,但是有几个规则需要注意一下:
![5e4c258b2276343c30d40ca4f789f99f.png](https://i-blog.csdnimg.cn/blog_migrate/1e4ef3c8adb63b75e4005bcc48f76625.jpeg)
![57191ce550cc91300ec6c8177e959b6b.png](https://i-blog.csdnimg.cn/blog_migrate/fef8ae7d22da3ebce51e26f1c7b672fd.jpeg)
![4cf532d9ad4814c3e6ad64a558012fd6.png](https://i-blog.csdnimg.cn/blog_migrate/2ffad24f893d4d3fd23619f22f08a1b4.jpeg)
![37f5f4a70474cc9be39d378d481e9516.png](https://i-blog.csdnimg.cn/blog_migrate/af883cbaf55a748c159e7c68fe6a3c6d.jpeg)
![d83896d31da2208e0329ab5941a29384.png](https://i-blog.csdnimg.cn/blog_migrate/2251e04367e344fd0e2759154fa8c2ae.jpeg)
![96e8c677191b5f76cd856e07647c59cf.png](https://i-blog.csdnimg.cn/blog_migrate/7ce6cb3d943fae79899dcc310d34cd6c.png)
6.2 kotlin 类
6.2.2 kotlin object 类
object 全局声明的对象只有一个,所以他是天生的单例模型
![d662252090f4b561b3187ffe1d100edb.png](https://i-blog.csdnimg.cn/blog_migrate/bc26f6bc6a87564f1eb355dc71713eb1.jpeg)
6.2.2 kotlin data 类
在 Kotlin 里面声明一个 data 类需要满足以下条件:
- data 类必须要有一个构造方法,且包含至少一个参数
- 该 data 类构造参数强制使用 val 或 var 进行声明
- data class 之前没有 abstract , open 或 inner 进行修饰
![2b7889d3a61544bea7a9057f28e2d927.png](https://i-blog.csdnimg.cn/blog_migrate/2ee8918579a36f9d1771e70f2c46f302.jpeg)
6.2.3 kotlin 内部类
![9d1fed8a8ce5fc809db63fda7598715a.png](https://i-blog.csdnimg.cn/blog_migrate/006028909acf9336374860f155be7e6a.jpeg)
6.3 kotlin 接口
![5221fa0299b1b3595a459840d94a22ff.png](https://i-blog.csdnimg.cn/blog_migrate/2bc6a5f2060fbe786de84778b323a101.jpeg)
6.4 kotlin 伴生对象
![e33567488932d5da6704bc3cd9730df0.png](https://i-blog.csdnimg.cn/blog_migrate/4420012f9c856eabc410f8f876c05b76.jpeg)
![8072595c91817fb4f33f621ab1f99726.png](https://i-blog.csdnimg.cn/blog_migrate/5dda0f60c22aef0bdc83f745229379a7.jpeg)
6.5 kotlin 修饰符
6.5.1 限制修饰符
- val 翻译成字节码就是 final
![bbccb909f48d34c35d44a24a5df5aebc.png](https://i-blog.csdnimg.cn/blog_migrate/f4d64b732033748f2243f5416fa6a12f.jpeg)
- private
![32697f57fd5bfef2b1f5d1b8f542785e.png](https://i-blog.csdnimg.cn/blog_migrate/391ac7b41c1eb35be6e76d8c9c84cb36.png)
- internal
![3b93e1f0103fbcb2198de38b2c34f6ba.png](https://i-blog.csdnimg.cn/blog_migrate/673f2efa2c551f63b4c603a1b9f1f337.jpeg)
6.5.2 可见修饰符
嵌套类的用法: 如果要在 Kotlin 中嵌套一个类,需要在该内部类加 inner 修饰
//嵌套类属于静态类和外部类没任何关系
fun main(args : Array<String>){
var ot = OutClass().innerClass()
ot.hello()
}
class OutClass{
var name ="李武"
//inner表示内部类
inner class innerClass{
var name = "张三"
fun hello(){
println("你好 $name")
//内部类使用this,访问外部类的变量
println("你好 ${this@OutClass.name}")
}
}
}
七. 总结
本文先从 kotlin 概念带大家进入 kotlin 语言,然后说了一下 kotlin 和 java8 的异同以及相互转换方式,最后对 Kotlin 基础语法进行一一阐述,如: kotlin 方法和变量使用,kotlin 字符串模板, lambada 表达式,以及各种表达式的实际运用。好了今天的 kotlin 就说到这里了,如果大家对此有什么疑问欢迎到评论区留言~
各位看官,看都看了,帮忙点个赞呗~