Swift vs Kotlin

这篇文章是想着帮助Android开发快速学习Swift编程语言用的. (因为这个文章的作者立场就是这样.)

我不想写一个非常长, 非常详尽的文章, 只是想写一个快速的版本能让你快速上手工作.

当然这个文章可能也适合于以下人群:

  • 有经验的其他任何语言的开发者, 想学Swift.
  • 一个会Swift的iOS开发者, 想横向对比, 了解学习一下Kotlin.
  • iOS初级程序员, 刚开始学习.
  • 用过Swift, 但是有一阵子没用了, 想快速刷新一下回忆.

基本类型

Swift Kotlin
Bool Boolean
Array Array, List, MutableList
Set Set
Dictionary Map

其他基本类型都是差不多的.

语法

Swift Kotlin
变量声明 let/var val/var
具名参数 at: 0 at = 0
函数/方法 func name() → returnType fun name(): returnType
表达无值 nil null
unwrapped type String! -
if if number != nil if(number != null)
为空时提供默认值 xxx ?? “default string” ? : ”default string”
不为空时做某件事 if let number = Int(”333”) {} ?.let {}
for loop for i in 1...5 {} for (i in 1..5) {}
for loop for i in 1..<5 {} for (i in 1 until 5) {}
do while loop repeat {} while do {} while ()
this instance self this
value object struct data class
as? as?
as! as
try? -
try! -
class initializer initializer constructor
init a mutable list var someInts: [Int] = [] val someInts = mutableListOf()
init a empty dictionary/map var namesOfIntegers: [Int: String] = [:] val namesOfIntegers = mutableMapOf<Int, String>()

Constants and Variables

Swift:

  • let 不能再次赋值. 如果对象类型是struct, 不能更新对象的任何字段. 如果是class, 则仍可更新对象的var字段.
  • var 可以给变量重新赋值, 也可以更新变量的var字段.
  • var 可以声明一个mutable的集合类型.

Kotlin:

  • val和java中的final等价, 不能再给变量重新赋值, 但是仍然可以更新对象的var字段.
  • var意味着可以给变量重新赋值.
  • 集合类型的可变与否是被具体的集合类型声明所决定的.

Switch case

Swift:

var x = 3
switch x {
case 1: print("x == 1")
case 2, 4: print("x == 2 or x == 4")
default: print("x is something else")
}

Kotlin:

val x = 3
when (x) {
1 -> print("x == 1")
2, 4 -> print("x == 2 or x == 4")
else -> print("x is something else")
}

String interpolation

Swift:

var name = "Mike"
print("Hello \(name)")

也可以给String规定格式:

let str = NSString(format:"%d , %f, %ld, %@", 1, 1.5, 100, "Hello World")
print(str)

Kotlin:

var name = "Mike"
println("Hello $name") val str = String.format("%d, %f, %d, %s", 1, 1.5, 100, "Hello World")
print(str)

Function and Closure

Swift的function有一个argument label:

func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}

这里parameterName是方法内部使用的, argumentLabel是被外部调用者使用的. (目的是为了增强可读性.)

当argument label没有提供的时候, parameter name同时也扮演argument label的角色.

在方法调用时argument label 默认是不能省略的(虽然有时候它和parameter name一样), 如果你想在调用的时候省略, 可以用下划线_明确指明.

Closure

闭包和Kotlin中的lambda相似.

一个简单的Swift例子:

let sayHello = { (name: String) -> String in
let result = "Hello \(name)"
print(result)
return result
} sayHello("Mike")

用Kotlin做同样的事情:

val sayHello : (String) -> String = { name: String ->
val result = "Hello $name"
print(result)
result
} sayHello("Mike")

相同点:

  • 可以根据上下文推断类型, 所以有时候类型可以省略.
  • 可以作为另一个函数的参数传入, 从而实现高阶方法.
  • 如果闭包/lambda是方法的最后一个参数, 可以提到圆括号外面. 如果是唯一的参数, 可以省略圆括号.

不同点:

  • 在Swift中,只有单句表达式可以省略return关键字, 把表达式结果作为返回值. 而在Kotlin中, 最后的表达式值会被作为返回结果, 且在lambda中没有return关键字.
  • Swift有缩略版本的参数名, 比如: $0, $1, $2.

Custom types

Swift Kotlin
class class
protocol interface
extension extension methods

class

Swift和Kotlin中的类定义和用法十分相似.

继承是通过:符号, 子类可以override父类的方法.

继承的的时候父类class需要放在protocol前.

只有构造看起来有点不同, 在Swift中叫initializer:

class Person {
let name: String
init(name: String = "") {
self.name = name
}
}
let p1 = Person()
print("\(p1.name)") // default name: "" let p2 = Person(name: "haha")
print("\(p2.name)")

在Kotlin中, 可以通过如下的代码达到相同的目的:

class Person(val name: String = "") {
}
val p1 = Person()
print("${p1.name}") // default name: "" val p2 = Person(name="haha")
print("${p2.name}")

struct

struct是一个值类型.

struct和class的区别:

  • class可以继承.
  • struct是值类型: 拷贝多份不会共享数据; class是引用类型, 所有的赋值拷贝最终都指向同一份数据实例.
  • class有deinit.
  • class的实例可以被let保存, 同时实例的var字段仍然可被修改, struct则不可修改.
class Person {
var name = "Lily"
} let p1 = Person()
p1.name = "Justin" print("\(p1.name)")

这是ok的.

如果Person是struct:

struct Person {
var name = "Lily"
} let p1 = Person()
p1.name = "Justin"
// Compiler error: Cannot assign to property: `p1` is a `let` constant

编译器会报错.

想要改变字段值, 只能声明: **var** p1 = Person().

protocol

protocol类似Kotlin中的interface.

我们可以定义一些方法或者计算属性作为契约.

Properties写起来是这样的:

protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}

protocol和interface有一点点小区别: 比如实现protocol的类的方法上不需要使用override关键字.

extension

在Swift, extension更像是一个用来放扩展方法和属性的地方.

extension String {
func trimmed() -> String {
self.trimmingCharacters(in: .whitespacesAndNewlines)
} mutating func trim() {
self = self.trimmed()
} var lines: [String] {
self.components(separatedBy: .newlines)
}
}

在Kotlin中扩展方法可以是顶级方法, 只需要在.之前声明类型:

fun String.someMethod() : String {
return this.trim()
}

enum

Swift enum:

enum CompassPoint {
case north
case south
case east
case west
}

多个case也可以写在一行, 用逗号分隔:

enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

在Swift中使用枚举的时候, 我们可以省略前面的类型, 只用一个.开头:

var directionToHead = CompassPoint.west
directionToHead = .east

Swift enum有一个allCases属性, 暴露所有case的集合.

Kotlin:

enum class Direction {
NORTH, SOUTH, WEST, EAST
}

在枚举中我们也可以定义方法和属性, 这个Swift和Kotlin是一样的.

Optionals

虽然Swift的optional type和Kotlin的nullable type看起来是类似的(都是具体类型后面加个问号), 但实际上它们还是有点不同.

Swift的optional type更像Java的Optional.

因为你在用之前永远需要解包(unwrap).

var someString : String? = nil
print(someString?.count) // print nil
print(someString!.count) // Fatal error: Unexpectedly found nil while unwrapping an Optional value

当变量有值时, 我们需要用它:

var someString : String? = "Hello"

if (someString != nil) {
print("\(someString) with length \(someString?.count)")
// print: Optional("Hello") with length Optional(5) print("\(someString!) with length \(someString!.count)")
// print: Hello with length 5
}

注意当直接用的时候, 变量的类型永远是Optional.

必须解包才能拿到值.

实际上在Swift中有一种更简单的写法来做这件事, 使用if let:

if let someStringValue = someString {
print("\(someStringValue) with length \(someStringValue.count)")
}

这里someStringValue是从someString解包过的值, 后面的block只有当它不为nil时才会被执行.

在Kotlin中:

var someString : String? = null
print(someString?.length) // print null
print(someString!!.length) // NullPointerException

不同点主要在于有值的时候:

var someString : String? = "Hello"
if(someString != null) {
print("$someString with length: ${someString.length}")
}
// print: Hello with length: 5

在Kotlin中, 如果我们判断过变量不为null, 后面就可以直接用了, 编译器知道这个变量现在不为空了.

if let 和 guard let

我们上面的例子用if let解包Optional, 只在不为nil的时候执行大括号里面的内容.

guard let做的事情正好相反: else block只在值为nil的时候才执行:

func printSquare(of number: Int?){
guard let number = number else {
print("Oops we got nil")
return
} print("\(number) * \(number) is \(number * number)")
}

所以guard let通常被用来做参数检测, 不合法就return.

并且在guard语句之后, number不再是一个optional的类型, 是一个确定有值的类型.

最后

学习新的语言的时候, 不太建议花太多的时间钻研语言的每个细节.

只需要了解一些最基本的知识, 然后就可以上手做具体的工作和任务.

在实际的任务中进行进一步的学习和练习.

总之, 希望这篇文章对你有用.

References

[Android开发学iOS系列] 语言篇: Swift vs Kotlin的更多相关文章

  1. [Android开发学iOS系列] 工具篇: Xcode使用和快捷键

    [Android开发学iOS系列] 工具篇: Xcode使用和快捷键 工欲善其事必先利其器. 编辑 Cmd + N: 新建文件 Option + Cmd + N: 新建文件夹 Cmd + / : 注释 ...

  2. [Android开发学iOS系列] iOS写UI的几种方式

    [Android开发学iOS系列] iOS写UI的几种方式 作为一个现代化的平台, iOS的发展也经历了好几个时代. 本文讲讲iOS写UI的几种主要方式和各自的特点. iOS写UI的方式 在iOS中写 ...

  3. [Android开发学iOS系列] Auto Layout

    [Android开发学iOS系列] Auto Layout 内容: 介绍什么是Auto Layout. 基本使用方法 在代码中写约束的方法 Auto Layout的原理 尺寸和优先级 Auto Lay ...

  4. [Android开发学iOS系列] 快速上手UIKit

    快速上手iOS UIKit UIKit是苹果官方的framework, 其中包含了各种UI组件, window和view, 事件处理, 交互, 动画, 资源管理等基础设施支持. 按照前面的介绍, 用U ...

  5. [Android开发学iOS系列] ViewController

    iOS ViewController 写UIKit的代码, ViewController是离不开的. 本文试图讲讲它的基本知识, 不是很深入且有点杂乱, 供初级选手和跨技术栈同学参考. What is ...

  6. iOS系列 基础篇 03 探究应用生命周期

    iOS系列 基础篇 03 探究应用生命周期 目录: 1. 非运行状态 - 应用启动场景 2. 点击Home键 - 应用退出场景 3. 挂起重新运行场景 4. 内存清除 - 应用终止场景 5. 结尾 本 ...

  7. iOS系列 基础篇 05 视图鼻祖 - UIView

    iOS系列 基础篇 05 视图鼻祖 - UIView 目录: UIView“家族” 应用界面的构建层次 视图分类 最后 在Cocoa和Cocoa Touch框架中,“根”类时NSObject类.同样, ...

  8. iOS系列 基础篇 07 Action动作和输出口

    iOS系列 基础篇 07 Action动作和输出口 目录:  1. 前言及案例说明 2. 什么是动作? 3. 什么是输出口? 4. 实战 5. 结尾 1. 前言及案例说明 上篇内容我们学习了标签和按钮 ...

  9. Android开发—智能家居系列】(二):用手机对WIFI模块进行配置

    在实际开发中,我开发的这款APP是用来连接温控器,并对温控器进行控制的.有图为证,哈哈. 上一篇文章[Android开发—智能家居系列](一):智能家居原理的文末总结中写到: 手机APP控制智能温控器 ...

随机推荐

  1. 浅尝Spring注解开发_Bean生命周期及执行过程

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含Bean生命周期.自定义初始化方法.Debug BeanPostProcessor执行过程及在Spring底层中的应 ...

  2. 在GO中调用C源代码#基础篇1

    开坑说明 最近在编写客户端程序或与其他部门做功能集成时多次碰到了跨语言的sdk集成,虽说方案很多诸如rpc啊,管道啊,文件io啊,unix socket啊之类的不要太多,但最完美的基础方式还是让程序与 ...

  3. 【爬虫+情感判定+Top10高频词+词云图】“刘畊宏“热门弹幕python舆情分析

    一.背景介绍 最近一段时间,刘畊宏真是火出了天际,引起一股全民健身的热潮,毕竟锻炼身体,是个好事! 针对此热门事件,我用Python的爬虫和情感分析技术,针对小破站的弹幕数据,分析了众多网友弹幕的舆论 ...

  4. 流,用声明性的方式处理数据集 - 读《Java 8实战》

    引入流 Stream API的代码 声明性 更简洁,更易读 可复合 更灵活 可并行 性能更好 流是什么? 它允许以声明方式处理数据集合 遍历数据集的高级迭代器 透明地并行处理 简短定义:从支持数据处理 ...

  5. Python 生成图片验证码

    验证码图片生成 #!/usr/bin/env python # -*- coding: utf-8 -*- # refer to `https://bitbucket.org/akorn/wheezy ...

  6. 使用Rclone将Onedirve挂载到Linux本地

    1. centos挂载onedrive时, 需要安装fuse. # 安装fuse yum -y install fuse 2. 安装完fuse后使用rclone进行挂载 #创建挂载目录 mkdir - ...

  7. 大数据分析VMWare虚拟机centos系统下配置网络参数

    最近搞大数据方面的数据,通过网上视频学习和自己实践,有些不成文的实践就零碎的记录在此吧. 系统也可安装CentOS DVD版. 1.先进入配置文件,并查看基本情况,如下: 2.用vi编辑器打开物理网卡 ...

  8. Halodoc使用 Apache Hudi 构建 Lakehouse的关键经验

    Halodoc 数据工程已经从传统的数据平台 1.0 发展到使用 LakeHouse 架构的现代数据平台 2.0 的改造.在我们之前的博客中,我们提到了我们如何在 Halodoc 实施 Lakehou ...

  9. 数位 dp 总结

    数位 dp 总结 特征 问你一个区间 \([L,R]\) 中符合要求的数的个数 一个简单的 trick :把答案拆成前缀和 \(Ans(R)-Ans(L-1)\) 如何求 \(Ans()\) ,就要用 ...

  10. ”只用 1 分钟“ - 超简极速 Apk 签名 & 多渠道打包神器

    众所周知,渠道包作为当下国内 Android 应用市场常见的分发方式,当 APP 和后台交互或进行数据上报时,会带上各自的 channel 渠道信息,以此方便企业 & 开发者统计 APP 在各 ...