原文
こちらが原文です。翻訳の許可を頂いてます。
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での整数の宣言するには:
var age: Int = 15
型推論でプロパティを宣言するには:
var firstName = "Ray"
Swift – Mutable Properties(変更可能なプロパティ)
Swiftでの整数の宣言するには:
var age: Int = 15
型推論でプロパティを宣言するには:
var firstName = "Ray"
Kotlin — Immutable Properties(不変プロパティ)
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(不変プロパティ)
let hoursInADay = 24
let secondsInAMinute = 60
let quote = "If you don't work smart, you will probably be replaced by an AI."
したがって上記のように、KotlinとSwiftの不変プロパティの宣言の基本的な違いは、val
とlet
キーワードです。
Data Structures(データ構造)
データ構造はどの言語も重要な部分です。これらは、データの整理、格納、および操作に使用されます。Array、linked list、dictionaryまたはmap、classおよびinterfaceは、すべてデータ構造の例です。
Arrays
SwiftとKotlinの両方でarrayは固定型です。つまりarrayは文字列の集合ですが文字列と整数の混合ではありません。
Swift — Array
Swiftで可変なarrayを宣言するには:
var names = Array<String>()
値を追加するには:
names.append("Fuad") //index 0
names.append("Eric") //index 1
names.append("Joe") // index 2
Swiftで不変なarrayを宣言するには:
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
Kotlin — Arrays and Lists
Kotlinではarrayは変更可能ですが、固定サイズで宣言されています。次の例では5つの要素を持つKotlinの可変arrayを宣言しています。すべてデフォルト値の0に設定されています。
val numbers = Array<Int>(5){0}
array内の項目の値を変更するには:
numbers[1] = 2 //numbers is now (0, 2, 0, 0, 0)
上記のarrayは変更可能ですがval
キーワードを使用して不変変数に割り当てることができました。これについては後で詳しく説明します。
Kotlinで不変のarrayを宣言するには:
val days = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
もう1つのKotlinコレクションタイプ、listsはSwiftのarrayタイプに似ています。listは可変数の要素を持つことができ宣言後にサイズが大きくなります。
var names = ArrayList<String>() //creates a list of strings, initially with no elements
Kotlinリストに値を追加するには:
names.add("Fuad") //index 0
names.add("Eric") //index 1
names.add("Joe") // index 2
Kotlinで不変のlistを宣言するには:
val days = listOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
Dictionaries / Maps
Dictionary(Swift)またはMap(Kotlin)は別個の値を格納する場合に非常に有用なデータ構造です。デフォルトでDitionaryやMapは拡大できますが、KotlinとSwiftの両方で、パフォーマンスを向上させるための容量を定義できます。
Kotlin
var namesWithAges = HashMap<String, Int>()
var namesWithAges = HashMap<String, Int>(20) //define capacity
値を割り当てるには:
namesWithAges.put("John Doe", 34)
//OR
namesWithAges["John Doe"] = 34
不変のMapを作成するには:
val namesWithAges = mapOf("John Doe" to 34, "Jane Doe" to 29)
Swift
SwiftのDictionaryの作成するには:
var namesWithAges : [String: Int] = [:]
namesWithAges.reserveCapacity(20) //define capacity
値を割り当てるには:
namesWithAges["John Doe"] = 34
不変のDictionaryを宣言するには:
let namesWithAges = ["John Doe" : 34, "Jane Doe" : 29]
Functions
関数(function)は、コードベースの基本ブロックです。コードを読みやすく保守的に整理するのに役立ちます。
Kotlin Functions
Kotlin関数のプロトタイプ(Prototype):
fun functionName(parameterName: DataType): ReturnType {
//function body
}
関数の戻り値の型はコロン(:
)で表されます。Kotlinではすべての関数に戻り型があります。戻り値の型を指定しなかった場合は、デフォルトで特別な型のUnit
が返されます。
Kotlin関数の例を次に示します。
fun greetUser(name: String): String {
return "Hello ${name}!"
}
Kotlin — Default Argument(デフォルト引数)
Kotlinではパラメータにデフォルト値を割り当てることができます。たとえば上記の関数の例を考えてみましょう。
fun greetUser(name: String = "World"): String {
return "Hello ${name}!"
}
今、上記のような引数を渡さずに上記の関数を呼び出すことができます:
greetUser() //returns Hello World!
Swift Functions
Swift関数のプロトタイプ(Prototype):
func functionName(parameterName: DataType) -> ReturnType {
//function body
}
関数の戻り値の型は->
で表されます。さらに関数が明示的に値を返さないときにSwiftが推論する関数の型は特殊な型です。空のタプルであるVoid
は、()
として記述されます。
Swiftに実際の関数を書く:
func getGreetings(name: String) -> String {
return "Hello \(name)!"
}
Swift — Default Argument(デフォルト引数)
デフォルトの引数を持つSwift関数:
func getGreetings(name: String = "World") -> String {
return "Hello \(name)!"
}
次のようなデフォルトの動作で関数を呼び出します。
getGreetings() //returns Hello World!
Lambda Functions / Closures
ラムダ(Kotlin)とクロージャ(Swift)はあなたのコーディング工廠で別の有用なビルディングブロックになります。 本質的には、それらは無名の関数です。それらは変数に割り当てられ、他の値と同様に渡されます。関数を値として扱う機能はSwiftとKotlinの関数プログラミングの側面の1つです。クロージャの一般的な使用例としては、非同期呼び出しの処理(ネットワーク要求やコレクションの操作など)があります。
Kotlin — Lambda
ラムダを使うには:
val square = { a:Int ->
a * a
}
println(square(4)) //16
ラムダの戻り値の型は ->
で表されます。
パラメータが1つしかなくその型を推測できるラムダの場合、Kotlinはパラメータのプレースホルダオブジェクト名it
を指定し->
を削除してより簡潔な構文にします。
it
を使ってラムダを呼び出すには:
var multiple: (Int) -> Int = { it * it }
println(multiple(4)) //16
multiple = {it * it * it} //don't need '->'
println(multiple(2)) //8
Swift — Closure
クロージャを使用するには:
var square = { (a: Int) -> Int in
return a * a
}
print(square(4))
Swiftでは、クロージャの戻り値の型は ->
で表されます。
ちょうどKotlinのように、これはプレースホルダオブジェクトを使ってより簡潔にすることができます。ただしSwiftでは一つのパラメータに限定されているわけではありません。$0
はプレースホルダーオブジェクト名です。Swiftはclosure引数に与えます。ここで$0
、$1
、$2
などは連続する各パラメーターに使用されます。変数square
の型が既に整数として設定されている上記の例から続けると、次のようにクロージャを書き直すことができます:
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
var authToken: String? = "some long string"
authToken = null
Swift — Optional Type
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
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安全性を利用するためにさらに簡潔な方法があります。
var id: Int? = null
val userId = id ?: -1 //userID is -1
エルビス演算子はnull値に遭遇した場合のデフォルト値を提供することによって、何があってもnullableから値を取得することを保証します。
上記の例ではエルビス演算子を使用するとuserId
はid
の値になり、id
にnullが含まれている場合は-1となります。
SwiftとKotlinは条件付きキャスト(Kotlinでは「safe cast」と呼ばれます)をサポートしていますが、Swiftではサブキャストへのダウンキャストにのみ適用されます。
Kotlin
var practiceTime = (trombonePlayer as? Bandmember).practiceTime
Swiftとは異なり、Kotlinではguard
文はありません。ただしエルビス演算子を使用して同様の効果を得ることができます。
先に学んだことを使ってエルビス演算子を以下のようなsafe castと組み合わせてください:
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な式を処理する書き方ができます。
val userId = id.let { nonNullId -> nonNullId } ?: -1
最後にKotlinはタイプチェックとキャストを組み合わせたスマートキャストという機能を利用します。Swiftではif let式(下を参照)がありますが、Kotlinではスマートキャストがあります。Kotlinのスマートなキャストはちょっと「スマート」ように見えますが、Swiftの場合はコードを読むときに少しだけはっきりしています。基本的にKotlinではnullable castが真であることが証明されると(つまりnullでないか、または型が正しい型です)、そのスコープの残りの部分では、キャストした変数はそれ以上のキャストや構文なしで直接使用できます。
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のエルビス演算子のように動作します。
let id: Int? = nil
var userId = id ?? -1 //prints -1
Swiftは、unwrapを強制する(force unwrapping)のではなく、オプションを扱うための適切な方法として、先に述べたif let
構文を使用します。
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
の条件が必要です。
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で動作します:
if (a > b) {
println("Choose a")
} else {
println("Choose b")
}
Swift — if-else
Swiftのクールな機能の一つは、条件のまわりの括弧()
もoptionalです。私はちょうどKotlinがこの機能を採用するのにどれくらいの時間がかかるか疑問に思っています。
if a > b {
print("Choose a")
} else {
print("Choose b")
}
Kotlin — when
when
はKotlinのswitch
文に与えられた名前です。
val x = 3
when (x) {
1 -> println("x == 1")
2 -> println("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
Swift – switch
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を書くにはいくつかの方法があります。
val days = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
for (day in days) {
println(day)
}
範囲演算子(range operator)を使用してfor
ループを作成します。
for (item in 1..10) {
println(item)
}
この例では、..
は包括的な範囲を指定します。番号1〜10が印刷されます。KotlinとSwiftの両方はさまざまな種類の範囲演算子を提供します。
Swift – for loop
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
for day in days {
print(day)
}
Swiftの別の種類の範囲演算子を使用する:
for item in 1..<10 {
print(item)
}
Classes
両方の言語でクラスを宣言することはほぼ同じです:
Kotlin - Class
class MainActivity: AppCompatActivity() {
}
Swift - Class
class ViewController: UIViewController {
}
Kotlinの"constructors"と呼ばれるものは、Swiftの"initializers"です。Kotlinには、コンストラクタと対になるinit
ブロックもあります。
Kotlin - Constructor
class Car constructor(_model: String) {
val model: String
init {
model = _model
}
}
Swift - Initializer
public class Car {
let model: String
init(model: String) {
self.model = model
}
}
Class Extensions
クラス拡張は現代の言語で採用されているクールな機能です。既存のクラスの機能を拡張するのに役立ちます。
Kotlin - Extension
整数の2乗を返す関数を使ってInt
クラスを拡張することを検討してください:
fun Int.square(): Int {
return this * this
}
println(5.square()) //prints 25
Swift - Extension
extension Int {
func square() -> Int {
return self * self
}
}
print(5.square()) //prints 25
Interface / Protocol
Kotlinや他の言語のインターフェースとして知られているものは、Swiftのプロトコルと呼ばれています。それは分離されたコードベースを維持したりポリモーフィズムを実現したり、テスト用のモックを作成するのに役立ちます。
Kotlin — Interface
Kotlinでは、インタフェース宣言はクラスの宣言と似ています:
interface Animal {
var canFly: Boolean
fun eat()
fun makeSound()
}
Swift - Protocol
protocol Animal {
var canFly: Bool {get}
func eat()
func makeSound()
}
interface / protocolは、デフォルト実装を持つこともできます。デフォルトの動作は、その関数がオーバーライドされずに呼び出されるたびに同じままです。変更する必要があるまで、または変更する必要がない限り、すべての条件でプロパティcanFly
をfalse
にしたいとします。
Kotlin - Default Implementation
interface Animal {
val canFly: Boolean
get() = true
}
Swift - デフォルトの実装
Extensionsはプロトコルにデフォルトの実装を与えるために使用されます:
protocol Animal {
var canFly: Bool {get}
}
extension Animal {
var canFly: Bool {
return false
}
}
Functional Programming Tools
これらの新しい言語には機能的なパラダイムを使ってコードを書くことができる強力なツールが満載されています。それらによって提供される多くのツールがありますが、それらのほんのいくつかを見てください。
map
map
はデータを新しいフォームに変換するために使用されます。
Kotlin - map
プロパティnameと生年月日を含むdata class
Personを考えてみましょう。
data class Person(var name: String, var dob: String)
注:Kotlinでは、Data classはclassと似ていますが、データを保持する唯一の目的を持っています。コードが簡潔になり、イニシャライザを書く必要がありません。data classは、「value objects」を実装するためのKotlinの強力な方法です。
さてPerson型のオブジェクトがほとんどないリストを考えてみましょう。
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
の機能を使えば実現します。
val names = persons.map { it?.name } //Jon, Kim, Vanessa, Alisa, null
Swift — map
同様にSwiftではPersonstruct
を考えてみましょう:
struct Person {
let name: String
let dob: String
init(name: String, dob: String) {
self.name = name
self.dob = dob
}
}
そしてPersonのarrayを作ります:
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の配列に変換するには:
let names = persons.map { $0?.name } //Jon, Kim, Vanessa, Alisa, nil
filterNotNull & compactMap
arrayからnull
/nil
のオブジェクトを削除するには:
Kotlin - filterNotNull
//returns List<Person> instead of List<Person?>
val nonNullPersons = persons.filterNotNull()
Swift - compactMap
compactMap
はmap
関数に似ていますが、すべてのnon-nilオブジェクトを返します。
//returns [Person] instead of [Person?]
let nonNullPersons = persons.compactMap{$0}
Filter
loopを使用してarrayまたはlistを反復処理する代わりにfilter関数を使用して目的の値をフィルタリングすることができます。
生年月日が「12月」のarrayをフィルタリングします。
Kotlin - filter
val personWithSameDOB = persons.filter { it?.dob == "12 August" }
Swift - filter
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クラスを考えてみましょう:
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
として考えてみましょう。
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
値を再度割当できない場合を除き、val
とlet
は2つの言語でどのように処理されるかに若干の違いがあります。
Kotlin — val
以下のpersonsのリストを考えてみましょう:
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では:
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の類似点と相違点について学ぶのに役立つ追加リソースがあります:
- Swift Is Like Kotlin
- Kotlin vs. Swift: Are Android and iOS Moving Towards Creating a Universal Language?
- Swift vs. Kotlin
- Swift Vs. Kotlin: Similarities and Differences Every Developer Should Know
- Server Side Swift with Vapor
- Kotlin Apprentice
- Swift Apprentice
- My Transition From Swift to Kotlin by Hector Matos
注記
nullable = null許可(null可能)