この記事は私のような「Kotlinが気になっているSwiftプログラマー」向けです。
Apple公式のSwiftチュートリアル A Swift Tour を、Kotlinで書き替えてみました。
第二回のトピックはこちらです。
・クラス
・列挙体
・構造体
・プロトコル(インターフェイス)
・拡張
前回の記事をまだお読みでなければ、こちらもぜひ。
SwiftとKotlinの構文比較 (1) 〜シンプルな値、制御フロー、関数とクロージャー
クラス
コンストラクタ
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class NamedShape(name: String) {
var numberOfSides: Int = 0
var name: String = ""
init {
this.name = name
}
fun simpleDescription() : String {
return "A shape with ${numberOfSides} sides."
}
}
Kotlinは上のようなプライマリー・コンストラクタ宣言と、セカンダリー・コンストラクタ宣言で記法が異なります。
セカンダリー・コンストラクタはconstructor(引数名: 型名)
という構文で宣言します。
ちょっと独特な感じがします。
Kotlin Refrence -Classes
継承
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
print(test.area()) // 27.04
print(test.simpleDescription()) // A square with sides of length 5.2.
class Square(sideLength: Double, name: String): NamedShape(name) {
var sideLength: Double
init {
this.sideLength = sideLength
numberOfSides = 4
}
fun area() : Double {
return sideLength * sideLength
}
override fun simpleDescription() : String {
return "A square with sides of length ${sideLength}."
}
}
fun main(args: Array<String>) {
val test = Square(sideLength = 5.2, name = "my test square")
println(test.area()) // 27.0400000000000003
println(test.simpleDescription()) // A square with sides of length 5.2.
}
Kotlinはスーパークラスでopen class NamedShape(name: String)
というように明示的にopen
宣言をしないと、継承やoverrideができません。
保守性を向上するための仕様でしょうね。なるほどな〜と思いました。
自分自身のインスタンスを示すキーワードはthis
です。
プロパティ
SwiftとKotlinのプロパティ構文は良く似ています。
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter) // 9.3
triangle.perimeter = 9.9
print(triangle.sideLength) // 3.3
class EquilateralTriangle(sideLength: Double, name: String): NamedShape(name) {
var sideLength: Double = 0.0
init {
this.sideLength = sideLength
numberOfSides = 3
}
var perimeter: Double
get() {
return 3.0 * sideLength
}
set(newValue) {
sideLength = newValue / 3.0
}
override fun simpleDescription() : String {
return "An equilateral triangle with sides of length ${sideLength}."
}
}
fun main(args: Array<String>) {
var triangle = EquilateralTriangle(sideLength = 3.1, name = "a triangle")
println(triangle.perimeter) // 9.3
triangle.perimeter = 9.9
println(triangle.sideLength) // 3.3000000000000003
}
Kotlinはクラスに(Javaのような)フィールドを持つことはできません。
プロパティにバッキングフィールドが必要な場合、field
という変数名で自動的にそれを提供してくれます。
var counter = 0 // イニシャライザの value はバッキングフィールドへ直に書き込まれる
set(value) {
if (value >= 0)
field = value
}
Kotlin Reference -Properties and Fields
列挙体
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
print(ace.rawValue) // 1
print(ace.simpleDescription()) // ace
enum class Rank(val rawValue: Int) {
ace(1),
two(2), three(3), four(4), five(5), six(6), seven(7), eight(8), nine(9), ten(10),
jack(11), queen(12), king(13);
fun simpleDescription() : String {
when(this) {
ace ->
return "ace"
jack ->
return "jack"
queen ->
return "queen"
king ->
return "king"
else ->
return rawValue.toString()
}
}
}
fun main(args: Array<String>) {
val ace = Rank.ace
println(ace.rawValue) // 1
println(ace.simpleDescription()) // ace
}
SwiftはInt、Kotlinはクラスを実体としているということで、少々違いがあります。
Kotlinはenum定数にnameプロパティを持っており、例えば上のコードのreturn "ace"
をreturn ace.name
としても同じ結果を得られます。
さらには、以下のようにも書けます。
when(this) {
ace, jack, queen, king ->
return this.name
else ->
return rawValue.toString()
}
構造体
struct Card {
var rank: Rank
func simpleDescription() -> String {
return "The \(rank.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .three)
print(threeOfSpades.simpleDescription()) // The 3
data class Card(var rank: Rank) {
fun simpleDescription() : String {
return "The ${rank.simpleDescription()}"
}
}
fun main(args: Array<String>) {
val threeOfSpades = Card(rank = Rank.three)
println(threeOfSpades.simpleDescription()) // The 3
}
Kotlinはclassにdata
修飾子をつけます。
プロトコル(インターフェイス)
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
interface ExampleProtocol {
var simpleDescription: String
fun adjust()
}
Kotlinのインターフェイスも実装(デフォルト実装)を持てます。
拡張
extension Int {
var simpleDescription: String {
return "The number \(self)"
}
}
print(7.simpleDescription) // The number 7
val Int.simpleDescription: String get() = "The number ${this}"
fun main(args: Array<String>) {
println(7.simpleDescription) // The number 7
}
Swiftはクラスを拡張、Kotlinはメソッドやプロパティ単位で拡張します。
その関係上、Swiftでのextension クラス: プロトコル
と同等の設計はKotlinではできません。
おわりに
SwiftとKotlinの構文は良く似ていて、どちらもシンプルでセーフティですよね。
どちらも書いていて楽しい言語だと感じます。
唯一の欠点はJavaを書くのが辛くなってしまうことです(笑)
なお、例外処理については、概念的な違いの理解が必要であり、構文比較だけでは意味がないと思われたため割愛しました。