全面了解Kotlin

//定义一个成员函数 方法内的this会指向被拓展的对象。
//即这里的this是这个字符串
fun String.print() = println(this)

//使用
“string extension”.print()

//输出
string extension

拓展函数也是顶层函数,所以它在Java中也是静态函数,调用如下:

TopFuncKt.print(“extension in Java”);

拓展函数仅仅是为了缩短语法而存在。并非真正意义上的"拓展",也做不到真正的拓展,所以拓展函数无法进行重写,或者在Java中当作成员函数来调用。

3.3.2拓展属性

类似拓展函数,拓展属性提供了一种方法,用来拓展类的API,可以用来访问属性,用的是属性语法而不是函数的语法。**尽管它们被称为属性,但他们可以没有任何状态,也没有合适的地方来存储它们,**不能给现有的Java对象的实例添加额外的字段。

var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
setCharAt(length - 1, value)
}

**上面的代码只是利用拓展属性提供了一种快捷访问该类成员方法的途径,但是并没有给StringBuilder这个类添加lastChar这个属性。**尽管如此,拓展属性依旧十分实用,比如Android中常用的Float转换为dp:

val Float.dp
get() = get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)

3.4 可变参数和中缀调用

vararg:可变参数。只需要注意一点

fun test(vararg string: String){
test2(string)//这里传递可变参数需要添加
}

fun test2(vararg string: String){

}

中缀调用:

//允许使用中缀符号调用函数,函数会返回一个Pair对象
infix fun Any.with(other:Any) = Pair(this,other)

val c = “3” with 4 // c为pair对象
val (f, s) = “3” with 4 //val (f,s)称为解构声明,将Pair的first给f,second给s.
println(“f: f , s : f,s: f,s:s”)

//输出结果
f:3,s:4

4.程序逻辑控制

4.1带返回值的if

kotlin没有三目运算符,取而代之的是带返回值的if语句。而且是把每一个条件中的最后一行代码作为返回值

//Kotlin 中把每一个条件中的最后一行代码作为返回值
fun largeNumber(number1: Int,number2: Int) : Int{
return if(number1 > number2){
number1
}else {
number2
}
}

//根据上面学习的语法糖和 Kotlin 类型推导机制,我们还可以简写 largeNumber 函数,最终变成了这样
fun largeNumber(number1: Int,number2: Int) = if(number1 > number2) number1 else number 2

4.2带返回值的when

类比 Java 中的 Switch 语句学习,Java 中的 Switch 并不怎么好用:

①Switch 语句只能支持一些特定的类型,如整型,短于整型,字符串,枚举类型。如果我们使用的并非这几种类型,Switch 并不可用

②Switch 语句的 case 条件都要在最后加上一个 break

这些问题在 Kotlin 中都得到了解决,而且 Kotlin 还加入了许多强大的新特性:

when 条件语句也是有返回值的,和 if 条件语句类似,条件中的最后一行代码作为返回值

when 条件语句允许传入任意类型的参数

when 条件体中条件格式:匹配值 -> { 执行逻辑 }

⑥when 条件语句和 if 条件语句一样,当条件体里面只有一行代码的时候,条件体的 {} 可省略

//when 中有参数的情况
fun getScore(name: String) = when (name) {
“tom” -> 99
“jim” -> 80
“lucy” -> 70
else -> 0
}

//when 中无参数的情况,Kotin 中判断字符串或者对象是否相等,直接使用 == 操作符即可
fun getScore(name: String) = when {
name == “tom” -> 99
name == “jim” -> 80
name ==“lucy” -> 70
else -> 0
}

4.3循环语句

kotlin中主要有两种循环语句:while和for-in。其中while和Java中使用一致。

而kotlin中的for-in则比Java中更为方便易用。

for-in:

//使用 … 表示创建两端都是闭区间的升序区间[0,10]
for (i in 0…10){
print("$i ")
}
//打印结果
0 1 2 3 4 5 6 7 8 9 10

for-until:

//使用 until 表示创建左端是闭区间右端是开区间的升序区间[0,10)
for (i in 0 until 10){
print("$i ")
}
//打印结果
0 1 2 3 4 5 6 7 8 9

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 = “hel
lo 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{
startActivity(intent)//同上,this为ActivityCollector.currentActivity()获取到的不为空的activity,
//当然为空的时候,则不执行,需要警惕
}

其他

带默认实现的接口,比如截图权限配置:

interface ICaptureView {

fun canCapture() = false
}

不能截图的类只需要实现接口,能够截图的实现重写即可。

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取
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{
startActivity(intent)//同上,this为ActivityCollector.currentActivity()获取到的不为空的activity,
//当然为空的时候,则不执行,需要警惕
}

其他

带默认实现的接口,比如截图权限配置:

interface ICaptureView {

fun canCapture() = false
}

不能截图的类只需要实现接口,能够截图的实现重写即可。

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-YKp5VlCw-1719339043438)]

[外链图片转存中…(img-nI3LAHsk-1719339043438)]

[外链图片转存中…(img-IhG0Gx2N-1719339043439)]

[外链图片转存中…(img-bSsQQSzr-1719339043439)]

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值