以下是我的kotlin系列文章
kotlin基础知识—基本语法(一)
kotlin基础知识—基本语法(二)
前言
我个人使用的工具是Android Studio
写这篇文章呢,主要是熟悉了解kotlin
kotlin教程:https://www.yiibai.com/kotlin/kotlin-higher-order-function.html
不讲基本使用
-
查看kotlin字节码和转成java
高阶函数
- 接受函数作为参数或者返回一个函数的函数就叫做高阶函数
fun cost(block: () -> Int) {
val start = System.currentTimeMillis()
block()
println(System.currentTimeMillis() - start)
}
fun sum(a: Int, b: Int): Int {
return a + b
}
fun main() {
cost{
sum(4,5)
}
}
内联函数
内联函数使用关键字inline内联声明,内联函数的使用增强了高阶函数的性能。 内联函数告诉编译器将参数和函数复制到调用站点。
看个例子,我们将刚刚上面高阶函数的代码转成对应Java代码
public final class KotlinKt {
public static final void cost(@NotNull Function0 block) {
Intrinsics.checkParameterIsNotNull(block, "block");
long start = System.currentTimeMillis();
block.invoke();
long var3 = System.currentTimeMillis() - start;
boolean var5 = false;
System.out.println(var3);
}
public static final int sum(int a, int b) {
return a + b;
}
public static final void main() {
cost((Function0)null.INSTANCE);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
我们对cost函数加上inline
inline fun cost(block: () -> Int) {
val start = System.currentTimeMillis()
block()
println(System.currentTimeMillis() - start)
}
fun sum(a: Int, b: Int): Int {
return a + b
}
fun main() {
cost{
sum(4,5)
}
}
转成对应的Java代码
public final class KotlinKt {
public static final void cost(@NotNull Function0 block) {
int $i$f$cost = 0;
Intrinsics.checkParameterIsNotNull(block, "block");
long start = System.currentTimeMillis();
block.invoke();
long var4 = System.currentTimeMillis() - start;
boolean var6 = false;
System.out.println(var4);
}
public static final int sum(int a, int b) {
return a + b;
}
public static final void main() {
int $i$f$cost = false;
long start$iv = System.currentTimeMillis();
int var3 = false;
sum(4, 5);
long var4 = System.currentTimeMillis() - start$iv;
boolean var6 = false;
System.out.println(var4);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
我们可以看到inline将函数本身内联到调用处,并且函数的参数也被内联到调用处
高阶函数的return
val datas= intArrayOf(1,2,3,4)
fun main(){
datas.forEach {
if(it==3)return
println("$it")
}
}
转成对应的Java代码
public static final void main() {
int[] $this$forEach$iv = datas;
int $i$f$forEach = false;
int[] var2 = $this$forEach$iv;
int var3 = $this$forEach$iv.length;
for(int var4 = 0; var4 < var3; ++var4) {
int element$iv = var2[var4];
int var7 = false;
if (element$iv == 3) {
return;
}
String var8 = String.valueOf(element$iv);
boolean var9 = false;
System.out.println(var8);
}
}
return是直接退出整个for循环
return @forEach的使用
val datas= intArrayOf(1,2,3,4)
fun main(){
datas.forEach {
if(it==3)return@forEach
println("$it")
}
}
转成对应Java代码
public static final void main() {
int[] $this$forEach$iv = datas;
int $i$f$forEach = false;
int[] var2 = $this$forEach$iv;
int var3 = $this$forEach$iv.length;
for(int var4 = 0; var4 < var3; ++var4) {
int element$iv = var2[var4];
int var7 = false;
if (element$iv != 3) {
String var8 = String.valueOf(element$iv);
boolean var9 = false;
System.out.println(var8);
}
}
}
实际上return@forEach是跳过指定条件
object类的使用
用object替换class类
object A{
val age:Int=0
}
转成对应Java代码
public final class A {
private static final int age = 0;
public static final A INSTANCE;
public final int getAge() {
return age;
}
private A() {
}
static {
A var0 = new A();
INSTANCE = var0;
}
}
我们可以看到object替换类的使用实际就是一个单例,并将所有属性设置为static
如果想做静态双重校验所单例,怎么做?
class A{
var instance: A? = null
get() {
if (field == null) {
synchronized(this) {
field = A()
}
}
return field
}
companion object {
private var instance: A? = null
}
}
- 推荐单例写法
class Single private constructor(){
companion object{
fun get():Single{
return Holder.instance
}
}
private object Holder{
val instance=Single()
}
}
by没有反射的动态代理
interface View {
fun click()
}
class TextView :View{
override fun click() {
println("View点击")
}
}
class ViewGroup(view:View):View by view
fun main() {
ViewGroup(TextView()).click()
}
结果:打印View点击。
转成对应的Java代码,主要看ViewGroup
public final class ViewGroup implements View {
// $FF: synthetic field
private final View $$delegate_0;
public ViewGroup(@NotNull View view) {
Intrinsics.checkParameterIsNotNull(view, "view");
super();
this.$$delegate_0 = view;
}
public void click() {
this.$$delegate_0.click();
}
}
此时,我们发现kolin在转成Java之后,会将动态代理转成静态代理
泛型
Java中的泛型
我们都知道下面这行代码会报错
List< TextView> mViews=new LinkedList<Button>();
//或者
List<Button> mButtons = new LinkedList<TextView>();
- 理解通配符? extends和?supre
在继承关系中,子类是继承于父类的,所以我们可以理解为父类在上,子类在下,所以子类想赋值给父类,需要使用? extends上界,且我们知道TextView是button的父类,所以改成如下
List<? extends TextView> mViews = new LinkedList<Button>();
同理,父类向赋值给子类,就需要下界,也就是super
List<? super Button> mButtons = new LinkedList<TextView>();
? extends只能读取不能修改(修改指的是添加元素)
? super只能修改不能读取
kotlin中的out和in
使用
var mViews: MutableList<out TextView?>? = LinkedList<Button>()
var mButtons: MutableList<in Button> = LinkedList<TextView>()
out表示只能输出不能输入,你只能读我不能写我(相当于? extends)。in则相反,只能输入不能输出,你只能写我不能读我(相当于? super)
*号
- Java中的?也可以作为泛型通配符,相当于? extends Object
- 而kotlin等效的用法是 *,相当于? extends Any
- 和 Java 不同的地方是,如果你的类型定义里已经有了 out 或者 in,那这个限制在变量声明时也依然在,不会被 * 号去掉。
reified关键字
如果想知道泛型T是什么类型,Java中是无法知道的,因为Java具有泛型擦除功能
而kotlin中可以使用reified关键字,reified必须和inline一起使用
class A {
inline fun <reified T> test(item: Any?) {
if (item is T) {
println(item)
}
}
}
高级特性
解构
fun main() {
val user = User(18, "peakmain")
val (age, name) = user //👈解构
println(age)
println(name)
}
class User(val age: Int, var name: String) {
operator fun component1()=age//👈必须是component+数字
operator fun component2()=name
}
解构可以自动把一个对象拆成一个个属性。解构用的更多的是在map集合中
fun main() {
val map = mapOf("peakmain" to 18, "Treasure" to 19)
for ((key,value) in map){
println("$key——$value")
}
}
kotlin海量操作符
- rxjava操作符
说到操作符,我们通常会想到rxjava,rxjava也具有丰富的操作符
public static void main(String[] args) {
final String[] a = new String[]{"4", "0", "7", "i", "f", "w", "0", "9"};
final Integer[] index = new Integer[]{5, 3, 9, 4, 8, 3, 1, 9, 2, 1, 7};
Observable.just(index)
//拆成一个个integer
.flatMap(new Function<Integer[], ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(Integer[] integers) throws Exception {
return Observable.fromArray(integers);
}
})
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) throws Exception {
return integer < a.length;
}
})
.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
return a[integer];
}
}).reduce(new BiFunction<String, String, String>() {
@Override
public String apply(String s, String s2) throws Exception {
return s + s2;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
System.out.println(s);
}
});
}
那么同样的功能kotlin如何实现的呢
fun main() {
val a = arrayOf("4", "0", "7", "i", "f", "w", "0", "9")
val index = arrayOf(5, 3, 9, 4, 8, 3, 1, 9, 2, 1, 7)
index
.filter {
it < a.size
}.map {
a[it]
}.reduce { s, s1 ->
s + s1
}
.also {
println(it)
}
}
filter和rxjava的filter一样,代表过滤,map转换,reduce从第一项到最后一项进行累计,会将上个结果继续操作,最后一次输出,also相当于rxjava的subscribe
几个常用的作用域函数
- run函数
val user: User = User("peakmain")
user.run { "let::${this.name}" }
源码分析
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
我们可以看到这实际就是将T进行扩展,扩展的方法为run,且没有闭包参数
- let
let和run都会返回闭包执行的结果,let有闭包参数,run没有闭包参数
使用
val user: User = User("peakmain")
user.let { "let::${user.name}" }
直接看源码
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
我们可以看到run和let的主要区别在于,run是this.T,而let是参数T,所以推荐使用let,且有闭包参数
- also和apply
laso和apply都不会返回闭包执行结果,区别在于also有闭包参数,apply没有闭包参数
also源码
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
apply源码
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
同理:推荐also
- takeIf和takeUnless
takeIf的闭包返回一个判断结果,为false时,takeIf函数会返回空
takeUnless则相反,闭包的结果为true的时候返回空 - repeat
重复执行当前闭包 - use
当我们需要关闭一些流,可使用此方法有效避免内存泄漏
源码
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
- with
var datas= intArrayOf(1,2,3,4)
fun test(){
with(datas,{
datas[0]=11
datas[1]=12
})
}
源码
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
所以上面代码实际类似下面代码
datas.also {
datas[0] = 11
datas[1] = 12
}
with不是一个扩展函数,它是将参数执行了方法