吉星高趙 發表於 2022-7-19 01:27:00

[Android开发学iOS系列] 语言篇: Swift vs Kotlin

<h1 id="android开发学ios系列-语言篇-swift-vs-kotlin"> 语言篇: Swift vs Kotlin</h1>
<p>这篇文章是想着帮助Android开发快速学习Swift编程语言用的. (因为这个文章的作者立场就是这样.)</p>
<p>我不想写一个非常长, 非常详尽的文章, 只是想写一个快速的版本能让你快速上手工作.</p>
<p>当然这个文章可能也适合于以下人群:</p>
<ul>
<li>有经验的其他任何语言的开发者, 想学Swift.</li>
<li>一个会Swift的iOS开发者, 想横向对比, 了解学习一下Kotlin.</li>
<li>iOS初级程序员, 刚开始学习.</li>
<li>用过Swift, 但是有一阵子没用了, 想快速刷新一下回忆.</li>
</ul>
<h2 id="基本类型">基本类型</h2>
<table>
<thead>
<tr>
<th>Swift</th>
<th>Kotlin</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bool</td>
<td>Boolean</td>
</tr>
<tr>
<td>Array</td>
<td>Array, List, MutableList</td>
</tr>
<tr>
<td>Set</td>
<td>Set</td>
</tr>
<tr>
<td>Dictionary</td>
<td>Map</td>
</tr>
</tbody>
</table>
<p>其他基本类型都是差不多的.</p>
<h2 id="语法">语法</h2>
<table>
<thead>
<tr>
<th></th>
<th>Swift</th>
<th>Kotlin</th>
</tr>
</thead>
<tbody>
<tr>
<td>变量声明</td>
<td>let/var</td>
<td>val/var</td>
</tr>
<tr>
<td>具名参数</td>
<td>at: 0</td>
<td>at = 0</td>
</tr>
<tr>
<td>函数/方法</td>
<td>func name() → returnType</td>
<td>fun name(): returnType</td>
</tr>
<tr>
<td>表达无值</td>
<td>nil</td>
<td>null</td>
</tr>
<tr>
<td>unwrapped type</td>
<td>String!</td>
<td>-</td>
</tr>
<tr>
<td>if</td>
<td>if number != nil</td>
<td>if(number != null)</td>
</tr>
<tr>
<td>为空时提供默认值</td>
<td>xxx ?? “default string”</td>
<td>? : ”default string”</td>
</tr>
<tr>
<td>不为空时做某件事</td>
<td>if let number = Int(”333”) {}</td>
<td>?.let {}</td>
</tr>
<tr>
<td>for loop</td>
<td>for i in 1...5 {}</td>
<td>for (i in 1..5) {}</td>
</tr>
<tr>
<td>for loop</td>
<td>for i in 1..&lt;5 {}</td>
<td>for (i in 1 until 5) {}</td>
</tr>
<tr>
<td>do while loop</td>
<td>repeat {} while</td>
<td>do {} while ()</td>
</tr>
<tr>
<td>this instance</td>
<td>self</td>
<td>this</td>
</tr>
<tr>
<td>value object</td>
<td>struct</td>
<td>data class</td>
</tr>
<tr>
<td></td>
<td>as?</td>
<td>as?</td>
</tr>
<tr>
<td></td>
<td>as!</td>
<td>as</td>
</tr>
<tr>
<td></td>
<td>try?</td>
<td>-</td>
</tr>
<tr>
<td></td>
<td>try!</td>
<td>-</td>
</tr>
<tr>
<td>class initializer</td>
<td>initializer</td>
<td>constructor</td>
</tr>
<tr>
<td>init a mutable list</td>
<td>var someInts: = []</td>
<td>val someInts = mutableListOf<int>()</int></td>
</tr>
<tr>
<td>init a empty dictionary/map</td>
<td>var namesOfIntegers: = [:]</td>
<td>val namesOfIntegers = mutableMapOf&lt;Int, String&gt;()</td>
</tr>
</tbody>
</table>
<h3 id="constants-and-variables">Constants and Variables</h3>
<p>Swift:</p>
<ul>
<li><code>let</code> 不能再次赋值. 如果对象类型是<code>struct</code>, 不能更新对象的任何字段. 如果是<code>class</code>, 则仍可更新对象的<code>var</code>字段.</li>
<li><code>var</code> 可以给变量重新赋值, 也可以更新变量的<code>var</code>字段.</li>
<li><code>var</code> 可以声明一个mutable的集合类型.</li>
</ul>
<p>Kotlin:</p>
<ul>
<li><code>val</code>和java中的<code>final</code>等价, 不能再给变量重新赋值, 但是仍然可以更新对象的<code>var</code>字段.</li>
<li><code>var</code>意味着可以给变量重新赋值.</li>
<li>集合类型的可变与否是被具体的集合类型声明所决定的.</li>
</ul>
<h3 id="switch-case">Switch case</h3>
<p>Swift:</p>
<pre><code class="language-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")
}
</code></pre>
<p>Kotlin:</p>
<pre><code class="language-kotlin">val x = 3
when (x) {
    1 -&gt; print("x == 1")
    2, 4 -&gt; print("x == 2 or x == 4")
    else -&gt; print("x is something else")
}
</code></pre>
<h3 id="string-interpolation">String interpolation</h3>
<p>Swift:</p>
<pre><code class="language-swift">var name = "Mike"
print("Hello \(name)")
</code></pre>
<p>也可以给String规定格式:</p>
<pre><code class="language-swift">let str = NSString(format:"%d , %f, %ld, %@", 1, 1.5, 100, "Hello World")
print(str)
</code></pre>
<p>Kotlin:</p>
<pre><code class="language-kotlin">var name = "Mike"
println("Hello $name")

val str = String.format("%d, %f, %d, %s", 1, 1.5, 100, "Hello World")
print(str)
</code></pre>
<h2 id="function-and-closure">Function and Closure</h2>
<p>Swift的function有一个<code>argument label</code>:</p>
<pre><code class="language-swift">func someFunction(argumentLabel parameterName: Int) {
    // In the function body, parameterName refers to the argument value
    // for that parameter.
}
</code></pre>
<p>这里<code>parameterName</code>是方法内部使用的, <code>argumentLabel</code>是被外部调用者使用的. (目的是为了增强可读性.)</p>
<p>当argument label没有提供的时候, parameter name同时也扮演argument label的角色.</p>
<p>在方法调用时argument label 默认是不能省略的(虽然有时候它和parameter name一样), 如果你想在调用的时候省略, 可以用下划线<code>_</code>明确指明.</p>
<h3 id="closure">Closure</h3>
<p>闭包和Kotlin中的lambda相似.</p>
<p>一个简单的Swift例子:</p>
<pre><code class="language-swift">let sayHello = { (name: String) -&gt; String in
    let result = "Hello \(name)"
    print(result)
    return result
}

sayHello("Mike")
</code></pre>
<p>用Kotlin做同样的事情:</p>
<pre><code class="language-kotlin">val sayHello : (String) -&gt; String = { name: String -&gt;
    val result = "Hello $name"
    print(result)
    result
}

sayHello("Mike")
</code></pre>
<p>相同点:</p>
<ul>
<li>可以根据上下文推断类型, 所以有时候类型可以省略.</li>
<li>可以作为另一个函数的参数传入, 从而实现高阶方法.</li>
<li>如果闭包/lambda是方法的最后一个参数, 可以提到圆括号外面. 如果是唯一的参数, 可以省略圆括号.</li>
</ul>
<p>不同点:</p>
<ul>
<li>在Swift中,只有单句表达式可以省略<code>return</code>关键字, 把表达式结果作为返回值. 而在Kotlin中, 最后的表达式值会被作为返回结果, 且在lambda中没有<code>return</code>关键字.</li>
<li>Swift有缩略版本的参数名, 比如: <code>$0</code>, <code>$1</code>, <code>$2</code>.</li>
</ul>
<h2 id="custom-types">Custom types</h2>
<table>
<thead>
<tr>
<th>Swift</th>
<th>Kotlin</th>
</tr>
</thead>
<tbody>
<tr>
<td>class</td>
<td>class</td>
</tr>
<tr>
<td>protocol</td>
<td>interface</td>
</tr>
<tr>
<td>extension</td>
<td>extension methods</td>
</tr>
</tbody>
</table>
<h3 id="class">class</h3>
<p>Swift和Kotlin中的类定义和用法十分相似.</p>
<p>继承是通过<code>:</code>符号, 子类可以<code>override</code>父类的方法.</p>
<p>继承的的时候父类class需要放在protocol前.</p>
<p>只有构造看起来有点不同, 在Swift中叫initializer:</p>
<pre><code class="language-swift">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)")
</code></pre>
<p>在Kotlin中, 可以通过如下的代码达到相同的目的:</p>
<pre><code class="language-kotlin">class Person(val name: String = "") {
}
val p1 = Person()
print("${p1.name}") // default name: ""

val p2 = Person(name="haha")
print("${p2.name}")
</code></pre>
<p>除此之外, Kotlin的class还有<code>init {}</code> block, 可以用来做一些额外的初始化工作.</p>
<h3 id="struct">struct</h3>
<p>struct是一个值类型.</p>
<p>struct和class的区别:</p>
<ul>
<li>class可以继承.</li>
<li>struct是值类型: 拷贝多份不会共享数据; class是引用类型, 所有的赋值拷贝最终都指向同一份数据实例.</li>
<li>class有<code>deinit</code>.</li>
<li>class的实例可以被<code>let</code>保存, 同时实例的<code>var</code>字段仍然可被修改, struct则不可修改.</li>
</ul>
<pre><code class="language-swift">class Person {
    var name = "Lily"
}

let p1 = Person()
p1.name = "Justin"

print("\(p1.name)")
</code></pre>
<p>这是ok的.</p>
<p>如果<code>Person</code>是struct:</p>
<pre><code class="language-swift">struct Person {
    var name = "Lily"
}

let p1 = Person()
p1.name = "Justin"
// Compiler error: Cannot assign to property: `p1` is a `let` constant
</code></pre>
<p>编译器会报错.</p>
<p>想要改变字段值, 只能声明: <code>**var** p1 = Person()</code>.</p>
<h3 id="protocol">protocol</h3>
<p>protocol类似Kotlin中的<code>interface</code>.</p>
<p>我们可以定义一些方法或者计算属性作为契约.</p>
<p>Properties写起来是这样的:</p>
<pre><code class="language-swift">protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}
</code></pre>
<p>protocol和interface有一点点小区别: 比如实现protocol的类的方法上不需要使用<code>override</code>关键字.</p>
<h3 id="extension">extension</h3>
<p>在Swift, extension更像是一个用来放扩展方法和属性的地方.</p>
<pre><code class="language-swift">extension String {
    func trimmed() -&gt; String {
      self.trimmingCharacters(in: .whitespacesAndNewlines)
    }
   
    mutating func trim() {
      self = self.trimmed()
    }
   
    var lines: {
      self.components(separatedBy: .newlines)
    }
}
</code></pre>
<p>在Kotlin中扩展方法可以是顶级方法, 只需要在<code>.</code>之前声明类型:</p>
<pre><code class="language-kotlin">fun String.someMethod() : String {
    return this.trim()
}
</code></pre>
<h3 id="enum">enum</h3>
<p>Swift enum:</p>
<pre><code class="language-swift">enum CompassPoint {
    case north
    case south
    case east
    case west
}
</code></pre>
<p>多个case也可以写在一行, 用逗号分隔:</p>
<pre><code class="language-swift">enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
</code></pre>
<p>在Swift中使用枚举的时候, 我们可以省略前面的类型, 只用一个<code>.</code>开头:</p>
<pre><code class="language-swift">var directionToHead = CompassPoint.west
directionToHead = .east
</code></pre>
<p>Swift enum有一个<code>allCases</code>属性, 暴露所有case的集合.</p>
<p>Kotlin:</p>
<pre><code class="language-kotlin">enum class Direction {
    NORTH, SOUTH, WEST, EAST
}
</code></pre>
<p>在枚举中我们也可以定义方法和属性, 这个Swift和Kotlin是一样的.</p>
<h2 id="optionals">Optionals</h2>
<p>虽然Swift的optional type和Kotlin的nullable type看起来是类似的(都是具体类型后面加个问号), 但实际上它们还是有点不同.</p>
<p>强制解包Swift用一个!, Kotlin用两个!!, 如果值为空都会异常.</p>
<p>Swift的optional type更像Java的<code>Optional</code>.</p>
<p>因为你在用之前永远需要解包(unwrap).</p>
<pre><code class="language-swift">var someString : String? = nil
print(someString?.count) // print nil
print(someString!.count) // Fatal error: Unexpectedly found nil while unwrapping an Optional value
</code></pre>
<p>当变量有值时, 我们需要用它:</p>
<pre><code class="language-swift">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
}
</code></pre>
<p>注意当直接用的时候, 变量的类型永远是Optional.<br>
必须解包才能拿到值.</p>
<p>实际上在Swift中有一种更简单的写法来做这件事, 使用<code>if let</code>:</p>
<pre><code class="language-swift">if let someStringValue = someString {
    print("\(someStringValue) with length \(someStringValue.count)")
}
</code></pre>
<p>这里<code>someStringValue</code>是从<code>someString</code>解包过的值, 后面的block只有当它不为nil时才会被执行.</p>
<p>在Kotlin中:</p>
<pre><code class="language-kotlin">var someString : String? = null
print(someString?.length) // print null
print(someString!!.length) // NullPointerException
</code></pre>
<p>不同点主要在于有值的时候:</p>
<pre><code class="language-kotlin">var someString : String? = "Hello"
if(someString != null) {
   print("$someString with length: ${someString.length}")
}
// print: Hello with length: 5
</code></pre>
<p>在Kotlin中, 如果我们判断过变量不为null, 后面就可以直接用了, 编译器知道这个变量现在不为空了.</p>
<h3 id="if-let-和-guard-let">if let 和 guard let</h3>
<p>我们上面的例子用<code>if let</code>解包Optional, 只在不为nil的时候执行大括号里面的内容.</p>
<p><code>guard let</code>做的事情正好相反: <code>else</code> block只在值为nil的时候才执行:</p>
<pre><code class="language-swift">func printSquare(of number: Int?){
    guard let number = number else {
      print("Oops we got nil")
      return
    }
   
    print("\(number) * \(number) is \(number * number)")
}
</code></pre>
<p>所以<code>guard let</code>通常被用来做参数检测, 不合法就return.</p>
<p>并且在guard语句之后, number不再是一个optional的类型, 是一个确定有值的类型.</p>
<h2 id="最后">最后</h2>
<p>学习新的语言的时候, 不太建议花太多的时间钻研语言的每个细节.</p>
<p>只需要了解一些最基本的知识, 然后就可以上手做具体的工作和任务.<br>
在实际的任务中进行进一步的学习和练习.</p>
<p>总之, 希望这篇文章对你有用.</p>
<h2 id="references">References</h2>
<ul>
<li>Swift book: https://docs.swift.org/swift-book/</li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    作者: 圣骑士Wind<br>
出处: 博客园: 圣骑士Wind<br>
Github: https://github.com/mengdd<br>

微信公众号: 圣骑士Wind<br>
<img src="https://images.cnblogs.com/cnblogs_com/mengdd/869539/o_200422055937qrcode_for_gh_0e2ed690dcda_258.jpg" alt="微信公众号: 圣骑士Wind"><br><br>
来源:https://www.cnblogs.com/mengdd/p/swift-vs-kotlin.html
頁: [1]
查看完整版本: [Android开发学iOS系列] 语言篇: Swift vs Kotlin