Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
クラスの作り方に見るKotlinの表現力
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Taro Nagasawa
February 20, 2017
Programming
1.7k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
クラスの作り方に見るKotlinの表現力
JJUGナイトセミナー(
https://jjug.doorkeeper.jp/events/57443
)で発表した資料です。
Taro Nagasawa
February 20, 2017
More Decks by Taro Nagasawa
See All by Taro Nagasawa
Android開発者のための Kotlin Multiplatform入門
ntaro
0
1.8k
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.4k
#Ubie 狂気の認知施策と選考設計
ntaro
14
14k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.2k
KotlinでSpring 完全理解ガイド #jsug
ntaro
6
3.7k
Kotlinでサーバサイドを始めよう!
ntaro
1
1.1k
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
3.1k
Kotlin Contracts #m3kt
ntaro
4
4.4k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
590
Other Decks in Programming
See All in Programming
XHTMLが残したもの
yosuke_furukawa
PRO
2
570
Security issues being discussed on Web Platforms
petamoriken
0
420
自分的「カンファレンスの楽しみ方」
syumai
0
200
Foundry Localでエージェント開発
seosoft
0
160
Gmail/Google DriveをトリガーにAIエージェントを動かそう! / Run AI agents with Gmail/Google Drive as triggers!
har1101
3
490
数年滞っていたダークモード対応をおよそ2週間で完了させる
chigichan24
0
700
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
3
520
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
6
3.2k
Webの地図
yosuke_furukawa
PRO
4
3.6k
【DroidKaigi 2026】「アクセシビリティを利用するとき、 アクセシビリティもまたこちらを利用している」 〜マルウェアによる攻撃と防衛について〜
halunoyo
0
430
[GoCon2026] When Goroutines Are Not Enough: Runtime Locality in High-Throughput Go
takehaya
6
1.6k
[DroidKaigi 2026] Bring your own phones to Gradle Managed Devices
f2lk
0
110
Featured
See All Featured
Building Flexible Design Systems
yeseniaperezcruz
330
41k
Test your architecture with Archunit
thirion
2
2.4k
For a Future-Friendly Web
brad_frost
183
10k
The untapped power of vector embeddings
frankvandijk
2
1.9k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Product Roadmaps are Hard
iamctodd
55
13k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
260
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.8k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
GitHub's CSS Performance
jonrohan
1033
470k
Design in an AI World
tapps
1
310
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
Transcript
クラスの作り方に見る Kotlinの表現力 2017-02-20 JJUGナイトセミナー 長澤 太郎 @ngsw_taro
Kotlinとは? • Java仮想マシンをターゲットとしたプログラミング言語 ◦ JavaScriptやAndroidもサポート • IntelliJ IDEAでおなじみのJetBrainsが開発 • 2011年に発表され、2016年2月にver1.0がリリース
• 現在 ver1.0.6, 1.1-beta2 • 静的型付けオブジェクト指向言語 • 簡潔、安全、Javaとの相互運用性
Hello World package sample fun main(args: Array<String>) { if(args.isEmpty()) return
val name = args[0] println("Hello, ${name}!") }
クラスの作り方に見る Kotlinの表現力
• 長澤 太郎 たろーって呼んでね • @ngsw_taro 自己紹介
エムスリー株式会社 jobs.m3.com/engineer
エバンジェリストな私 • Kotlin歴 5年 • 日本Kotlinユーザグループ代表 • 講演実績多数 ◦ DroidKaigi
2015, 2016, 2017予定 ◦ JJUG CCC 2015 Fall ◦ 福岡、京都など遠征も • 執筆実績多数 ◦ 単行本、商業誌、同人誌
もくじ 1. 有理数 2. 片方向リストのノード
• 有理数クラス Rational • プロパティ ◦ 分子(numerator) ◦ 分母(denominator) •
メソッド ◦ いろんな計算を提供 題材その1: 有理数を表現するクラス
クラス定義 class Rational(val numerator: Int, val denominator: Int) // 使い方
fun main(args: Array<String>) { val half = Rational(1, 2) println(half.denominator) // 2 }
クラス定義 class Rational(val numerator: Int, val denominator: Int) // 使い方
fun main(args: Array<String>) { val half = Rational(1, 2) println(half.denominator) // 2 } クラス名
クラス定義 class Rational(val numerator: Int, val denominator: Int) // 使い方
fun main(args: Array<String>) { val half = Rational(1, 2) println(half.denominator) // 2 } プライマリコンストラクタ
クラス定義 class Rational(val numerator: Int, val denominator: Int) // 使い方
fun main(args: Array<String>) { val half = Rational(1, 2) println(half.denominator) // 2 } プロパティ
クラス定義 class Rational(val numerator: Int, val denominator: Int) // 使い方
fun main(args: Array<String>) { val half = Rational(1, 2) println(half.denominator) // 2 } インスタンス生成
クラス定義 class Rational(val numerator: Int, val denominator: Int) // 使い方
fun main(args: Array<String>) { val half = Rational(1, 2) println(half.denominator) // 2 } プロパティにアクセス: Javaで言うフィールドとアクセサが一緒になったようなもの
ちなみにJavaだと public final class Rational { private final int numerator;
private final int denominator; public Rational(int numerator, int denominator) { this.numerator = numerator; this.denominator = denominator; } public int getNumerator() { return numerator; } public int getDenominator() { return denominator; } }
メソッドtoStringをオーバライド class Rational(val numerator: Int, val denominator: Int) { override
fun toString() = "${numerator}/${denominator}" }
メソッドtoStringをオーバライド class Rational(val numerator: Int, val denominator: Int) { override
fun toString(): String = "${numerator}/${denominator}" } オーバライドに必須
メソッドtoStringをオーバライド class Rational(val numerator: Int, val denominator: Int) { override
fun toString(): String = "${numerator}/${denominator}" } イコールで式を結びつける
メソッドtoStringをオーバライド class Rational(val numerator: Int, val denominator: Int) { override
fun toString(): String = "${numerator}/${denominator}" } 式が評価された文字列内に展開される = String Templates
確認しやすくなった! fun main(args: Array<String>) { println(Rational(2, 5)) // 「2/5」 println(Rational(3,
0)) // 「3/0」 println(Rational(4, 10)) // 「4/10」 println(Rational(9, 3)) // 「9/3」 }
確認しやすくなった! fun main(args: Array<String>) { println(Rational(2, 5)) // 「2/5」 println(Rational(3,
0)) // 「3/0」 println(Rational(4, 10)) // 「4/10」 println(Rational(9, 3)) // 「9/3」 } 分母ゼロを禁止したい!
イニシャライザ class Rational(val numerator: Int, val denominator: Int) { init
{ if(denominator == 0) throw IllegalArgumentException("ゼロはダメ") } override fun toString(): String = ... }
イニシャライザ class Rational(val numerator: Int, val denominator: Int) { init
{ if(denominator == 0) throw IllegalArgumentException("ゼロはダメ") } override fun toString(): String = ... } イニシャライザ
イニシャライザ class Rational(val numerator: Int, val denominator: Int) { init
{ require(denominator != 0, {"ゼロはダメ"}) } override fun toString(): String = ... } 標準関数
もう大丈夫!? fun main(args: Array<String>) { println(Rational(2, 5)) // 「2/5」 println(Rational(3,
0)) // 例外 println(Rational(4, 10)) // 「4/10」 println(Rational(9, 3)) // 「9/3」 }
もう大丈夫!? fun main(args: Array<String>) { println(Rational(2, 5)) // 「2/5」 println(Rational(3,
0)) // 例外 println(Rational(4, 10)) // 「4/10」 println(Rational(9, 3)) // 「9/3」 } 約分したい!
非公開メソッド class Rational(val numerator: Int, val denominator: Int) { ...
private fun gcd(x: Int, y: Int): Int = if(y == 0) x else gcd(y, x % b) } xとyの最大公約数を返すメソッド
非公開メソッド class Rational(val numerator: Int, val denominator: Int) { ...
private fun gcd(x: Int, y: Int): Int = if(y == 0) x else gcd(y, x % y) } if-elseは式(値を返す)
非公開メソッド class Rational(val numerator: Int, val denominator: Int) { ...
tailrec private fun gcd(x: Int, y: Int): Int = if(y == 0) x else gcd(y, x % y) } 末尾再帰なので、最適化可能
非公開プロパティ class Rational(n: Int, d: Int) { init { require(d
!= 0, {"ゼロはダメ"}) } private val g: Int = gcd(n, d) val numerator: Int = n / g val denominator: Int = d / g ... }
非公開プロパティ class Rational(n: Int, d: Int) { init { require(d
!= 0, {"ゼロはダメ"}) } private val g: Int = gcd(n, d) val numerator: Int = n / g val denominator: Int = d / g ... } コンストラクタ引数(プロパティでない)
非公開プロパティ class Rational(n: Int, d: Int) { init { require(d
!= 0, {"ゼロはダメ"}) } private val g: Int = gcd(n, d) val numerator: Int = n / g val denominator: Int = d / g ... } 非公開プロパティ
非公開プロパティ class Rational(n: Int, d: Int) { init { require(d
!= 0, {"ゼロはダメ"}) } private val g: Int = gcd(n, d) val numerator: Int = n / g val denominator: Int = d / g ... } 公開プロパティ
すばらしい! fun main(args: Array<String>) { println(Rational(2, 5)) // 「2/5」 println(Rational(3,
0)) // 例外 println(Rational(4, 10)) // 「2/5」 println(Rational(9, 3)) // 「3/1」 }
演算子オーバロード class Rational(n: Int, d: Int) { ... operator fun
plus(r: Rational): Rational = Rational( numerator * r.denominator + r.numerator + denominator, denominator * r.denominator ) }
演算子オーバロード class Rational(n: Int, d: Int) { ... operator fun
plus(r: Rational): Rational = Rational( numerator * r.denominator + r.numerator + denominator, denominator * r.denominator ) } 有理数との足し算メソッド
演算子オーバロード class Rational(n: Int, d: Int) { ... operator fun
plus(r: Rational): Rational = Rational( numerator * r.denominator + r.numerator + denominator, denominator * r.denominator ) } +演算子を使ったメソッド呼び出しが可能に
演算子で計算できる! Rational(1, 6) + Rational(1, 3) //=> 1/2
オーバロード class Rational(n: Int, d: Int) { ... operator fun
plus(r: Rational): Rational = ... operator fun plus(i: Int): Rational = Rational(numerator + i * denominator, denominator) }
演算子で計算できる! Rational(1, 6) + Rational(1, 3) //=> 1/2 Rational(1, 4)
+ 2 //=> 9/4
じゃあこれは? Rational(1, 6) + Rational(1, 3) //=> 1/2 Rational(1, 4)
+ 2 //=> 9/4 2 * Rational(2, 5) // ???
拡張関数 class Rational(n: Int, d: Int) { ... } operator
fun Int.times(r: Rational): Rational = Rational(r.numerator * this, r.denominator)
拡張関数 class Rational(n: Int, d: Int) { ... } operator
fun Int.times(r: Rational): Rational = Rational(r.numerator * this, r.denominator) Intの拡張関数(しかも演算子オーバロード)
できた!! Rational(1, 6) + Rational(1, 3) //=> 1/2 Rational(1, 4)
+ 2 //=> 9/4 2 * Rational(2, 5) // 4/5
• 片方向リストのノード Node • プロパティ ◦ 値(value) ◦ 次のノードへのポインタ(next) 題材その2:
片方向リストのノード value: 111 next: value: 222 next: value: 333 next: nil Node.of(111, Node.of(222, Node.of(333, Node.nil)))
抽象クラス abstract class Node<out T> { abstract val value: T
abstract val next: Node<T> }
abstract class Node<out T> { abstract val value: T abstract
val next: Node<T> } 抽象クラス 抽象プロパティ
abstract class Node<out T> { abstract val value: T abstract
val next: Node<T> } 抽象クラス 共変指定 Java的には <? extends T>
継承してノードを表現 class PresentNode<out T> (override val value: T, override val
next: Node<T>): Node<T>() { override fun toString(): String = "${value} -> ${next}" }
継承してノードを表現 class PresentNode<out T> (override val value: T, override val
next: Node<T>): Node<T>() { override fun toString(): String = "${value} -> ${next}" } 継承 スーパクラスのコンストラクタ呼び出し
class PresentNode<out T> (override val value: T, override val next:
Node<T>): Node<T>() { override fun toString(): String = "${value} -> ${next}" } 継承してノードを表現 プロパティのオーバライド 内部フィールド + そのアクセサを自動提供
継承してノードを表現 class PresentNode<out T> (override val value: T, override val
next: Node<T>): Node<T>() { override fun toString(): String = "${value} -> ${next}" } ついでに 「1 -> 2 -> 3 -> X」のような表現を
(シングルトン)オブジェクト object AbsentNode: Node<Nothing> { override val value: Nothing get()
= throw UnsupportedOperationException() override val next: Nothing get() = throw UnsupportedOperationException() override fun toString(): String = "X" }
(シングルトン)オブジェクト object AbsentNode: Node<Nothing> { override val value: Nothing get()
= throw UnsupportedOperationException() override val next: Nothing get() = throw UnsupportedOperationException() override fun toString(): String = "X" }
(シングルトン)オブジェクト object AbsentNode: Node<Nothing> { override val value: Nothing get()
= throw UnsupportedOperationException() override val next: Nothing get() = throw UnsupportedOperationException() override fun toString(): String = "X" } あらゆる型のサブタイプ
(シングルトン)オブジェクト object AbsentNode: Node<Nothing> { override val value: Nothing get()
= throw UnsupportedOperationException() override val next: Nothing get() = throw UnsupportedOperationException() override fun toString(): String = "X" } プロパティのオーバライド 内部フィールドを持たない。カスタムgetterを提供
リストを作れる! PresentNode(1, PresentNode(2, AbsentNode)) //=> 1 -> 2 -> X
PresentNode<Number>(1.2, PresentNode<Int>(3, AbsentNode)) //=> 1.2 -> 3 -> X
リストを作れる! PresentNode(1, PresentNode(2, AbsentNode)) //=> 1 -> 2 -> X
PresentNode<Number>(1.2, PresentNode<Int>(3, AbsentNode)) //=> 1.2 -> 3 -> X
リストを作れる! PresentNode(1, PresentNode(2, AbsentNode)) //=> 1 -> 2 -> X
PresentNode<Number>(1.2, PresentNode<Int>(3, AbsentNode)) //=> 1.2 -> 3 -> X class Node<out T> ↑クラス定義時点における共変指定
コンパニオンオブジェクト abstract class Node<out T> { companion object { val
nil: Node<Nothing> = AbsentNode fun <T> of(value: T, next: Node<T> = nil) = PresentNode(value, next) } ... }
コンパニオンオブジェクト abstract class Node<out T> { companion object { val
nil: Node<Nothing> = AbsentNode fun <T> of(value: T, next: Node<T> = nil) = PresentNode(value, next) } ... } コンパニオンオブジェクト
コンパニオンオブジェクトのメンバを使う Node.of(1, Node.of(2, Node.nil)) //=> 1 -> 2 -> X
Node.of(1.2, Node.of(3, Node.of(4)) //=> 1.2 -> 3 -> 4 -> X
コンパニオンオブジェクトのメンバを使う Node.of(1, Node.of(2, Node.nil)) //=> 1 -> 2 -> X
Node.of(1.2, Node.of(3, Node.of(4)) //=> 1.2 -> 3 -> 4 -> X
デフォルト引数 abstract class Node<out T> { companion object { val
nil: Node<Nothing> = AbsentNode fun <T> of(value: T, next: Node<T> = nil) = PresentNode(value, next) } ... } 呼び出し時に引数を省略すると デフォルト値が使用される
通常の抽象クラスは見える人なら継承OK abstract class Node<out T> {...} class PresentNode<out T>(...): Node<T>()
{...} object AbsentNode: Node<Nothiing>() {...} class MyNode<out T>: Node<T>() {...}
通常の抽象クラスは見える人なら継承OK abstract class Node<out T> {...} class PresentNode<out T>(...): Node<T>()
{...} object AbsentNode: Node<Nothiing>() {...} class MyNode<out T>: Node<T>() {...} 今回つくったやつ
abstract class Node<out T> {...} class PresentNode<out T>(...): Node<T>() {...}
object AbsentNode: Node<Nothiing>() {...} class MyNode<out T>: Node<T>() {...} 通常の抽象クラスは見える人なら継承OK 第3者が継承させることができる。でも都合が悪い
シールドクラスで継承を制限 sealed class Node<out T> { ... class PresentNode<out T>(...):
Node<T>() {...} object AbsentNode: Node<Nothing>() {...} } class MyNode<out T>: Node<T>() {...}
シールドクラスで継承を制限 sealed class Node<out T> { ... class PresentNode<out T>(...):
Node<T>() {...} object AbsentNode: Node<Nothing>() {...} } class MyNode<out T>: Node<T>() {...} ↓この人はもはや継承できない(コンパイルエラー) 注: ver1.1では、シールドクラスの継承可能な範囲が その内部クラスから同一ファイル内へと緩和される。
ノード数プロパティ size sealed class Node<out T> { ... val size:
Int get() { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } return go(this, 0) } }
ノード数プロパティ size sealed class Node<out T> { ... val size:
Int get() { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } return go(this, 0) } } プロパティ size 内部フィールドを持たず、カスタム getterを提供
ノード数プロパティ size sealed class Node<out T> { ... val size:
Int get() { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } return go(this, 0) } } 再帰呼び出しでノード数を計算
ノード数プロパティ size sealed class Node<out T> { ... val size:
Int get() { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } return go(this, 0) } } when式=switchの強い版 分岐の網羅が必須
委譲プロパティによる遅延初期化 sealed class Node<out T> { ... val size: Int
by lazy { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } go(this, 0) } }
委譲プロパティによる遅延初期化 sealed class Node<out T> { ... val size: Int
by lazy { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } go(this, 0) } } プロパティアクセスを byの後に続くオブジェクトに委譲
委譲プロパティによる遅延初期化 sealed class Node<out T> { ... val size: Int
by lazy { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } go(this, 0) } } 標準関数 lazy 委譲される遅延初期化用オブジェクト を生成する
委譲プロパティによる遅延初期化 sealed class Node<out T> { ... val size: Int
by lazy { tailrec fun go(node: Node<T>, acc: Int): Int = when(node) { is AbsentNode -> acc is PresentNode<T> -> go(node.next, acc + 1) } go(this, 0) } } lazyの引数としてのラムダ式
本日登場したキーワード • クラス • プロパティ • メソッド • プライマリコンストラクタ •
オーバライド • String Templates • イニシャライザ • if-else • TCO • 演算子オーバロード • オーバロード • 拡張関数 • 抽象クラス • 抽象プロパティ • 共変 • 継承 • オブジェクト • Nothing型 • コンパニオンオブジェクト • デフォルト引数 • シールドクラス • when式 • 委譲プロパティ • ラムダ式
あなたと赤べこ、今すぐ書店へ