Dart vs Swift

发布于: 2020 年 06 月 28 日
Dart vs Swift

前言

Dart、Swift 目前是开发用的比较多的语言,他们都是比较现代化的语言,在语法方面都比较像,本文会对其语言进行一下对比,便于记忆和语言过渡。本次的对比是基于 Dart 2.8.3 和 Swift 5.2 的版本进行。

预览

(😅居然不支持Markdown 表格)

变量声明

Swift  中语法如下:

var name:String
var age:Int
var height:Double
//赋值语法
var name = "Summer"
var age = 6
var height = 1.12

Dart 中语法如下:

String name;
int age;
double height;
//赋值语法
var name = "Summer";
var age = 6;
var height = 1.12;

由于两种语言都有类型推断,所以可以通过表达式中的值推断出类型。 Dart 需要增加分号, Swift 中可以不加分号(根据习惯你也可以加)。

更多类型,对比下文 Swift 会更严格一些对于混合型的需要加 Any 表明,个人其实也同意 Swift 的做法,如果类型为 Any 意味着你将失去编译器的类型检查机制,隐藏的挖坑。对于动态类型 Swift 使用 Any 关键词,而 Dart 使用 dynamic 。

  • Swift 

var mapArgsMix = ["arg1":"hello", "arg2":"world", "arg3":2020] as [String:Any]
var listArgsMix = ["hello","world",2020] as [Any]
var mapArgs = ["arg1":"hello", "arg2":"world"] //[String:String]
var listArgs = ["hello","world"] //[String]

  • Dart 

var maxArgs = {"arg1":"hello", "arg2":"world","arg3":2020} // Map<String,Object>
var listArgs = ["hello","world",2020] //List<Object>

Swift 的类型扩展阅读链接,Dart 的在此链接

可变与不可变变量声明

我们编程过程经常用到可变变量与不可变变量,两者都可以通过 var 来声明可修改变量,声明不可变变量的语法有所不同。

  • Swift

var age = 60
age = 18

  • Dart

var age = 60;
age = 18

对于 Swift 使用 let 关键字, Dart 使用 final 和 const 来声明。 let 和 final 的作用相似,都可以声明不可变变量,而且 Swift 的声明非可选类型和 Dart 的 final 声明的变量必须初始化,否则会编译报错。 Dart 的 const 是编译时常量,相对 final 会增加严格,如同是 final 和 const 声明的数组,前者声明的数组变量内部可以修改,但是 const 声明的数组内部也不允许修改,而 Swift 用 let 声明的数组编译时就不允许访问下标,想一想 NSArray  和 NSMutaleArray 关系就理解了。

  • Swift

let age = 60
age = 18 //Cannot assign to value: 'age' is a 'let' constant
let children = ["summer","winter"]
children[0] = "spring" //Cannot assign through subscript: 'children' is a 'let' constant

  • Dart

final age = 60;
age = 18; //'age' can't be used as a setter because it's final.
final child = ["summer","winter"];
child[0] = "Spring";
const balls = ["football","basketball"];
balls[0] = ["ping-pong"];// 运行时报错 UnsupportedError (Unsupported operation: Cannot modify an unmodifiable list)

函数声明

Swift 和 Dart 中函数都是一等公民,因此都可以作为参数传递,变量声明,作为返回值等。

  • Swift

func foo()
func bar() -> Int

  • Dart

void foo();
int bar();

查看关于函数的介绍更多: SwiftDart

参数命名与非命名参数

Swift 和 Dart 都支持参数命名和非参数命名的方式传参,两种实现方式都很友好。看几个例子。

  • Swift

//带参数名的传参
func foo(name: String, age: Int, height: Double)
foo(name: "summer", age: 6, height: 1.12)
//不带参数传参
func foo(_ name: String,_ age: Int,_ height: Double)
foo("summer",16,1.12)

  • Dart

//带参数名的传参
void foo({String name, int age, double height});
foo(name: "summer", age: 6, height: 1.12);
foo(age: 6,name: "summer",height: 1.12);//支持参数顺序变换
//不带参数传参
void foo(String name, int age, double height); // 对比上面的函数去掉了大括号
foo("summer",16,1.12);

从调用的角度看都是一样的,Dart 还有个好用的语法糖。如 foo({ this.name, this.age, this.height }) ,可以保证传参名字和内部变量名称相同。

函数参数可选和默认值

两者都支持参数可选,也支持设置默认值。设置默认值后可以通过传参时指定值覆盖掉默认值,提供这个语法很大的好处是读代码的时候可以一目了然,那些值不传的话默认值会不会设置。

  • Swift

func foo(name: String, age: Int = 0, height: Double = 0.8)
foo(name:"spring",age:3)

  • Dart

void foo(String name, [int age = 0, double height = 0.8]);//指定顺序
void foo(String name, {int age = 0, double height = 0.8});//同上
void foo({String name, int age = 0, double height = 0.8});//不指定顺序
foo(name:"spring",age:3);

更多阅读查看这里 Swift 文档, Dart 的文档

闭包

由于函数作为一等公民,因此可以做为参数进行传递,对比 Java,Swift 和 Dart 对闭包的支持更加强大,高阶函数的提出,其实也是对编程的函数高阶抽象。

  • Swift

let list = ["car","bike","motorcycle"]
list.forEach { (item) in
print("this transport is \(item)")
}
//或者如下
list.forEach({print("this transport is \($0)")})

在 Swift 中可以用 $0 、 $1 代替第几个参数,可以简化写法

  • Dart

final list = ["car","bike","motorcycle"];
list.forEach((element) {print("this transport is $element");});
//或者
list.forEach((element)=>print("this transport is $element"));

这里如果直接 return 的匿名函数写法可以简化为 ()=>dosomething 闭包经常用在异步编程中作为回调参数,我们将会在异步编程这一节详细介绍。

查看更多:Swift 闭包介绍,Dart 异步编程

元组(Tuple)

元组在函数返回多个值的时候非常有帮助, Dart 目前还不支持,有第三方库的实现,但是不推荐,可读性没有Swift 的元组清晰,建议返回定义的数据结构解决这个问题。

  • Swift

let tpHasName = (max: 3, min: 0)//推荐写法
print("max is \(tpHasName.max) and min is \(tpHasName.min)")
let tpNoName = (3,0)
print("first is \(tpNoName.0) and second is \(tpNoName.1)")

控制流

Swift 的 IF 条件和循环控制都还中规中矩,到 Switch 的时候就放飞自我了。Dart 相对来说没有那么多语法,相对简单。

循环

  • Swift

let list = ["car","bike","motorcycle"]
for item in list {
print(item)
}
for i in 0..<list.count {
print(list[i])
}
for i in 0...2 {
print(list[i])
}
var i = 0
while i < list.count {
print(list[i])
i += 1;
}
var j = 0
repeat {
print(list[j])
j += 1
}while j < list.count

  • Dart

var list = ["car","bike","motorcycle"];
void loops(){
for(var item in list){
print(item);
}
for(int i=0;i<list.length;i++){
print(list[i]);
}
var k = 0;
do {
print(list[k]);
k++;
} while (k < list.length);
var j = 0;
while (j < list.length){
print(list[j]);
j++;
}
}

IF

条件的判断上 Swift 不用 () 来包含条件,这与 let 语法解包可能有一定关系。 Dart 采用的是传统的 () 包含条件。

  • Swift

var name:String? = "summer"
if let n = name {
print(n)
}
var num = 7
if num > 10 {
print("num is big than 10")
} else if num < 5 {
print("num is smaller than 5")
} else {
print("this num between 5 and 10")
}

  • Dart

var num = 7;
if (num > 10){
print("num is big than 10")
} else if (num < 5){
print("num is smaller than 5")
} else {
print("this num between 5 and 10")
}

Switch

结合枚举 Swift 可以用 Switch 玩出各种特色,除此之外还可以 switch 字符,元组, case 可以增加多个条件,通过默认不需要写 break ,默认只执行匹配到第一个 case ,可以通过 fallthrough 像其他语言一样匹配下一个 case 。Dart 相对比较简单就是普通的语句。

  • Swift

var num = 8
switch num {
case 0...9:
print("we meet in 0..9")
fallthrough
case 7..<10:
print("we meet in >=7 && < 10")
case 5,6,7,8:
print("we meet in 5 6 7 8")
default:
print("we meet nothing")
break;
}
var tp = (x:0,y:10)
switch tp {
case (_,10):
print("y is match")
case (let x, 10):
print("y is match and x is \(x)")
case let (x,y) where x == y :
print("x is equal to y and x is \(x) y is \(y)")
case let (x,y):
print("x:\(x) and y:\(y) is normal")
}

  • Dart

var lang = "ch";
switch (lang) {
case "ch":
print('language is ch');
break;
case "en":
print('language is en');
break;
default:
print("language is $lang");
}

其他

Swift 中还有一些独特的关键字,如 defer , guard 查看官网文档吧。

集合类型

集合类型是非常常用的类型。

数组(list)

  • Swift

var emptyArray = [Int]()
var list = [1, 2, 4]
list.count // 3
list[1] // 1

  • Dart

var emptyArray = <int>[];
var list = [1, 2, 4];
list.length; // 3
list[1] // 1

集合(Sets)

  • Swift

var emptySets = Set<String>()
var transpot = Set<String>(["car", "bike", "motorcycle"])

  • Dart

var emptySets = <String>{};
var transport = {"car", "bike", "motorcycle"};

键值类型(Map)

  • Swift

var emptyMap = [String:Int]();
var nameOfAge = ["summer" : 6, "spring" : 3];

  • Dart

var emptyMap = Map<String,Int>();
var emptyMap = <String,Int>();
var nameOfAge = {"summer" : 8, "spring" : 3};

更多的介绍查看这里 Swift 集合类型,Dart 集合类型

空值和可选类型

在 Dart 访问 null 类型会产生空引用异常,Swift 自诞生开始就支持了 optional 类型,本来在运行时才能进行的检查,我们可以在编译期就支持空值的处理,Dart 目前也已经支持,截止到这片文章发布,该功能还处于 测试阶段,Dart 团队还不建议在生产环境使用。

  • Swift

var x:Int? //optional
var y:Int = 1 //non-optional must be initialized
var z:Int? = 2
func foo(name:String?){
guard let name = name else {
print("no value")
return
}
print(name)
print(z!)//如果确定不是null,可以使用 ! 解包
}
foo(name: nil) //no value
foo(name:"summer") //summer

  • Dart

var int? x;
var double? f = 1.3;
print(f!) //已知 f 不为空 可以直接解包
print(x?.floor()) // 如果x 是null,则返回null,否者返回运行的结果

可以看到除了声明方式有些不一样,其他方面都很像。其实这也说明一个问题,可选类型的发明是为了解决空引用的问题而诞生的,如果方式方法相同,本质是一样的解决方案。学习这些语言特性的时候多从设计语言的人的角度看看,就很容易理解,各种设计是为了解决问题而诞生的,不是为了存在而存在。更多阅读 Swift 可选类型链, Dart null-safety

类(Class)

类在两个语言中都有实现,语法有些区别。由于 Swift 中对可选类型的约定,如果是非可选类型必须在初始化的时候设置值,否则会编译不通过。比如下面代码除 name 之外其他的属性必须都设置值。

  • Swift

class Person{
var name:String?
let age:Int
let height:Double
init(age: Int, height: Double) {
self.age = age
self.height = height
}
}
let p = Person(age: 6, height: 1.12)

  • Dart

class Person{
Person(this.name,this.age,this.height);
final String name;
final int age;
final double height;
}

注意这里, this.[propertyname] 是Dart 提供的语法糖,可以快速实现原始属性的赋值。

构造器功能扩展

  • Swift

Swift 结构体和类都支持构造器,Swift 中默认是不调用父类的 init 方法的,类还支持 convenience ,更多的内容查看 Swift 的 初始化(非常推荐)介绍。

class Human{
var legs:String?
var hands:String?
init(legs:String?,hands:String?) {
self.legs = legs
self.hands = hands
}
}
class Person:Human{
var name:String?
let age:Int
let height:Double
override init(legs: String?, hands: String?) { //重写父类的初始化方法
self.age = 0;
self.height = 0;
super.init(legs: legs ?? "biglegs", hands: hands ?? "bighands");//初始化完自己后再调用父类
}
init(name: String?, age: Int, height: Double) { //designated initializer
self.name = name
self.age = age
self.height = height
super.init(legs: "humanHands", hands: "humanLegs") //调用父类designated initializer
self.hands = "bobyhands" //访问父类属性必须在初始化父类后再访问
}
init(age:Int,height:Double){ //designated initializer
self.age = age
self.height = height
super.init(legs: "humanHands", hands: "biglegs")
}
convenience init() { //convenience 方法
self.init(age:10,height:1.1)
self.name = "noname"
}
}

  • Dart

Dart 可以通过 facory 关键词声明一个构造器不一定返回一个新的实例回来,比如单例或者来自 Cache 。

class SingletenSimple{
static get instance => _getInstance();
factory SingletenSimple() => _getInstance();
factory SingletenSimple.single() => _getInstance();// 示例一下其他写法
static SingletenSimple _instance;
SingletenSimple._();
static SingletenSimple _getInstance(){
if(_instance == null){
_instance = SingletenSimple._();
}
return _instance;
}
}

继承,协议,Mixin

Swift 支持单继承类,也支持继承协议(Swift面向协议编程(笑~)),Dart 支持 Mixin 混合模式,实际是和Swift 一样的效果,继承是多继承,但是 superclass  只有一个。另外截止到当前版本,Dart 没有协议支持,协议实现需要用 abstract 抽象类代替。Dart 的官网对 Mixin 定义

每个对象都是类的实例,都派生自 Object 。基于 Mixin 的继承实现,虽然每个类(除了Object)都只有一个父类(superclass),但是类的内部可以被其他类复用。

思考一下, Mixin 是不是对多重继承的改进?子继承父的概念没有变,但是又扩大了继承的复用面。从这方面看 Swift 其实也实现了 Mixin ,因为 Swift 支持协议扩展,协议又可以被继承。

  • Swift

protocol Runable{
func run();
}
extension Runable{
func run(){
print("I am running...")
}
}
class Human{
var legs:String?
var hands:String?
}
class Person:Human,Runable{ //继承实现
let name:String
let age:Int
let height:Double
init(name: String, age: Int, height: Double) {
self.name = name
self.age = age
self.height = height
}
}
let p = Person(name: "summber", age: 6, height: 1.12);
p.run() // I am running...

  • Dart

abstract class Runable {
void run(){
print('I am running...');
}
}
abstract class Human {
String legs;
String hands;
}
class Person extends Human with Runable { //extends 是继承,with 是 mixin 的写法
String name;
int age;
double height;
Person(this.name,this.age,this.height);
}
var p = Person("summber",6,1.12);
p.run(); // I am running...

更多阅读请查看 Swift 类与结构体,Dart 类的结构

属性

Dart 中称为 instance variables , Swift 中称为 property 。前面例子里有很多的声明了,这里主要关注一下 get set 的方法和 Swift 中特殊的 Property observers 。

  • Swift

class Circle {
init(radius:Double){
self.radius = radius
}
var radius:Double
var diameter:Double{ // 如果只有get 方法可以只写 return radius * 2.0
get {
return radius * 2.0
}
set {
radius = newValue / 2.0
}
}
}

  • Dart

class Circle{
double radius;
Circle(this.radius);
double get diameter{
return radius * 2.0;
}
set diameter(double newvalue){
radius = newvalue / 2.0;
}
}

Swift 支持属性值观察,类似 KVO 

  • Swift

var diameter:Double{
willSet(newDiameter){
print("will set old: \(diameter), new: \(newDiameter)")
}
didSet {
print("did set old:\(oldValue), new: \(diameter)")
}
}

阅读更多 Swift 属性篇 Dart 的 instance variablesgetter & setter

扩展

Swift 支持对类,现有基本类型,结构体,枚举,协议进行扩展。Swift 可以写出如下的代码,更多的内容查看 Swift 扩展。自 Dart 2.7 开始也支持扩展

  • Swift

extension Int {
func addOne() -> Self {
return self + 1
}
}
print(3.addOne()) // 4

  • Dart

extension numparer on int {
int addone(){
return this + 1;
}
}
var num = 3;
print(3.addone()); // 4

枚举

Swift 的枚举非常强大,除了面向协议的编程还诞生了面向枚举的编程(这不是真的,别信。。。)。Dart 就只有基本支持。由于 Swift 的枚举表达能力过强,我们这里只讨论基本的样式,更多的请查看 Swift 的枚举

  • Swift

//普通版本
enum Human{
case man
case wowan
}
class Person{
var name:String
var age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
class Girl:Person{
var bag:String?
}
//常见版本
enum Human{
case man(he:Person)
case woman(she:Girl)
func turn() -> Self {
switch self {
case .man(let he):
print("he \(he.name) will become she")
return .woman(she: Girl(name: he.name, age: he.age))
case .woman(let girl):
print("she \(girl) will become he")
return .man(he: Person(name: girl.name, age: girl.age))
}
}
}
let aMan = Human.man(he: Person(name: "Tom", age: 20))
aMan.turn() //he Tom will become she

  • Dart

enum Person {
man,
woman,
}

结构体

Swift 支持类也支持结构体,两者最大的不同是结构体是值类型,类是引用类型(如果不知道值类型和引用类型,面壁思过去。。。)。

struct Person {
var name:String
let age:Int
let height:Double
}
let p = Person(name: "Summer", age: 6, height: 1.12)
var q = p
q.name = "Spring"
print(p.name)// Summer

阅读更多Swift Copy-on-Write, 类与结构体

错误处理

Swift 和 Dart 都采用了 try/catch 的方式处理错误。两者有少量的不同,Swift 会更严格一些,要产生异常的代码方法名以 throws 结尾,异常的类型必须继承 Error , Dart 可以 throw 任何类型。

  • Swift

enum AccountError: Error {
case insufficientFunds
}
class BankAccount {
var balance: Double
init(balance: Double) {
self.balance = balance
}
func withdraw(amount: Double) throws { //相对Dart 增加 throws 关键字
if amount > balance {
throw AccountError.insufficientFunds
}
balance -= amount
}
}
var account = BankAccount(balance: 100)
do {
try account.withdraw(amount: 50) // ok
try account.withdraw(amount: 200) // throws
} catch AccountError.insufficientFunds {
print("Insufficient Funds")
}

Swift 有三种 try try? try! 的使用方式, try? 可以不用 try/catch ,而 try! 适合在完全确定不会有异常的情况下使用,但是不建议在生产环境使用,如果出现异常会发生 Crash,可以写测试案例的使用。

var account = BankAccount(balance: 100)
try? account.withdraw(amount: 50) // ok
try? account.withdraw(amount: 200) // 不会产生异常
var account = BankAccount(balance: 100)
try! account.withdraw(amount: 50) // ok
try! account.withdraw(amount: 200) // crash

  • Dart

class BankAccount{
double balance;
BankAccount(this.balance);
void withdraw(double account){
if(account > balance){
throw(Exception('Insufficient funds'));
}
balance -= account;
}
}
var account = BankAccount(balance: 100);
try {
account.withdraw(50); // ok
account.withdraw(200); // throws
} catch (e) {
print(e); // prints 'Exception: Insufficient funds'
}

总体来看 Swift 语言设计的更加健壮一些,可以清晰的看到调用的方法是否会有异常。查看更多的内容 Swift 如何处理错误, Dart 异常处理