1.1 Kotlin 是一门怎么样的语言?
Kotlin 是一种在 JVM 上运行的静态类型编程语言,可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。
Kotlin 核心的目标
- 简约:帮你减少实现同一个功能的代码量。
- 易懂:让你的代码更容易阅读,同时易于理解。
- 安全:移除了你可能会犯错误的功能。
- 通用:基于 JVM 和 JavaScript,你可以在很多地方运行。
- 互操作性: Kotlin 和 Java 可以相互调用,同时 Jetbrains 的目标是让他们 100% 兼容。
对比 Java,Kotlin 或许显得有点年轻,但这并不代表它的生态圈不够稳定。
1.2 强大的生态圈
Kotlin 编译为 JVM 字节码或 JavaScript
它可以吸引所有使用垃圾回收机制语言的程序员,包括 Java,Scala,Go,Python, Ruby 和 JavaScript。
kotlin 产自工业,而不是学术界。
它解决了程序员目前所面临的一些问题。举个例子:类型系统可以帮助你避免空指针异常。当我们在使用一些 API 或者大型库时判断是否为 null,往往没有什么实际作用。
Kotlin 完美兼容 Java
在 Idea 中,你可以一键实现 Java 与 Kotlin 的转换。也就是说, Kotlin 写的项目中可以使用所有现有的 Java 框架和库,甚至是依赖于注解的高级框架,并且它整合了 Maven,Gradle 和其他构建工具——帮助我们实现无缝的互操作。
Kotlin 相对入门简单
所以正如大家所说的那样,Kotlin 是一门十分友好的语言,你甚至可以花几个小时阅读下相关书籍就能入门。它的语法简洁直观,也许大多时候看起来比较像 Scala,实际上简单了很多。这种语言很好地平衡了简洁和可读性。
不施加运行时间的开销
标准库是比较小的:主要是对 Java 标准库的重点扩展。大量使用编译时内联(compile-time inlining)就意味着像 map
、 filter
、 reduce
这样的语法编译起来与命令式的相似。
Android扩展库
Anko 与 kovenant 让 Kotlin 在 Android 的开发中更加方便,如果你看过这两个库,一定也为之青睐。
除 Android 外,其实企业级别的 Java 项目也可以考虑使用 Kotlin:
- 它有一个成熟的公司强大的商业支持,JetBrains 致力于该项目的发展,拥有一支高效的团队和稳定的商业模式。在较长的时间内,你都不用担心 Kotlin 被抛弃。
- Kotlin 的风险很低:可以由一两个成员尝试在某个功能块使用,但并不会影响其他模块:Kotlin 类可以导出一个与常规 Java 代码看起来相同的 Java API。
- Java 6 废土中的一线希望:如果升级 JVM 对于你们的成本很高,你可以使用 Kotlin,它是「加强版的 Java 6」。
看完生态圈,来看看最吸引我们的语言功能部分。
1.3 特征
1.3.1 属性前置
Java Bean 中总是出现一些重复工作量的代码:
public class Customer{
String name;
int age;
public Customer(String name, int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public setName(String name){
this.name=name;
}
public setAge(int age){
this.age=age;
}
}
复制代码
在 Java 对象中,构造函数和属性的 Getter
、Setter
函数在每个Bean类中都会出现,看起来非常冗余。
但是使用 Kotlin,代码是这样的:
data class Customer(val name:String, val age:Int)
复制代码
Kotlin 将Getter
、Setter
等函数省略,让我们更加关注属性。
1.3.2 函数式编程是变革的关键
如果你是一名 「FP」 (Functional Programming) 爱好者,你肯定知道 Scala 这门语言。而 Kotlin 的设计初衷就是提供 Scala 里的很多特性,但不被编译时间束缚。
网上对函数式编程的介绍,大多数对初学者都是不够友好的,下面是一种比较简洁的概括:
函数编程以基于数学的函数构成,实现一个功能时倾向于实现的目标,而不是实现目标的方式。
也就是说,函数式编程处理的是不可变的数据,即不会改变变量的值。它所做的只是 Receive(接收)-> Compute(计算)-> Return(返回) ,而不改变它接收到的值,或者引起「副作用」。
1.3.3 Kotlin 重新定义了函数的写法
- 简化函数重载。
- 命名参数不遵循顺序:命名参数允许完全控制什么值被传递给变量,并在与布尔处理,举个例子:
// Java
void check(boolean isAlpha, boolean isNum){
// do something
}
check(true, false); // 函数调用
// Kotlin
fun check(isAlpha:Boolean, isNum:Boolean = false) {
// do something
}
check(true)// 1
check(true,true)// 2
check(isAlpha = true, isNum = false) // 3
check(isNum = false, isAlpha = true) // 4
其中1、3、4实际效果一致。
函数可读性
- 单行表达式是提高代码可读性的第一步。
// Java
int sum (int x, int y){ // 必须声明返回类型
x = x + y; // x的值被改变,带来副作用
return x;
}
// kotlin
fun sum(x: Int, y: Int) = x + y // 完美
在 Android 开发时,我们应该都会对视图的 onClick
事件感到烦躁。
// Java
view.setOnClickListener(new View.OnClickKistener() {
@ Override
public void onClick(View v){
System.out.println(“sad”);
}
});
在 kotlin 中,这也得到简化:
view.onClick {
println(“nice !!”)
}
类型安全
在 Android 开发中,出现 NullPointerException
已经可以说是家常便饭了。但如果在运行的时候出现这个异常导致程序崩溃,对用户体验造成的损失是巨大的。
Kotlin 能很好的避免这个问题:
- 指定 null
// Java
String text1 = null // NE
// Kotlin
var text1: String = “Kotlin”// 正常
var text2: String = null // NE
var text3: String ?= null // 正常
Kotlin 中只有加了 ? 才允许为 null。
- 检查 null Kotlin 可以通过
?
确保安全调用:
// Java
if(a != null){
int x = a.length();
}
// Kotlin
val x = a?.length // 仅当 a 不为 null 时才能通过编译
- 为 null 时默认值
val x = b?.length ?: 0 // 如果 b 为 null,那他的 length 就为0
val x = b?.length ?: throw NullPointerException()
val x = b!!.length
Lambda 优化
Lambda 表达式增加了 Kotlin 的简洁功能,从而也让代码更容易理解。(Java 6 不支持 lambda )
val sum = {x: Int, y: Int -> x + y} // 类型:(Int, Int) -> Int
val result = sum(2, 7)// result == 9
条件判断更优雅
kotlin 中 when
同样能带给你惊喜,你可以用它简化 if..else
或 switch
:
// Java
if(x == 1) println(“x is 1”);
else if(x == 2) println(“x is 2”);
else if(x == 3 || x == 4) println(“x is 3 or 4”);
else if(x >= 5 || x <= 10) println(“x is 5, 6, 7, 8, 9, or 10”);
else println(“x is out of range”);
// Kotlin
when (x) {
1 -> print(“x is 1”)
2 -> print(“x is 2”)
3, 4 -> print(“x is 3 or 4”)
in 5…10 -> print(“x is 5, 6, 7, 8, 9, or 10”)
else -> print(“x is out of range”)
}
当然除了以上这些,Kotlin 还有许多令人惊喜的特性,
第二章 Kotlin初阶易错知识点
1、不需要分号,不需要分号,不需要分号
2、字段定义://var 可变变量;val 常量
例如:
var a: Int = 1
val b = 2
val c: Int = 0
var d: Int = 0
复制代码
3、字符串的定义,去掉空格
var age: String? = “abc” //加 ? 可为空
val age2 = age!!.toInt() //抛出空指针异常
val age3 = age?.toInt() //不做处理,返回null
val age4 = age?.toInt() ?: -1 //age为空时,返回-1
text.trimMargin()
复制代码
4、list,数组是不型变的(invariant)
val items = listOf(“item1”, “item2”, “item3”)
for (item in items) {
println(item)
}
for (index in items.indices) {
println(“item at $index is ${items[index]}”)
}
//输出
// item at 0 is apple
// item at 1 is banana
// item at 2 is kiwi
复制代码
5、数组
//[1,2,3]
val a = arrayOf(1, 2, 3)
//[0,2,4]
val b = Array(3, { i -> (i * 2) })
for (i in a.indices) {
println("eche element : " + a[i])
}
复制代码
6、运算符(>/</=/>=/<=/!=)
if-else
复制代码
7、运算逻辑when(X)可以传int,enum等
val sss = 5
val validNumbers = 11…19
when (sss) {
0, 1 -> print(“x == 0 or x == 1”)
in 1…10 -> print(“x is in the range”)
in validNumbers -> print(“x is valid”)
!in 10…20 -> print(“x is outside the range”)
else -> print(“none of the above”)//如果包含了所有的情况,就不需要写else了
}
fun hasPrefix(x: Any): Boolean = when (x) {
is String -> x.startsWith(“prefix”)//当是string类型,则运算startsWith(),返回是否以特定前缀结尾
else -> false//不是string类型,直接返回false
}
//判断某个值是否在一个set集合里面
val items = setOf(“apple”, “banana”, “kiwi”)
when {
“orange” in items -> {
println(“juicy”)
println(“{do something}”)
}
“apple” in items -> {
println(“apple is fine too”)
println(“do another thing!”)
}
}
复制代码
8、类型转换
val e: Byte = 1
val f: Int = e.toInt()//转换
复制代码
中缀操作符 as
eg: val x: String = y as String
val x: String? = y as String?
val x: String? = y as? String
9、数字定义(可以用"_"来区别很长的数字)
//使用下划线使数字常量更易读
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
复制代码
10、for循环(极其方便)
fun forCircle() {
for (i in 1…5 step 1) {
print(i)
//输出12345
}
for (i in 5 downTo 1 step 1) {
print(i)
//输出54321
}
for (i in 1…5 step 2) {
print(i)
//输出13
}
for (i in 1 until 5 step 1) {
print(i)
//输出1234,没有5 等同于[1,5)
}
}
复制代码
11、类型替代(typealias)
typealias f = Foo//类型替代(f代表新的名词,Foo代表需要另起名字的对象名,比如,Foo全名为:尼古拉斯.凯奇.赵四,那么f可以叫做赵四,哪里需要这么高大上的名称)
复制代码
12、is和!is
fun getStringLength(obj: Any): Int? {
if (obj !is String) {
return null
}
//此时obj已经自动转化为string类型
return obj.length
}
复制代码
13、lambda表达式使用之一
//limbda表达式
val sumLambda: (Int, Int) -> Int = { x, y -> x + y }//xy是入参,x+y是返回值
复制代码
14、长度可变的入参(关键字:vararg)
//可变长度入参
fun sum5(vararg v: Int): Int {
var sum = 0
for (vt in v) {
sum += vt
}
return sum
}
复制代码
15、类相关(主构造函数,次构造函数,init方法,默认参数,变量的get和set方法,super父类方法,重写父类方法,共有函数,私有函数)
class Runoob constructor(name1: String = “jack”, psd1: String = “123456”) {//默认参数
//主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀
init {
println(“name : $name1, psd : $psd1”)
// name = “”//这里连name常量都没有定义,所以不能够进行赋值
}
constructor(name: String, psd: String, addr: String) : this(name, psd) {
this.name = name
this.psd = psd
}
//次构造函数
constructor(name: String, psd: String, age: Int) : this(name, psd) {
}
var nickName = name1
get() {
return nickName
}
var name: String
get() {
return name.toUpperCase()
}
set(value) {
name = value
}
var psd: String = “”
private set
get() {
return psd + “@”
}
}
复制代码
16、interface的方法体是否实现是可选的。属性也是抽象的,需要实现类重新赋值。
interface BaseView {
var mNickName: String//抽象的属性
fun walk() {//方法体是否实现是可选的
println(“BaseView,walk!!!”)
}
fun yeal() {}//
}
复制代码
17、密封类
//密封类
sealed class Expr//相当于枚举的扩展
data class Const(val number: Double) : Expr()//这里只有另外一个double的状态入参
data class Sum(val e1: Expr, val e2: Expr) : Expr()//这个密封类里面就有两个Expr的状态入参
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
//因为已经覆盖了所以的情况,就不需要else子句了
}
复制代码
18、枚举
Color.BLACK.name
enumValues().size
enumValues().get(0)
enumValues().plus(Color.WHITE)
enumValues().iterator()
enumValueOf(Color.WHITE.toString())
复制代码
19、list和Comparable
fun <T : Comparable> sort(list: List) {
}
sort(listOf(1, 2, 3)) // OK。Int 是 Comparable 的子类型
// 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型
// sort(listOf(HashMap<Int, String>()))
复制代码
20、object对象
val site = object {
var name = “jack”
var url = “www.tansu.com”
}
复制代码
21、委托类,委托对象(通过关键字 by 建立)
interface MyBase {
fun bark()
// fun bak()
}
class BaseImpl(val x: Int) : MyBase {
fun bak() {}
override fun bark() {
println(“x:$x”)
}
}
// 通过关键字 by 建立 委托类
class Deried(b: MyBase) : MyBase by b
fun test3() {
var baseImpl = BaseImpl(10)
Deried(baseImpl).bark()
baseImpl.bak()
// Deried(baseImpl).bak()//无法访问到非interface定义的方法
}
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
//说明:如果p是var的话,那么委托类需要有getValue和setValue两个方法(因为此时是可变的变量,需要由set方法)
//说明:如果p是val的话,那么委托类只需要有getValue方法即可,另外一个可以不要求(此时为不可变常量,不需要set值进去)
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return “$thisRef, 这里委托了 ${property.name} 属性”//这里name为p
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println(“$thisRef 的 ${property.name} 属性赋值为 $value”)//value为设置进来的值
}
}
fun test4() {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = “Runoob” // 调用 setValue() 函数
println(e.p)
// Example@433c675d, 这里委托了 p 属性
// Example@433c675d 的 p 属性赋值为 Runoob
// Example@433c675d, 这里委托了 p 属性
}
复制代码
21、监听类值的变化
class User {
var name: String by Delegates.observable(“初始值”) { prop, old, new ->
println(“旧值:
o
l
d
−
>
新值:
old -> 新值:
old−>新值:new. prop:$prop”)
}
}
val user = User()
user.name = “第一次赋值”
user.name = “第二次赋值”
// 旧值:初始值 -> 新值:第一次赋值
// 旧值:第一次赋值 -> 新值:第二次赋值
复制代码
22、map
//把属性储存在映射中
class MyWebSite(val map: Map<String, Any?>) {
val name: String by map
val url: String by map
}
使用
var myWebSite = MyWebSite(mapOf(“name” to “jack”, “url” to “www.baidu.com”))
myWebSite.name
myWebSite.url
//结果读取出来
复制代码
23、其它
class Foo {
var notNullBar: String by Delegates.notNull()
}
使用
var foo = Foo()
foo.notNullBar = “bar”
println(foo.notNullBar)
kotlin最佳实践
类型的声明与使用
val与var
val->不可变引用,var->可变引用。
我们应该尽可能地使用val关键字来声明所有的kotlin变量 为什么呢?
- 首先一个变量在声明时是不可变的,那就代表你在使用的时候不需要考虑其他地方会对它重新复制和改变(对于对象注意只是引用不可变),直接使用。
- val声明的类型由于必须初始化,它是线程安全的。
- kotlin为了保证类型安全,所有变量声明的地方必须要做初始化,即显示赋一个初值。
空与非空
kotlin
对于可空类型
和非空类型
认为是两个完全不同的类型,比如Int
与Int?
,他们俩就不是相同的类型。利用这个特点和编译时检查,kotlin
基本可以避免空指针异常
。
上面已经说了kotlin的类型声明时必须要有初值
,所以空与非空类型
和val与var
一组合就会变成4种情况,下面我们对一个Person
对象进行声明:
val p:Person = Person()
val p:Person? = null // 这个情况是没有意义的
var p:Person = Person() // 如果这个对象是非空类型,那么初始化的时候必须赋一个非null初值
var p:Person? = null //可以给一个null,也可以给一个对象实例
上面这段代码基本解释了val与var
和空与非空的
关系。
空与非空的使用
kotlin
对空与非空
做了严格的限制,那我们在使用时不都要对可空类型
类型做判断吗?为了避免这个问题,kotlin提供了许多运算符来使开发人员对于可空与非空
编码更加愉快。
- 安全调用运算符 “?.”
比如我们声明了这样一个类型var p:Person? = null
。如果我们直接使用p.name
,kotlin编译器是会报错的无法编译通过,所有我们必须这么做:
if(p != null) p.name
这种代码写多了实在是太没有意义了,所有kotlin
提供了?.
。上面代码我们可以直接这样代替p?.name
。它的实际执行过程是这样的:如果p
为空则不操作,如果p
不为空则调用p.name
。
- Elvis 运算符 “?:”
在kotlin中我们会写这种代码:
val name = if(p != null) p.name else “” //kotlin中的if是一个表达式
不过使用?:
可以更简单实现上面的逻辑 : val name = p?.name ?: ""
。 它的实际执行逻辑是如果p为null,p?.name就会为null
, ?:
会检查前面的结果,如果是null,那么则返回""
。
- 安全转换 “as?”
在kotlin中类型转换关键字为as
。不过类型转换会伴随着转换失败的风险。使用as?
我们可以更优雅的写出转换无风险的代码:
//Person类中的方法
fun equals(o:Any?):Boolean{
val otherPerson = o as? Person ?: return false
…
}
即as?
在转换类型失败时会返回null
- 非空断言 “!!”
如果使用一个可空类型的方法,并且你不想做非空判断,那么你可以这样做: person!!.getAge()
。 不过如果person
为null,这里就会抛出空指针异常
。
其实还是在蛮多case下可以使用它,但是不建议使用, 因为你完全可以使用?、?:来做更优雅的处理, 你也可以使用lateinit来避免编译器的可空提示
val与bylazy、var与lateinit
- bylazy
它只能和val
一块使用。
private val mResultView:View = bylazy{
先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以扫码领取!!!!
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可免费领取!
11297603100)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以扫码领取!!!!
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
[外链图片转存中…(img-lxgNt13v-1711297603101)]
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
[外链图片转存中…(img-cuI3RaU0-1711297603101)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可免费领取!