Edited at

SwiftとKotlinはこんなに似てる!比較用チートシートを公開します


原文

こちらが原文です。翻訳の許可を頂いてます。

A Comparison of Swift and Kotlin Languages


サブタイトル

この記事ではSwiftとKotlinの主な類似点と相違点について、実装・スタイル・構文などの重要な詳細を中心に説明します。

SwiftとKotlinは開発者コミュニティを席巻し、両方のプラットフォームの開発者数を増やすのに役立っています。 どちらの言語も簡単な構文、簡単な文章、現代的なテクニックと機能により、急速に採用されました。

Swiftは2014年6月2日に登場し、AppleがCocoa Touchフレームワークを使用するために開発しました。オープンソースのコンパイラとツールチェーンであるLLVMの上で動作します。Swiftは、バージョン6がリリースされて以来、Xcodeの一部となっています。アップルは2015年12月3日にSwiftをオープンソース化しました。SwiftはObjective-Cとプロジェクトで混在することができますが、Objective-Cに取って代わります。

当初、Kotlinはロシアのサンクトペテルブルクの開発者グループによってJetBrains社で開発されました。Kotlinは2011年に公に発表されました。KotlinはJVM上で動作し、Javaとシームレスに相互運用できるように設計されており、Javaの代わりとなります。Googleは2017年にAndroidの開発言語としてKotlinの正式なサポートを発表しました。Kotlinは2017年10月にAndroid Studio 3.0をリリースしました。

この記事ではSwiftとKotlinのツアーとその比較方法を紹介します。実践的なチュートリアルはありませんが、これらの言語要素を見直すことでこれらの言語の両方の知識を広げることができます。このチュートリアルではSwiftまたはKotlinの基本的な知識があることを前提としています。


Understanding Similarities Between Swift and Kotlin

SwiftとKotlinはお互いに非常に似ています。以下のコードスニペットを自分自身で試してみたい場合はKotlinのWebPlaygroundやSwiftのXcodeplaygroundを使用してください。


Declaring Properties(プロパティの宣言)

どちらの言語でも変更可能性(mutability)と不変性(immutability)という概念があります。変更可能なプロパティは変更または再割り当てできますが、不変プロパティは変更できません。

Kotlin — Mutable Properties(変更可能なプロパティ)

Kotlinでの整数の宣言するには:


sample.kt

var age: Int = 15


型推論でプロパティを宣言するには:


sample.kt

var firstName = "Ray"


Swift – Mutable Properties(変更可能なプロパティ)

Swiftでの整数の宣言するには:


sample.swift

var age: Int = 15


型推論でプロパティを宣言するには:


sample.swift

var firstName = "Ray"


Kotlin — Immutable Properties(不変プロパティ)


sample.kt

val hoursInADay = 24

val secondsInAMinute = 60
val quote = "If you don't work smart, you will probably be replaced by an AI."

Swift — Immutable Properties(不変プロパティ)


sample.swift

let hoursInADay = 24

let secondsInAMinute = 60
let quote = "If you don't work smart, you will probably be replaced by an AI."


したがって上記のように、KotlinとSwiftの不変プロパティの宣言の基本的な違いは、valletキーワードです。


Data Structures(データ構造)

データ構造はどの言語も重要な部分です。これらは、データの整理、格納、および操作に使用されます。Array、linked list、dictionaryまたはmap、classおよびinterfaceは、すべてデータ構造の例です。


Arrays

SwiftとKotlinの両方でarrayは固定型です。つまりarrayは文字列の集合ですが文字列と整数の混合ではありません。


Swift — Array

Swiftで可変なarrayを宣言するには:


sample.swift

var names = Array<String>()


値を追加するには:


sample.swift

names.append("Fuad") //index 0

names.append("Eric") //index 1
names.append("Joe") // index 2

Swiftで不変なarrayを宣言するには:


sample.swift

let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]



Kotlin — Arrays and Lists

Kotlinではarrayは変更可能ですが、固定サイズで宣言されています。次の例では5つの要素を持つKotlinの可変arrayを宣言しています。すべてデフォルト値の0に設定されています。


sample.kt

val numbers = Array<Int>(5){0}  


array内の項目の値を変更するには:


sample.kt

numbers[1] = 2 //numbers is now (0, 2, 0, 0, 0)


上記のarrayは変更可能ですがvalキーワードを使用して不変変数に割り当てることができました。これについては後で詳しく説明します。

Kotlinで不変のarrayを宣言するには:


sample.kt

val days = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")


もう1つのKotlinコレクションタイプ、listsはSwiftのarrayタイプに似ています。listは可変数の要素を持つことができ宣言後にサイズが大きくなります。


sample.kt

var names = ArrayList<String>() //creates a list of strings, initially with no elements


Kotlinリストに値を追加するには:


sample.kt

names.add("Fuad") //index 0

names.add("Eric") //index 1
names.add("Joe") // index 2

Kotlinで不変のlistを宣言するには:


sample.kt

val days = listOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")



Dictionaries / Maps

Dictionary(Swift)またはMap(Kotlin)は別個の値を格納する場合に非常に有用なデータ構造です。デフォルトでDitionaryやMapは拡大できますが、KotlinとSwiftの両方で、パフォーマンスを向上させるための容量を定義できます。


Kotlin


sample.kt

var namesWithAges = HashMap<String, Int>()

var namesWithAges = HashMap<String, Int>(20) //define capacity

値を割り当てるには:


sample.kt

namesWithAges.put("John Doe", 34)

//OR
namesWithAges["John Doe"] = 34

不変のMapを作成するには:


sample.kt

val namesWithAges = mapOf("John Doe" to 34, "Jane Doe" to 29)



Swift

SwiftのDictionaryの作成するには:


sample.swift

var namesWithAges : [String: Int] = [:]

namesWithAges.reserveCapacity(20) //define capacity

値を割り当てるには:


sample.swift

namesWithAges["John Doe"] = 34


不変のDictionaryを宣言するには:


sample.swift

let namesWithAges = ["John Doe" : 34, "Jane Doe" : 29]



Functions

関数(function)は、コードベースの基本ブロックです。コードを読みやすく保守的に整理するのに役立ちます。


Kotlin Functions

Kotlin関数のプロトタイプ(Prototype):


sample.kt

fun functionName(parameterName: DataType): ReturnType {

//function body
}

関数の戻り値の型はコロン(:)で表されます。Kotlinではすべての関数に戻り型があります。戻り値の型を指定しなかった場合は、デフォルトで特別な型のUnitが返されます。

Kotlin関数の例を次に示します。


sample.kt

fun greetUser(name: String): String {

return "Hello ${name}!"
}


Kotlin — Default Argument(デフォルト引数)

Kotlinではパラメータにデフォルト値を割り当てることができます。たとえば上記の関数の例を考えてみましょう。


sample.kt

fun greetUser(name: String = "World"): String {

return "Hello ${name}!"
}

今、上記のような引数を渡さずに上記の関数を呼び出すことができます:


sample.kt

greetUser() //returns Hello World!



Swift Functions


Swift関数のプロトタイプ(Prototype):


sample.swift

func functionName(parameterName: DataType) -> ReturnType {

//function body
}

関数の戻り値の型は->で表されます。さらに関数が明示的に値を返さないときにSwiftが推論する関数の型は特殊な型です。空のタプルであるVoidは、()として記述されます。

Swiftに実際の関数を書く:


sample.swift

func getGreetings(name: String) -> String {

return "Hello \(name)!"
}


Swift — Default Argument(デフォルト引数)

デフォルトの引数を持つSwift関数:


sample.swift

func getGreetings(name: String = "World") -> String {

return "Hello \(name)!"
}

次のようなデフォルトの動作で関数を呼び出します。


sample.swift

getGreetings() //returns Hello World!



Lambda Functions / Closures

ラムダ(Kotlin)とクロージャ(Swift)はあなたのコーディング工廠で別の有用なビルディングブロックになります。 本質的には、それらは無名の関数です。それらは変数に割り当てられ、他の値と同様に渡されます。関数を値として扱う機能はSwiftとKotlinの関数プログラミングの側面の1つです。クロージャの一般的な使用例としては、非同期呼び出しの処理(ネットワーク要求やコレクションの操作など)があります。


Kotlin — Lambda

ラムダを使うには:


sample.kt

val square = { a:Int ->

a * a
}
println(square(4)) //16

ラムダの戻り値の型は ->で表されます。

パラメータが1つしかなくその型を推測できるラムダの場合、Kotlinはパラメータのプレースホルダオブジェクト名itを指定し->を削除してより簡潔な構文にします。

itを使ってラムダを呼び出すには:


sample.kt

var multiple: (Int) -> Int = { it * it }

println(multiple(4)) //16
multiple = {it * it * it} //don't need '->'
println(multiple(2)) //8


Swift — Closure

クロージャを使用するには:


sample.swift

var square = { (a: Int) -> Int in

return a * a
}
print(square(4))

Swiftでは、クロージャの戻り値の型は ->で表されます。

ちょうどKotlinのように、これはプレースホルダオブジェクトを使ってより簡潔にすることができます。ただしSwiftでは一つのパラメータに限定されているわけではありません。$0はプレースホルダーオブジェクト名です。Swiftはclosure引数に与えます。ここで$0$1$2などは連続する各パラメーターに使用されます。変数squareの型が既に整数として設定されている上記の例から続けると、次のようにクロージャを書き直すことができます:


sample.swift

square = { //we can even omit the parameter list

$0 * $0
}
print(square(3)) //9


Nullable / Optional Types

KotlinとSwiftはどちらも、「安全に」使用できる言語であるため、偶発的に値がnull / nilになることはありません。プログラマは故意にアプリケーションサイクルのある時点での値を持っていたり値を持たなかったりする特殊な型を持つ変数を宣言する必要があります。Swiftではこれを「optional」として知られており、Kotlinでは「nullable」と呼ばれています。どちらの言語でも変数型の右にが置かれます。


Kotlin — Nullable Type


sample.kt

var authToken: String? = "some long string"

authToken = null


Swift — Optional Type


sample.swift

var authToken: String? = "some long string"

authToken = nil

Kotlinでnullableを宣言し、Swiftでoptionalであると宣言する構文はまったく同じです。


Handling a Nullable / Optional Type

期待していない時にnull値にアクセスしてしまってクラッシュするのを避けるため、両方の言語でNullable型を適切にアンラッピングするための特定の構文が用意されています。どちらの言語でもnullableな変数が常に値を持つことを100%保証していない限り、optionalの型を「force unwrap(強制的に解除する)」(またはKotlinのnullable型に対してnot-null assertionを使用する)ことは推奨されません。強制アンラップは!を使って行うことができます!Swiftでは!、Kotlinでは!!です。Swiftの構文は開発者にとってwarning(警告)として機能し、Kotlinはそれをさらに一歩進めます!!私たちがそこで何をしたかを見てください。

optional / nullable 型を適切に処理するには、以下のような方法があります。


Kotlin — Null Safety


sample.kt

var id: Int? = 10

var userId = 0
if (id != null) {
userId = id
} else {
userID = -1
}
println(userID) //prints 10

上記のコードではidがnullの場合、userIdに-1の値が割り当てられます。

Kotlinにはエルビス演算子?:経由でnull安全性を利用するためにさらに簡潔な方法があります。


sample.kt

var id: Int? = null

val userId = id ?: -1 //userID is -1

エルビス演算子はnull値に遭遇した場合のデフォルト値を提供することによって、何があってもnullableから値を取得することを保証します。

上記の例ではエルビス演算子を使用するとuserIdidの値になり、idにnullが含まれている場合は-1となります。

SwiftとKotlinは条件付きキャスト(Kotlinでは「safe cast」と呼ばれます)をサポートしていますが、Swiftではサブキャストへのダウンキャストにのみ適用されます。


Kotlin


sample.kt

var practiceTime = (trombonePlayer as? Bandmember).practiceTime


Swiftとは異なり、Kotlinではguard文はありません。ただしエルビス演算子を使用して同様の効果を得ることができます。

先に学んだことを使ってエルビス演算子を以下のようなsafe castと組み合わせてください:


sample.kt

fun readyForParade (participant : Bandmember) : Boolean {

val trombonePlayer = participant as? Bandmember ?: return false
return trombonePlayer. practiceTime > 6000
}

ここでsafe cast内の値が失敗するとreadyForParadeはnullになり関数はそれ以上実行されずに戻ります。

Kotlinのlet関数をsafe-call演算子?.と組み合わせて使用すると、nullableな式を処理する書き方ができます。


sample.kt

val userId = id.let { nonNullId -> nonNullId } ?: -1


最後にKotlinはタイプチェックとキャストを組み合わせたスマートキャストという機能を利用します。Swiftではif let式(下を参照)がありますが、Kotlinではスマートキャストがあります。Kotlinのスマートなキャストはちょっと「スマート」ように見えますが、Swiftの場合はコードを読むときに少しだけはっきりしています。基本的にKotlinではnullable castが真であることが証明されると(つまりnullでないか、または型が正しい型です)、そのスコープの残りの部分では、キャストした変数はそれ以上のキャストや構文なしで直接使用できます。


sample.kt

val id: Int? = nil

if (id != nil) {
print(id) // id can be used directly in this scope
} else {
print("id is null")
}

次のセクションのif letのSwift文と上記のコードを対比してください。


Swift – Optional Handling

SwiftはSwift 3でnil-coalescing演算子を導入しました。これは、Kotlinのエルビス演算子のように動作します。


sample.swift

let id: Int? = nil

var userId = id ?? -1 //prints -1

Swiftは、unwrapを強制する(force unwrapping)のではなく、オプションを扱うための適切な方法として、先に述べたif let構文を使用します。


sample.swift

let id: Int? = nil

if let userId = id {
print(userId)
} else {
print("userId is null")
}

Swiftはguardステートメントを使用して、コード実行の早期終了を提供します。あなたが望まない条件よりむしろあなたが望む条件をチェックすることによって、そして重要なコードの大部分がifブロックの中にネストされないようにすることによって、if letを明確にします。

また、Kotlinのsmart castメカニズムに近づくと、guard文の後に、条件の一部としてoptionalのバインディングを使用して値が割り当てられた変数や定数が、guard文に現れます。guardステートメントには、関数から外れる時用のelseの条件が必要です。


sample.swift

func registerUser(userId: Int?) {

guard let id = userId else { return }
//do stuff with id
id.description
}

これはKotlinのスマートキャストメカニズムと全く同じではないことに注意してください。この例ではuserIdは依然としてoptionalとしてアクセスする必要があるため、userId.descriptionを記述することはできません。


Control Flow

関数やクロージャがアプリケーションのビルディングブロックであるように、if-else、switch、forループなどの制御フローは、アプリケーションを動作させるための論理的な接着剤(glue)です。


Kotlin — if-else

If-else文は他のほとんどの言語と同様にKotlinで動作します:


sample.kt

if (a > b) {

println("Choose a")
} else {
println("Choose b")
}


Swift — if-else

Swiftのクールな機能の一つは、条件のまわりの括弧()もoptionalです。私はちょうどKotlinがこの機能を採用するのにどれくらいの時間がかかるか疑問に思っています。


sample.swift

if a > b {

print("Choose a")
} else {
print("Choose b")
}


Kotlin — when

whenはKotlinのswitch文に与えられた名前です。


sample.kt

val x = 3

when (x) {
1 -> println("x == 1")
2 -> println("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}


Swift – switch


sample.swift

let x = 3

switch x {
case 1:
print("x == 1")
case 2:
print("x == 2")
default:
print("x is neither 1 nor 2")
}

Swiftではswitch文は網羅的でなければならないことに注意してください。 つまり条件の可能な単一の値がすべてテストされていない場合は、default句が必要です。これはKotlinでは当てはまりません。switch文の場合、Swiftは実際にはより明確で、プログラマのエラーを起こしにくいです。一方でKotlinのswitch文の構文はより簡潔です。どちらの言語でも最初の一致条件が評価されるとすぐに文が戻されるため、break文は必要ありません。


Kotlin — for loop

Kotlinではloopを書くにはいくつかの方法があります。


sample.kt

val days = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")

for (day in days) {
println(day)
}

範囲演算子(range operator)を使用してforループを作成します。


sample.kt

for (item in 1..10) {

println(item)
}

この例では、..は包括的な範囲を指定します。番号1〜10が印刷されます。KotlinとSwiftの両方はさまざまな種類の範囲演算子を提供します。


Swift – for loop


sample.swift

let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

for day in days {
print(day)
}

Swiftの別の種類の範囲演算子を使用する:


sample.swift

for item in 1..<10 {

print(item)
}


Classes

両方の言語でクラスを宣言することはほぼ同じです:

Kotlin - Class


sample.kt

class MainActivity: AppCompatActivity() {

}


Swift - Class


sample.swift

class ViewController: UIViewController {

}


Kotlinの"constructors"と呼ばれるものは、Swiftの"initializers"です。Kotlinには、コンストラクタと対になるinitブロックもあります。

Kotlin - Constructor


sample.kt

class Car constructor(_model: String) {

val model: String

init {
model = _model
}
}


Swift - Initializer


sample.swift

public class Car {

let model: String

init(model: String) {
self.model = model
}
}



Class Extensions

クラス拡張は現代の言語で採用されているクールな機能です。既存のクラスの機能を拡張するのに役立ちます。

Kotlin - Extension

整数の2乗を返す関数を使ってIntクラスを拡張することを検討してください:


sample.kt

fun Int.square(): Int {

return this * this
}

println(5.square()) //prints 25


Swift - Extension


sample.swift

extension Int {

func square() -> Int {
return self * self
}
}

print(5.square()) //prints 25



Interface / Protocol

Kotlinや他の言語のインターフェースとして知られているものは、Swiftのプロトコルと呼ばれています。それは分離されたコードベースを維持したりポリモーフィズムを実現したり、テスト用のモックを作成するのに役立ちます。


Kotlin — Interface

Kotlinでは、インタフェース宣言はクラスの宣言と似ています:


sample.kt

interface Animal {

var canFly: Boolean

fun eat()

fun makeSound()
}



Swift - Protocol


sample.swift

protocol Animal {

var canFly: Bool {get}

func eat()

func makeSound()
}


interface / protocolは、デフォルト実装を持つこともできます。デフォルトの動作は、その関数がオーバーライドされずに呼び出されるたびに同じままです。変更する必要があるまで、または変更する必要がない限り、すべての条件でプロパティcanFlyfalseにしたいとします。


Kotlin - Default Implementation


sample.kt

interface Animal {

val canFly: Boolean

get() = true
}



Swift - デフォルトの実装

Extensionsはプロトコルにデフォルトの実装を与えるために使用されます:


sample.swift

protocol Animal {

var canFly: Bool {get}
}

extension Animal {
var canFly: Bool {
return false
}
}



Functional Programming Tools

これらの新しい言語には機能的なパラダイムを使ってコードを書くことができる強力なツールが満載されています。それらによって提供される多くのツールがありますが、それらのほんのいくつかを見てください。


map

mapはデータを新しいフォームに変換するために使用されます。


Kotlin - map

プロパティnameと生年月日を含むdata classPersonを考えてみましょう。


sample.kt

data class Person(var name: String, var dob: String)



注:Kotlinでは、Data classはclassと似ていますが、データを保持する唯一の目的を持っています。コードが簡潔になり、イニシャライザを書く必要がありません。data classは、「value objects」を実装するためのKotlinの強力な方法です。


さてPerson型のオブジェクトがほとんどないリストを考えてみましょう。


sample.kt

val persons = listOf(Person("Jon", "12 August"),

Person("Kim", "10 July"),
Person("Vanessa", "12 August"),
Person("Alisa", "26 March"), null)

リストの最後になぜnullを割り当てたのだろうと思うかもしれません。nullのリストを割り当てる必要はありません。 タイトに座って!すぐに答えがわかります。

すべてのpersonのnameを別々のリストに入れたいとします。それについてはmapの機能を使えば実現します。


sample.kt

val names = persons.map { it?.name } //Jon, Kim, Vanessa, Alisa, null



Swift — map

同様にSwiftではPersonstructを考えてみましょう:


sample.swift

struct Person { 

let name: String
let dob: String

init(name: String, dob: String) {
self.name = name
self.dob = dob
}
}


そしてPersonのarrayを作ります:


sample.swift

let persons = [Person(name: "Jon", dob: "12 August"),

Person(name: "Kim", dob: "10 July"),
Person(name: "Vanessa", dob: "12 August"),
Person(name: "Alisa", dob: "26 March"), nil]

persons配列をnamesの配列に変換するには:


sample.swift

let names = persons.map { $0?.name } //Jon, Kim, Vanessa, Alisa, nil



filterNotNull & compactMap

arrayからnull/nilのオブジェクトを削除するには:


Kotlin - filterNotNull


sample.kt

//returns List<Person> instead of List<Person?>

val nonNullPersons = persons.filterNotNull()


Swift - compactMap

compactMapmap関数に似ていますが、すべてのnon-nilオブジェクトを返します。


sample.swift

//returns [Person] instead of [Person?]

let nonNullPersons = persons.compactMap{$0}


Filter

loopを使用してarrayまたはlistを反復処理する代わりにfilter関数を使用して目的の値をフィルタリングすることができます。

生年月日が「12月」のarrayをフィルタリングします。


Kotlin - filter


sample.kt

val personWithSameDOB = persons.filter { it?.dob == "12 August" } 



Swift - filter


sample.swift

let personWithSameDOB = persons.filter { $0?.dob == "12 August" }



Understanding Differences Between Swift and Kotlin

あなたが見てきたようにSwiftとKotlinは多くの点で似ています。しかし、まだいくつかの違いがあります。


Data Class vs. Struct

Swiftにはstructやreference型などの値型があり、Kotlinにはreference型のみがあります。いくつかの例を見てみましょう。


Kotlin — Data Class

Kotlinの前の例のPersonクラスを考えてみましょう:


sample.kt

val personOne = Person("Jon", "12 August")

val personTwo = personOne // assign to another variable
personTwo.name = "Vanessa"
println(personOne.name) // prints Vanessa
println(personTwo.name) // prints Vanessa


Swift — Struct

さて上記のPersonクラスをSwiftstructとして考えてみましょう。


sample.swift

struct Person {

let name: String
let dob: String
}

let personOne = Person(name: "Jon", dob: "12 August")
var personTwo = personOne // assign to another variable
personTwo.name = "Vanessa"
print(personOne.name) // prints Jon
print(personTwo.name) // prints Vanessa



val vs. let

値を再度割当できない場合を除き、valletは2つの言語でどのように処理されるかに若干の違いがあります。


Kotlin — val

以下のpersonsのリストを考えてみましょう:


sample.kt

val persons = mutableListOf(Person("Jon", "12 August"),

Person("Kim", "10 July"),
Person("Vanessa", "12 August"),
Person("Alisa", "26 March"))
persons.add(Person("Shaun", "11 Jan")) // can append a new value


Swift - let

Swiftでは:


sample.swift

let persons = [Person(name: "Jon", dob: "12 August"),

Person(name: "Kim", dob: "10 July"),
Person(name: "Vanessa", dob: "12 August"),
Person(name: "Alisa", dob: "26 March")]
persons.append(Person(name: "Shaun", dob: "11 Jan")) // throws compile time error

ここでの基本的な違いは、arrayやlistの扱いです。Kotlinはvalで宣言されていても値を可変listに追加することができます。同様にvalを使用してKotlinでarrayを宣言し、後で添字表記を使用してarrayのメンバーを再割り当てすることができます。対照的に、Swiftでは、添字表記でメンバーを更新したり、値を追加したりするために、varを使ってarrayを可変として宣言する必要があります。


Where to Go From Here?

SwiftとKotlinの比較を読んだあと、どれくらいの共通点があるかをよく理解できたと思います。

これらの言語はどちらもモバイル開発に限定されません。両方とも、サーバーサイドの開発やクロスプラットフォームのアプリケーション開発にも使用されています。この目的を果たすために、あなたを助けるためにそこにいくつかのフレームワークがあります。 VaporとKituraはサーバーサイドで使用されています。これらのSwiftとビデオコースは、当社のWebサイトにあります。 KotlinにはSpring Bootがあります。クロスプラットフォームのアプリケーション開発には、Kotlin-NativeやScadeのようなフレームワークがあります。

最後にSwiftとKotlinの類似点と相違点について学ぶのに役立つ追加リソースがあります:


注記

nullable = null許可(null可能)