全面了解Kotlin

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 * btimes
a / bdiv
a % bmod
a + bplus
a - bminus

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 iv2;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

是不是有了 inlinenoinline,对于我们开发人员来讲就够了呢?就满足了呢?显然不是的。考虑一种情况,我们既想让 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开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值