for-downTo:
//使用 downTo 表示创建两端都是闭区间的降序区间[10,0]
for (i in 10 downTo 0){
print("$i ")
}
//打印结果
10 9 8 7 6 5 4 3 2 1 0
步进:
//使用 downTo 表示创建两端都是闭区间的降序区间,每次在跳过3个元素
for (i in 10 downTo 0 step 3){
print("$i ")
}
迭代list
//使用withIndex迭代list
val list = arrayListOf(“10”,“11”,“100”)
for ((index,element) in list.withIndex()){//解构申明
println(“
i
n
d
e
x
:
index:
index:element”)
}
//打印结果
0:10
1:11
2:100
迭代map
val map = mapOf(1 to “Java”, 2 to “Kotlin”, 3 to “Flutter”)//中缀调用
for ((key, value) in map) { //解构
println(“
k
e
y
:
key:
key:value”)
}
//打印结果
1:Java
2:Kotlin
3:Flutter
5.类
和Java一样,类的定义如下
class Base {
var num = 1
}
但是意义却不太一样。
5.1可见性
**Kotlin中,默认类的可见性为public以及final的。**内部类默认为static的,用inner标记非静态内部类。
①Kotlin 中可见性
private
:私有,本类内部可见protected
:子类可见internal
:模块内可见public
:默认,公有
②对比 Java
private
:私有,本类内部可见protected
:子类可见default
:默认,包内可见public
:公有
单个构造函数&私有构造
class Response private constructor(val code: Int, val msg: String){
override fun toString(): String {
return “code:
c
o
d
e
,
m
s
g
:
code,msg:
code,msg:msg”
}
}
多个构造函数
//非open不可被继承
class Response {
val code: Int
val msg: String
constructor(code: Int) {
this.code = code
msg = “”
}
constructor(code: Int, msg: String) {
this.code = code
this.msg = msg
}
override fun toString(): String {
return “code:
c
o
d
e
,
m
s
g
:
code,msg:
code,msg:msg”
}
}
其中code和msg的getter会自动生成。
kotlin中也是单继承多实现,共同存在时,继承类写到第一位,后面追加逗号跟上接口接口。
public class String : Comparable, CharSequence{
…
}
密封类(private构造,默认open)
sealed class Expr {
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()
}
5.2Object关键字
①类单例
object SingleTon {
@JvmStatic
fun isEmpty(str: String) = str.isEmpty()
}
反编译成Java
public final class SingleTon {
public static final SingleTon INSTANCE;
@JvmStatic
public static final boolean isEmpty(@NotNull String str){
Intrinsics.checkParameterIsNotNull(str, “str”);
CharSequence var1 = (CharSequence)str;
boolean var2 = false;
return var1.length() == 0;
}
private SingleTon() {
}
static {
SingleTon var0 = new SingleTon();
INSTANCE = var0;
}
}
根据面可以看出,object单独修饰一个类,表示为静态代码块实现的单例。
@JvmStatic修饰的方法,kotlin会在其方法上添加static关键字,而static关键字在kotlin中是不存在的。
②匿名类
test(object : Listener {
})
interface Listener{
}
kotlin中没有new关键字,所以匿名类用object实现。
③伴生对象
kotlin没有static,那么如何实现静态变量以及常量等的定义和使用呢?
答案是伴生对象
class UserManager {
companion object{
val USER_TYPE = 0x01
}
}
上面的companion object会生成一个内部类Companion,并添加返回USER_TYPE的静态getter,如下
public final class UserManager {
private static final int USER_TYPE = 1;
public static final UserManager.Companion Companion = new UserManager.Companion((DefaultConstructorMarker)null);
public static final class Companion {
public final int getUSER_TYPE() {
return UserManager.USER_TYPE;
}
…
}
}
PS:const关键字
const关键字只能用在静态类中, 只能与val连用,即const val,而且只能修饰基本类型。意义为编译期常量,在用到的地方替换为该常量的值。如下:
object SingleTon {
const val str = “const”
}
fun test(): String? {
return SingleTon.str
}
其中test反编译Java如下:
public final String test() {
return “const”;
}
可以看到kotlin对const常量做了内联。
5.3类委托
equals
在Java中,可以使用来比较基本数据类型和引用类型,基本数据类型比较的是值,引用类型上比较的是引用。在kotlin中就等于调用Java中的equals。如果需要比较引用则需要用===。
by关键字
装饰器模式的代码通常就较为模板,kotlin中可以利用by关键字来实现类的委托。比如:
class MyList : List by ArrayList() {
//这里面默认利用ArrayList实现了List的所有接口
}
转换成Java:
public final class MyList implements List, KMappedMarker {
// $FF: synthetic field
private final ArrayList KaTeX parse error: Expected '}', got 'EOF' at end of input: … { return this.delegate_0.get(index);
}
…
}
当然,我们也可以通过重写来实现自己的逻辑。
by也可以用来实现延迟加载:
private val arr by lazy { MyList() }
它的实现是double-check的懒加载方式,如下:
private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress(“UNCHECKED_CAST”)
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress(“UNCHECKED_CAST”) (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else “Lazy value not initialized yet.”
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
6.lambda
6.1语法
lambda表达式,本质上就是可以传递给其他函数的一小段代码。
kotlin中简单lambda:
button.setOnclickListener{
}
kotlin中lambda始终被花括号{}包围。可以把lambda表达式存储在变量中:
val sum = { x:Int,y:Int -> x + y }
println(sum(1,2))
3
kotlin中,lambda作为最后一个参数可以把它放到()后面如下1;如果只有lambda作为参数,可以省略(),如下2。
list.maxBy({it.length})
list.maxBy(){it.length}//1
list.maxBy{it.length}//2
6.2集合函数API
比如list过滤:
val list = arrayListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
list.filter { it % 2 == 0 }
.forEach { print(it) }
//打印结果
246810
类似的还有:
all:全部匹配返回true
any:存在一个匹配返回true
count:返回匹配的数量
firstOrNull:第一个匹配,没有返回null
first:第一个匹配,没有抛出异常
find:找到第一个匹配 等于 firstOrNull
还有map,flatmap,groupby…
基本涵盖了RxJava的常用操作符。
apply,let,also…
apply改变this指向调用者。方便各种操作,返回调用者
val str = “123456”
val rec = str.apply {
println(“lastChar:KaTeX parse error: Expected 'EOF', got '}' at position 20: …(lastIndex)}") }̲ println("rec:rec”)
//打印结果
lastChar:6
rec:123456
with改变this指向参数,返回lambda最后一行结果
let创建局部变量,返回最后一行结果。
val str :String ? = “123456”
val res = str?.let {
println(“it:KaTeX parse error: Expected 'EOF', got '}' at position 15: it") "return" }̲ println("res:res”)
//打印结果
it:123456
res:return
also:创建it,返回调用者
run:改变this指向调用者,返回最后一行
类似的语法糖takeIf,repeat等等,都在Standard.kt中有定义。
二、深入理解
1.kotlin类型系统
1.1可空性
kotlin中类型定义如果没有添加为可空,当它接受到一个null时,kotlin会在运行时抛出ERROR:Type mismatch的错误。
当一个类规定为可空,可以使用安全调用?.,后面可以跟上Elvis运算符?:。标识在前面?.调用者为null时执行。
val str :String ? = “123456”
str?.get(str.lastIndex) ?: toast(“str is null”) //toast会在str为null时执行
安全转换:as?
is:检查类型,可以自动转型
val obj: Any = “str”
if (obj is String) {
println(“obj:$obj”)//obg自动转型为string
}
//打印结果
obj:str
as:类型转换,as?安全类型转换
val obj: Any = “str”
(obj as? String)?.print()//在obj为String时才会执行后面的语句,print为本地定义的拓展函数
//打印结果
str
!!非空断言
让kotlin在对象为空的时候抛出异常。
val obj: Any? = “str”
obj!!.toString()
obj.hashCode()//不需要再加!!,kotlin编译器自动检查
2.运算符重载
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
val p1 = Point(1,2)
val p2 = Point(5,6)
val p = p1 + p2
println(“p:$p”)//自动调用toString
//打印结果
p:Point(x=6, y=8)
可用于重载的运算符:
表达式 | 函数名 |
---|---|
a * b | times |
a / b | div |
a % b | mod |
a + b | plus |
a - b | minus |
PS:位运算也有自己的符号
运算符 | 操作 |
---|---|
shl | 带符号左移 |
shr | 带符号右移 |
ushr | 无符号右移 |
and | 按位与 |
or | 按位或 |
xor | 按位异或 |
inv | 按位取反 |
3.lambda作为形参
无其他参数:
fun exec(func:()->Unit){
func.invoke()
}
exec {
println(“hello world”)
}
带其他参数:
fun exec(msg: String,func:(msg:String)->Unit){
func.invoke(msg)
}
exec(“hello world”) {msg->
println(msg)
}
以上的lambda是作为不能为空的形参。如果为空,需要将其定义用()?包裹。如下:
fun exec(msg: String,func:((msg:String)->Unit)?){
func?.invoke(msg)
}
lambda作为参数传递虽然好,但是其实现传递的还是对象(匿名类),在每一次调用都会创建一个对象,如何避免这部分开销提升性能?
答案是内联函数。
4.内联函数系列
inline
当一个函数声明为inline时,它的函数体是内联的–换句话说,函数体会被直接替换到函数被调用的地方,而不是被正常调用。
inline fun inlineTest(inlineFunc:()->Unit){
println(“before invoke”)
inlineFunc()
println(“after invoke”)
}
inlineTest {
println(“hello world”)
}
//打印结果
before invoke
hello world
after invoke
to Java:
String var2 = “before invoke”;
System.out.println(var2);
String var5 = “hello world”;
System.out.println(var5);
var2 = “after invoke”;
System.out.println(var2);
内联函数的限制
当我们在内联的lambda中添加return,代码就不正确运行了,它在中途就return掉了。
inlineTest {
println(“hello world”)
return
}
println(“code works as well”)
//打印结果
before invoke
hello world
针对这种情况,我们需要添加return的范围
inlineTest {
println(“hello world”)
return@inlineTest
}
println(“code works as well”)
//打印结果
before invoke
hello world
after invoke
code works as well
当然也可以用其他方法,比如noinline
noinline
noinline
修饰的是 inline
方法中的 lambda
参数。noinline
用于我们不想让 inline
特性作用到 inline
方法的某些 lambda
参数上的场景。
// Kotlin
fun main(args: Array) {
val methodName = “main”
multiplyByTwo(5) {
result: Int -> println(“call method $methodName, Result is: $result”)
}
}
inline fun multiplyByTwo(
num: Int,
noinline lambda: (result: Int) -> Unit): Int {
val result = num * 2
lambda.invoke(result)
return result
}
反编译的结果是:
public final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, “args”);
final String methodName = “main”;
byte num
i
v
=
5
;
F
u
n
c
t
i
o
n
1
l
a
m
b
d
a
iv = 5; Function1 lambda
iv=5;Function1lambdaiv = (Function1)(new Function1() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke(((Number)var1).intValue());
return Unit.INSTANCE;
}
public final void invoke(int result) {
String var2 = "call method " + methodName + ", Result is: " + result;
boolean var3 = false;
System.out.println(var2);
}
});
int
i
i
if
m
u
l
t
i
p
l
y
B
y
T
w
o
=
f
a
l
s
e
;
i
n
t
r
e
s
u
l
t
multiplyByTwo = false; int result
multiplyByTwo=false;intresultiv = num
i
v
∗
2
;
l
a
m
b
d
a
iv * 2; lambda
iv∗2;lambdaiv.invoke(result$iv);
}
public final int multiplyByTwo(int num, @NotNull Function1 lambda) {
int
i
i
if$multiplyByTwo = 0;
Intrinsics.checkParameterIsNotNull(lambda, “lambda”);
int result = num * 2;
lambda.invoke(result);
return result;
}
可以看到, 因为使用了 noinline
修饰了 lambda
,所以,编译器使用了匿名内部类的方式来处理这个 lambda,生成了一个 Function1
对象。
crossinline
是不是有了 inline
和 noinline
,对于我们开发人员来讲就够了呢?就满足了呢?显然不是的。考虑一种情况,我们既想让 lambda
也被 inline
,但是又不想让 lambda
对调用方的控制流程产生影响。这个产生影响,可以是有意识的主动控制,但是大多数情况下是开发人员的不小心导致的。我们知道 java 语言是一个编译型语言,如果能在编译期间对这种 inline
lambda
对调用方产生控制流程影响的地方进行提示甚至报错,就万无一失了。
crossinline
就是为了处理这种情况而产生的。crossinline
保留了 inline
特性,但是如果想在传入的 lambda
里面 return
的话,就会报错。return
只能 return
当前的这个 lambda
。
// Kotlin
fun main(args: Array) {
val methodName = “main”
multiplyByTwo(5) {
result: Int -> println(“call method $methodName, Result is: $result”)
return@multiplyByTwo
}
}
如面代码所示,必须 return@multiplyByTwo
,而不能直接写 return
。
5.泛型
5.1泛型的基本用法
1)、首先我们解释下什么是泛型,泛型就是参数化类型,它允许我们在不指定具体类型的情况下进行编程。我们在定义一个类,方法,或者接口的时候,给他们加上一个类型参数,就是为这个类,方法,或者接口添加了一个泛型
//1、定义一个泛型类,在类名后面使用 这种语法结构就是为这个类定义一个泛型
class MyClass{
fun method(params: T) {
}
}
//泛型调用
val myClass = MyClass()
myClass.method(12)
//2、定义一个泛型方法,在方法名的前面加上 这种语法结构就是为这个方法定义一个泛型
class MyClass{
fun method(params: T){
}
}
//泛型调用
val myClass = MyClass()
myClass.method(12)
//根据 Kotlin 类型推导机制,我们可以把泛型给省略
myClass.method(12)
//3、定义一个泛型接口,在接口名后面加上 这种语法结构就是为这个接口定义一个泛型
interface MyInterface{
fun interfaceMethod(params: T)
}
2)、为泛型指定上界,我们可以使用 <T : Class>
这种语法结构,如果不指定泛型的上界,默认为 Any? 类型
class MyClass{
//我们指定了泛型的上界为 Number, 那么我们就只能传入数字类型的参数了
fun method(params: T) {
}
}
实化泛型
注意JVM上的泛型一般是通过泛型擦除实现的,所以kotlin上的泛型也是如此。但是kotlin可以通过内联函数来实化泛型(reified)
inline fun Iterable<*>.filterIsInstance(): List { //reified声明后不再进行擦除
val des = mutableListOf()
forEach {
if (it is T) des.add(it)
}
return des
}
fun main(args: Array) {
val items = arrayListOf(“one”, 2, “three”)
println(items.filterIsInstance())
}
//打印结果
[one, three]
kotlin为什么可以避免泛型?因为kotlin编译器把实现内联函数的字节码插入每一次调用的地方的时候,带上了实际作为参数的类型。
5.2协变和逆变
在Java中,在 Source 类型的变量中存储 Source 实例的引用是极为安全的——没有消费者-方法可以调用。但是 Java 并不知道这一点,并且仍然禁止这样操作:
void demo(Source strs) {
Source objects = strs; // !!!在 Java 中不允许
// ……
}
为了修正这一点,我们必须声明对象的类型为 Source<? extends Object>,这是毫无意义的,因为我们可以像以前一样在该对象上调用所有相同的方法,所以更复杂的类型并没有带来价值。但编译器并不知道。
所以在Kotlin中,有一种方法向编译器解释这种情况。这称为声明处型变:我们可以标注Source 的参数类型T 来确保它仅从Source 成员中返回(只读取,相当于Java中? extends T)。为此,kotlin提供out 修饰符。
//kotlin
interface Source {
fun nextT(): T
}
fun demo(strs: Source) {
val objects: Source = strs // 这个没问题,因为 T 是一个 out-参数
// ……
}
简单来说:消费者用 in,生产者用 out
一般原则是:当一个类C 的类型参数T 被声明为out 时,它就只能出现在C 的成员的输出位置,但回报是C 可以安全的作为C 的超类。 另外除了 out,Kotlin 又补充了一个型变注释:in。它使得一个类型参数逆变:只可以被写入而不可以被读取(相当于Java中 ? super T)。逆变类型的一个很好的例子是 Comparable:
interface Comparable {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable) {
x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型
// 因此,我们可以将 x 赋给类型为 Comparable 的变量
val y: Comparable = x // OK!
}
6.DSL
6.1DSL 介绍
DSL英文全称:domain specific language,中文翻译即领域特定语言,例如:HTML,XML等 DSL 语言
特点
- 解决特定领域的专有问题
- 它与系统编程语言走的是两个极端,系统编程语言是希望解决所有的问题,比如 Java 语言希望能做 Android 开发,又希望能做后台开发,它具有横向扩展的特性。而 DSL 具有纵向深入解决特定领域专有问题的特性。
总的来说,DSL 的核心思想就是:“求专不求全,解决特定领域的问题”。
6.2Kotin DSL
首先介绍一下Gradle:Gradle 是一个开源的自动化构建工具,是一种基于 Groovy 或 Kotin 的 DSL。我们的 Android 应用就是使用 Gradle 构建的,因此后续写脚本,写插件,我们可以使用 Kotlin 去编写,而且 AndroidStudio 对 Kotlin 的支持很友好,各种提示,写起来很爽。
比如:
例1-请求回调:
open class RequestCallback : Callback {
private val builder: Builder
…
private fun onSuccess(data: T) {
builder.onSuccess?.invoke(data)
}
private fun onError(code: Int, msg: String?) {
builder.onError?.invoke(code, msg) ?: toast(msg)
}
private fun onFinished() {
decrement()
builder.onFinished?.invoke()
}
class Builder {
internal var onSuccess: ((data: T) -> Unit)? = null
internal var onError: ((code: Int, msg: String?) -> Unit)? = null
internal var onFinished: (() -> Unit)? = null
fun onSuccess(func: ((data: T) -> Unit)?) {
onSuccess = func
}
fun onError(func: ((code: Int, msg: String?) -> Unit)?) {
onError = func
}
fun onFinished(func: (() -> Unit)?) {
onFinished = func
}
}
}
fun getCallback(refresh:Boolean = false,dsl: RequestCallback.Builder.() -> Unit): RequestCallback {
…
return RequestCallback(b)
}
//使用
api.fetchQrCode(orderNo, faceSuccess).enqueue(getCallback {
onSuccess {
…
}
onError { _, msg ->
…
}
})
例2-拓展系统Animator监听:
class AnimationCallbackBuilder {
internal var onRepeat: ((animation: Animator?) -> Unit)? = null
internal var onCancel: ((animation: Animator?) -> Unit)? = null
internal var onStart: ((animation: Animator?) -> Unit)? = null
internal var onEnd: ((animation: Animator?) -> Unit)? = null
fun onRepeat(function: ((animation: Animator?) -> Unit)?) {
this.onRepeat = function
}
fun onCancel(function: ((animation: Animator?) -> Unit)?) {
this.onCancel = function
}
fun onStart(function: ((animation: Animator?) -> Unit)?) {
this.onStart = function
}
fun onEnd(function: ((animation: Animator?) -> Unit)?) {
this.onEnd = function
}
}
fun AnimatorSet.registerCallback(listener: AnimationCallbackBuilder.() -> Unit) {
val mListener = AnimationCallbackBuilder().also(listener)
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
mListener.onRepeat?.invoke(animation)
}
override fun onAnimationEnd(animation: Animator?) {
mListener.onEnd?.invoke(animation)
}
override fun onAnimationCancel(animation: Animator?) {
mListener.onCancel?.invoke(animation)
}
override fun onAnimationStart(animation: Animator?) {
mListener.onStart?.invoke(animation)
}
})
}
//使用
set.registerCallback {
onEnd {
}
}
在kotlin中,规定一个方法返回一个可空类型,那么在每次调用的时候都需要打个?判断是否为空是挺麻烦的。比如:
//当然也可以更加定义自己的apply
inline fun T.exist(block: T.() -> Unit) = block()
inline fun curActivity(block: Activity.() -> Unit) = ActivityCollector.currentActivity()?.let(block)
//使用
item?.exist{
this.toString()//这里改变了this的指向,同时this不会为空
}
curActivity{
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
用的时候都需要打个?判断是否为空是挺麻烦的。比如:
//当然也可以更加定义自己的apply
inline fun T.exist(block: T.() -> Unit) = block()
inline fun curActivity(block: Activity.() -> Unit) = ActivityCollector.currentActivity()?.let(block)
//使用
item?.exist{
this.toString()//这里改变了this的指向,同时this不会为空
}
curActivity{
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-8Puw4rJX-1715850330211)]
[外链图片转存中…(img-UdRkhJfs-1715850330212)]
[外链图片转存中…(img-V8f0B12B-1715850330212)]
[外链图片转存中…(img-URB80nMz-1715850330213)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!