Dart 语言不如 Kotlin?这里列了 13 个原因

【CSDN 编者按】同受 Google 支持,一个是其官方开发的语言——Dart,一个被扶持为 Android 开发的官方语言——Kotlin。放在一起看,Kotlin 对比 Dart 的优越性远不止更安全这么简单,本文作者罗列出 13 个理由。

作者 | Joel       

译者 | 弯月        责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

我非常喜欢 Flutter,我认为它是开发高质量多平台应用的最佳选择之一,然而,我却不怎么喜欢 Dart(编写 Flutter 的语言)。为什么?和 Kotlin 相比,原因如下。

没有 null 安全

我知道这个问题近期就能得到解决(https://dart.dev/null-safety),但目前我们仍然需要面对。2018 年,Dart 首次发布的时候(我指的是 Dart 2),就没有 null 安全,太不应该了。

 

没有数据类 

数据类的主要目的是保存数据。在数据驱动开发模型中,数据类就是值对象,而且在适当的语言支持下,不可变的数据类型非常方便函数式编程。

Kotlin 有下面这样的支持:

data class User(val name: String, val age: Int)
  val user1 = User(name =“John”, age = 30)
  val user2 =user1.copy(age = 20)

即便不考虑复制(和相等比较)功能的实现,Dart 的写法也非常冗长:

class User {
  User({this.name,this.age});
  final String name;
  final int age;
}


final user1 = User(name: ‘John’, age: 30);
final user2 = User(name: user1.name, age: 20);

而且 Dart 还缺少 Kotlin 提供的某些功能。例如,在 Kotlin 中,你可以将这些参数作为位置参数或命名参数进行传递(甚至可以混合使用:val user = User('John', age = 30)),而在 Dart 中,位置或名称,你只能选择其一(但是它们是可选的,默认为 null)。

有一个很好的包 build_value,可以解决深度相等性检查和生成复制方法的问题,我们经常在代码库中使用这个包,但这不是理想的解决方案。

首先,与 Kotlin 版本相比,Dart 需要一些样板代码:

abstract class User implements Built<User, UserBuilder> {
  User._();
  factory User([voidFunction(UserBuilder) updates]) = _$User;
  String get name; int getage;
}


final user1 = User((b) => b ..name = ‘John’ ..age = 30);
final user2 = user1.rebuild((b) => b..age = 20);
 

其次,Dart 缺乏一些功能,比如没有简单的方法让编辑器在编译期间检查必须参数是否传递。

Dart 能够提供数据类的支持吗?可能会,但近期内还不行。

 

没有密封类

 

我们经常使用的 Kotlin 的另一个功能是密封类。从本质上讲,密封类表示了一种受限的类层次结构:值只能是有限的几种类型中的一种。与枚举不同,这个值不是 singleton,而是一个适当的类,可以拥有多个不同状态的实例。

为什么密封类很实用?我们来看一个很常见的例子:

sealed class Result<out VALUE> {
  data class Error(valerror: Throwable) : Result<Nothing>()
  data classSuccess<VALUE>(val value: VALUE) : Result<VALUE>()
}


fun process(result: Result<String>): String = when(result) {
  is Result.Error ->“Error: ${result.error}”
  is Result.Success ->“Success: ${result.value}”
}

用这个方法来代替异常处理很不错:它并不是去捕获异常(因为有时候会忘记捕获异常),而是强迫使用者处理结果,结果可能是错误的或正确的(在许多函数式语言和库中该类型被称为 Either)。不仅你需要考虑可能出现的错误,而且 Kotlin 也会提供一些很好的功能。你看,when 分支中有类型转换吗?你不需要进行类型转换,因为 Kotlin 很聪明,会自动进行转换。

那么 Dart 呢?我们希望有一天 Dart 也能提供这些功能。

 

枚举不支持自定义值

 

有时,我们需要在枚举中添加一些值。Kotlin 的写法很简单:

enum class Level(val value: Int) {
  INFO(10),
  WARNING(20),
  ERROR(30)
}


val value = Level.WARNING.value

Dart 没有类似的功能。但你可以使用扩展:

enum Level {
  info,
  warning,
  level
}


extension LevelValue on Level {
  int get value {
    switch(this) {
       case Level.info:
         return 10;
       case Level.warning:
         return 20;
       case Level.level:
         return 30;
    }
  }
}


final value = Level.warning.value;

首先,这段代码太冗长了;其次,请参照下面这一点。

 

编译器不够智能

 

前面的示例给出了如下警告:

This function has a return type of ‘int’, butdoesn’t end with a return statement. Try adding a return statement, or changingthe return type to ‘void’.

什么意思?Level 枚举只有 3 个选项,而且都列出来了。该函数不可能返回 void。我不想添加 default 语句(否则,如果我在枚举中添加了另一个选项,但忘记了更新扩展,就可能会返回错误的结果)。出于相同的原因,我不想忽略这个警告。我希望当且仅当所有选项都不匹配的时候报错,而 Kotlin 就是这么做的。

 

没有 singleton

 

如何在Kotlin中定义singletons?很简单:object Singleton。Dart有类似的东西吗?最简单的写法可能是:

class Singleton {
  const Singleton._();
  factory Singleton()=> const Singleton._();
}

当然,这也没什么大不了,但是如果你必须针对每种情况重复编写上述代码,那么工作量就大了。在 Kotlin 项目中,我们经常使用 singletons,例如作为密封类的“常量”等等,但 Dart 中连密封类都没有。

没有 switch / if / try 表达式

 

还记得在讨论 Dart 没有密封类时,我们提到的示例吗?虽然没有关键字 return,但函数仍然会返回字符串。这有可能要归功于两个方面:

  • fungetAnswer(): String = "42"  等价于 fun getAnswer(): String { return "42" }。在 Dart 中,你甚至只需编写 StringgetAnswer() => '42';

  • when 是一个表达式:这意味着你可以返回 when 的结果,并且编译器足够聪明,可以推断出正确的类型,因为每个条件分支都会返回 String。

这不仅仅是语法简洁性的问题,编译器返回 when,是为了强迫我们提供所有的选项(比如对于枚举或密封类,你必须指定所有选项或使用 else 语句)。

 

没有关键字 protected

 

为了私有化方法(或变量),我们需要在其名称前添加下划线。然而,当你需要从允许的范围之外访问该方法时,就会遇到编译错误。

为了保护某个方法,你可以使用 meta 包中的 @protected 注释。但是,你会收到静态分析的警告。

Dart 不支持 protected,所以要么是 public,要么是 private,不能折中。

没有类型别名

 

实际上,Dart 中有类型别名,但仅适用于函数类型。

你可以编写如下代码:

typedefFormatDate = String Function(DateTime);

但是不能写:

typedefJson = Map<String, dynamic>;

如果你的项目需要使用 json,就要在代码中书写大量的Map<String, dynamic>。

也许,有一天 Dart 会解决这个问题吧……

没有简洁的语法

 

你可能想说:“既然你已经习惯了 Kotlin 的语法,为什么不直接使用 Kotlin?为什么还要用 Dart?”

没错,我是很喜欢 Kotlin 的语法,但不仅仅是因为我习惯了。在 Kotlin 中编写 lambda 语法非常简洁,比如 listOf(1, 2, 3).map {it.toString() },远胜于[1,2,3].map((i) => i.toString());。如果遇到很多行的 lambda(或者 lambda 链),Dart 的书写就会非常复杂。甚至还需要分号……

 

没有嵌套类/扩展

 

我非常希望 Dart 能支持嵌套类。比如我们想做一些消息处理:

abstract class Translations {
  static abstract classCommon {
    static String yes =‘Yes’;
    static String no =‘No’;
  }


  static abstract classAuth {
    static String logIn =‘Log in’;
    static String logOut =‘Log out’;
  }
}


final message = Translations.Auth.logIn;

没有恰当的泛型协变

 

在 Dart 的类型中,泛型类中的变量是协变的。这有什么不好?因为这种方法无异乎自讨苦吃。我们来看一个例子:

class Animal {}


class Dog extends Animal {}


class Cat extends Animal {}


void main() {
  List<Animal>animals = [Dog()];
  List<Cat> cats =[];
  cats.add(animals.first);
}

虽然可以通过编译,但是运行的时候会出错:TypeError: Instance of 'Dog': type 'Dog' is not a subtype of type'Cat'。

如果在 Kotlin 中尝试这样的小把戏:

abstract class Animal


class Dog : Animal()


class Cat : Animal()


fun main() {
  val animals =listOf<Animal>(Dog())
  val cats =mutableListOf<Cat>()
 cats.add(animals.first())
}


编译都不过去,它会直接报错:Type inference failed. Expected type mismatch: inferred type isAnimal but Cat was expected。

 

没有 final 类

 

《Effective Java》这本书中说:“继承必须经过设计,并通过文档说明,否则就不该使用。”因此,在 Java 中,我们应该尽量使用 final 类。

Kotlin 则更进一步,默认情况下所有类都是 final。那么,Dart 呢?我们根本没办法指定 final 类,没办法禁止继承。

Dart 真的“一文不值”?

 

虽然上面我罗嗦了一大堆 Dart 的不足,但 Dart 也有一些我非常喜欢的优点:

  • 类声明本身就是接口。每个类都定义了一个由 public 成员组成的接口。因此,你只需 implement 类,并提供重载功能:在测试中模拟实现。

  • 无类型擦除。与Kotlin不同,List <String>在运行时仍然是List<String>。

  • 最后,很重要的一点:Dart 仍在不断发展中。我们有扩展程序,也许很快就能获得 null 安全。

希望有一天,本文所提及的不足都能得到改善,Dart 能够成为一门安全的现代编程语言。

原文链接:https://medium.com/codex/13-reasons-why-dart-is-worse-than-kotlin-eba93dfedd8

声明:本文为 CSDN 翻译,转载请注明来源。



☞三年白干!程序员孙某因违反《竞业协议》赔偿腾讯 97.6 万元,返还 15.8 万元☞CTO 写低级 Bug,致公司 70 GB 数据遭泄露!
☞再见!经典版Edge!
☞Google 重磅发布 Flutter 2 !一套代码横扫 5 大系统
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页