Kotlin开发者测验Flutter——错怪了Dart这门言语

前言

我曾经是Java安卓开发者,进入大学后了解并且转向了Kotlin开发安卓,时至今日现已有了一年时刻,Kotlin带给我的体会实在是太惊艳了,我坚信这便是我最喜爱的言语了。

抱着这种观点,我发现了Flutter+Dart这种笼统的组合,大量的嵌套如同让我在写ifelse,这导致了我迟迟没有触摸Flutter跨渠道结构,当然还有一些其他原因。

其实在之前Flutter的跨渠道能力现已惊艳到我了,这次寒假正好有时机让我学习它。

当我试着用它完结业务时,我发现如同也不是那么不可承受,我甚至还有那么点快感,假如你写过安卓Compose那么你会愈加觉得如此,因为在UI和业务的关系上它真的太简单绑定了,我不再考虑像XML监听数据变化,只可惜Dart语法仍然在一些地方让我感觉到不太好用,仍是得Kotlin来,等等,那我不便是想要Compose吗?

哈哈,不要着急,为什么这个项目是Flutter而不是KMP随后咱们再说。

其实我自身没有很严重的技术癖,面对新的事物和技术,一旦有合适的时机我都是乐意试一试,比起结构的挑选,我愈加享受开发过程,思想转换为代码的那一刻至少我是享受的。

这次挑选Flutter开发不意味着我会一向挑选和追捧它,更不会放弃安卓原生和KMP的学习,因而也期望阅览这篇文章读者意识到这点,我作为原生开发者学习Flutter是期望扩展技术而不是代替原生,Flutter自身也不是这么想的,它更像是给咱们了一个更低的开发门槛,让更多其他领域的创作者完结他们著作的一种前言。

假如你期望快速了解Kotlin开发者运用Dart的开发体会,那么直接跳过下面两部分,直接阅览#错怪的Dart。

动机

我觉得首要动机由两部分组成吧,一部分是跨渠道开发自身是我感兴趣的方向之一,另一边是未来作业或许需求吧,现在来看国内跨渠道趋势仍是比较明显的。

不过我更期望这次项目是体会移动跨渠道开发,而不是真实的深入学习移动跨渠道开发。为此,我期望能够找到学习成本和项目质量相平衡的开发方式,很惋惜我没有那么多的精力做到既要还要,这是我有必要面对的挑选。

面对众多跨渠道结构下我仍是挑选了Flutter,这首要与它的跨桌面端和生态完善有关,毫无疑问,Flutter有许多的成品组件,这让我能够快速轻松的上手跨渠道开发

为什么是Flutter

这个项目的首要功用便是播映器,只不过这个播映器比较特殊,后续文章咱们会揭晓它。

单就网络音频播映器开发使命而言,假设运用KMP或许没有现成封装好的库来给我用,或许许多开发者考虑没有就造一个,很惋惜,我不太具备这样的能力,咱们需求一起对接多个渠道的媒体播映,无论开发周期,单就这样的使命对我现已是很难了。

好吧,我想有必要供认我很菜,可是事实如此,因而我挑选了愈加成熟的Flutter,防止我写不出来,哈哈哈哈。

不过咱们今日先不谈Flutter,咱们看看Dart。

错怪的Dart

对Dart的刻板印象是从我第一次见到Flutter的语法时形成的,第一次见到Dart时我还没有触摸Kotlin。

看着有点像Java,还有很多_的姓名是什么鬼东西、怎样要写这么多return、为什么有个?、总归便是反人类啊!!!

当我真实测验去编写Flutter程序时,我发现,嗯,错怪Dart了,特别是因为我了解Kotlin后,Kotlin和Dart也有几分相似之处,这体现在一些语法特性上。

空安全

可空类型在Kotlin上能够说相当不错,在Dart上也能够体会到它,虽然它是类型前置,可是写法却是相同的在类型后加上”?”即可。

class AudioMediaItem {
  String title;
  String description;
  AudioMediaType type;
  String? mediaUrl;
  String? bvId;
  //省略其他代码.....
}

当咱们企图运用AudioMediaItem的目标时,咱们就能够像Kotlin那样做,留意mediaUrl现在是可空的。

audioMediaItem?.mediaUrl,假如咱们以为这个属性一定有值,那么就能够运用audioMediaItem!.mediaUrl,需求留意的是,dart中是”!”而不是”!!”

假如你期望运用Kotlin的Elvis操作符 ?: ,那么你能够这么做

audioMediaItem?.mediaUrl ?? "default";

对应Kotlin的

audioMediaItem?.mediaUrl ?: "default"

在这方面,dart和Kotlin是非常相似的,因而,你能够非常平滑的搬迁这部分的开发体会和理解。

推迟初始化

在Kotlin中,咱们能够运用lateinit var定义一个非空推迟初始化的变量,通俗的讲便是定义一个非空类型,可是不给初始值。dart也有对应从要害字,那便是late了。

late String name;

相当于Kotlin的

lateinit var String name

咱们知道推迟初始化意味着这个值必定有值,仅仅咱们期望这个值在代码运行过程中产生并且初始化,初始化后再运用该值,不然就会空指针了。

假如你现已熟悉了Kotlin的lateinit,那这儿也能够平滑搬迁了。

可是在Android Studio 2023.1.1我发现个有意思的事情。

late String? name;

ide没有提示这是过错的,我没试着运行,可是我觉得这应该是不合理的。

扩展函数

扩展函数在Kotlin傍边能够说相当重要,许多内置函数都是这个特性所带来的。

在Kotlin中,咱们通过 被扩展的类名.扩展函数名(){} 这样的写法就完成了一个扩展函数。

fun String.toColorInt(): Int = Color.parseColor(this)

Dart中也存在扩展函数的语法糖!

extension StringExtension on String {
  /// 字符串的首字母大写
  String capitalize() {
    if (isEmpty) {
      return this;
    }
    return '${this[0].toUpperCase()}${substring(1)}';
  }
}

其间,StringExtension仅仅这个扩展的姓名,相当于一个标志,能够随意起,on String则代表扩展String类,那么capitalize 自然便是扩展的办法名了。

将Kotlin的内置函数带入

Kotlin的内置函数实在是太棒了,下面以also和let为比如,模仿了Kotlin的扩展函数,只可惜Dart的lambda不太能像Kotlin那样,仍是有一些分裂。

extension AlsoExtension<T> on T {
  T also(void Function(T) block) {
    block(this);
    return this;
  }
}
extension LetExtension<T> on T {
  R let<R>(R Function(T) block) {
    return block(this);
  }
}
//用法
String demo = "xada".let((it) => "${it}xadadawdwad");

emm不过因为没办法直接传this,在变量很长或者类型可空时还有点用。

顶层函数

Kotlin中,咱们有时候需求在大局运用一些函数,可是不期望写在类里,而是随时随地直接能够调用或者拿到。

留意这些代码不在类里

val json = Json {
    prettyPrint = true
    isLenient = true
    ignoreUnknownKeys = true
}
var retrofit = Retrofit.Builder()
    .baseUrl("https://api.juejin.cn/")
    .addConverterFactory(json.asConverterFactory(MediaType.parse("application/json;charset=utf-8")!!))
    .build()

在某个类需求咱们就直接写retrofit.xxxx() 就能够了,咱们不需求再独自从类中找。

Dart也有这样的功用

final _cookieJar = CookieJar();
final Dio dioClient = Dio(BaseOptions(
  baseUrl: baseUrl,
  connectTimeout: const Duration(seconds: 5),
  receiveTimeout: const Duration(seconds: 5),
  contentType: Headers.jsonContentType,
  persistentConnection: true,
))
  ..transformer = BackgroundTransformer()
  ..let((it) {
    if (!kIsWeb) {
      it.interceptors.add(CookieManager(_cookieJar));
      return it;
    } else {
      return it;
    }
  });

上面的比如仅仅写了变量,写函数也是相同的,都能够直接在大局任何的方位调用。

高阶函数

在Kotlin中,高阶函数是特殊的一种函数,这种函数承受了另一个函数作为参数。

咱们以Kotlin的forEach函数为比如:


public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}
// 用法
fun main() {
    val demoList = listOf("da", "da", "da")
    demoList.forEach {
        println(it)
    }
}

forEach自身扩展了Iterable,可是它的参数非常特殊,咱们看看action参数的类型:

(T) -> Unit,这是Kotlin匿名函数的写法,意味着这个函数有一个参数,类型为T泛型,这个参数也没有起姓名,所以就只要类型T在。

这种状况,在Java中这种完成一般是接口类,咱们需求实例化这个匿名类,假设这个接口只要一个办法,那么就能够转换为lambda的写法。

在Kotlin里咱们能够直接写为lambda的方式,要方便很多,因为只要一个参数,那么kotlin默许就叫it了。

OK回顾完Kotlin,咱们看看Dart:

void forEach(void action(E element)) {
  for (E element in this) action(element);
}
//用法
List<String> demoList = ["da","da","da"];
demoList.forEach((element) { 
   print(element);
});

其实不同不大,仅仅咱们需求写void当作这个参数的类型,内部写法没有太大差异。

不过,Dart的lambda愈加贴近JS,写法根本上是一模相同。

信任假如你现已把握了Kotlin的高阶函数,那么在Dart测验也是不错的。

运算符重载

Kotlin傍边有个不太常用的东西,叫运算符重载,它在Dart中也有。

public operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> {
    if (elements is Collection) {
        val result = ArrayList<T>(this.size + elements.size)
        result.addAll(this)
        result.addAll(elements)
        return result
    } else {
        val result = ArrayList<T>(this)
        result.addAll(elements)
        return result
    }
}
//用法
val demoList = listOf("da", "da", "da") + listOf<String>("add")

能够看到kotlin通过operator要害字合作扩展函数完成了这个功用,dart也能够模仿这种手法:

// 模仿
extension ListPlusOperatorExtension<T> on List<T>  {
  List<T> operator +(List<T> elements) {
    List<T> result = this;
    addAll(elements);
    return result;
  }
}
// 用法
List<String> demo1 = ["da","da"];
List<String> demo2 = ["da","d1a"] + demo1;

不过这儿的加减乘除便是operator + 了。

总结

能够看得出,Dart也有部分咱们在Kotlin中喜爱的特性,假如你现已把握了Kotlin的根本语法,那么信任Dart对你来说也不是太大问题,你能够平滑的搬迁一些在Kotlin中的知识到Dart上去。

起初我是很坑距运用Flutter的,现在看见Dart的特性,我好像又承受了一些,好吧,对于Flutter开发、布局约束和其他感受我在下一篇文章再共享给咱们吧。

最终感谢咱们看到这儿,还有什么好玩的特性欢迎在下面留言,文章内容有过错请指出。