ボイラープレート
class User {
private final Long id;
private final String name;
public User(final Long id, final String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final User user = (User) o;
if (!id.equals(user.id)) return false;
return name.equals(user.name);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
● 例えば Userクラス
○ idとnameを持つ
● ゲッター
● equals, hashCode, toString
Slide 22
Slide 22 text
ボイラープレート
class User {
private final Long id;
private final String name;
public User(final Long id, final String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final User user = (User) o;
if (!id.equals(user.id)) return false;
return name.equals(user.name);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
● 例えば Userクラス
○ idとnameを持つ
● ゲッター
● equals, hashCode, toString
● idのデフォルト値の使用を許可
するためにコンストラクタを追加
● Lombokのようなビルダーパ
ターンを実装
Slide 23
Slide 23 text
ボイラープレート
class User {
private final Long id;
private final String name;
public User(final Long id, final String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final User user = (User) o;
if (!id.equals(user.id)) return false;
return name.equals(user.name);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
● 例えば Userクラス
○ idとnameを持つ
● ゲッター
● equals, hashCode, toString
● idのデフォルト値の使用を許可
するためにコンストラクタを追加
● Lombokのようなビルダーパ
ターンを実装
関心: 読み取り専用 id と name が存在する
Slide 24
Slide 24 text
nullポインタのデリファレンス問題
String s = null;
s.toUpperCase();
Slide 25
Slide 25 text
nullポインタのデリファレンス問題
String s = null;
s.toUpperCase();
当然、ぬるぽります。
Slide 26
Slide 26 text
ぬるぽが嫌ならnullチェックをすれば(ry
String s = なんらかのメソッド();
String got = (s != null)
? s.toUpperCase()
: null;
Slide 27
Slide 27 text
それ本当?
String s = reverse("Hello");
String got = (s != null)
? s.toUpperCase()
: null;
nullは返し得ないメソッド
Slide 28
Slide 28 text
それ本当? 実際、本当
String s = reverse("Hello");
String got = (s != null)
? s.toUpperCase()
: null;
nullは返し得ないメソッド
Slide 29
Slide 29 text
それ本当? 実際、本当
String s = reverse("Hello");
String got = (s != null)
? s.toUpperCase()
: null;
でも、そんなことしたら
余計に冗長になるだけ
nullは返し得ないメソッド
ラムダ式
val list = listOf(1, 2, 3)
map(list, { i: Int -> i * i }) //=> [1, 4, 9]
map(list, { i -> i * i })
map(list, { it * it })
map(list) { it * it }
Slide 60
Slide 60 text
ラムダ式
val list = listOf(1, 2, 3)
map(list, { i: Int -> i * i }) //=> [1, 4, 9]
map(list, { i -> i * i })
map(list, { it * it })
map(list) { it * it }
←引数の型を省略可
Slide 61
Slide 61 text
ラムダ式
val list = listOf(1, 2, 3)
map(list, { i: Int -> i * i }) //=> [1, 4, 9]
map(list, { i -> i * i })
map(list, { it * it })
map(list) { it * it }
←暗黙の変数 it
Slide 62
Slide 62 text
ラムダ式
val list = listOf(1, 2, 3)
map(list, { i: Int -> i * i }) //=> [1, 4, 9]
map(list, { i -> i * i })
map(list, { it * it })
map(list) { it * it } ←ラムダ式を外出し構文糖衣
Slide 63
Slide 63 text
ラムダ式
val list = listOf(1, 2, 3)
map(list, { i: Int -> i * i }) //=> [1, 4, 9]
map(list, { i -> i * i })
map(list, { it * it })
map(list) { it * it }
map(list) {...}ってイケてなくない?
クラス
class User(val name: String,
val id: Long)
プライマリコンストラクタ
Slide 73
Slide 73 text
プロパティ
class User(val name: String,
val id: Long)
val user = User("Taro", 123)
user.id //=> 123
user.name //=> "Taro"
Slide 74
Slide 74 text
プロパティ
class User(val name: String,
val id: Long)
val user = User("Taro", 123)
user.id //=> 123
user.name //=> "Taro"
インスタンス生成
newキーワード不要
Slide 75
Slide 75 text
プロパティ
class User(val name: String,
val id: Long)
val user = User("Taro", 123)
user.id //=> 123
user.name //=> "Taro"
プロパティ
→単純なgetter/setter地獄から解放される
Javaのフィールドのように見
えるが、実際には内部状態
(実装)とAPIを分けて持ってい
る。必要なら実装のカスタマイ
ズが可能。
Slide 76
Slide 76 text
デフォルト引数
class User(val name: String,
val id: Long? = null)
引数のデフォルト値
Slide 77
Slide 77 text
デフォルト引数
class User(val name: String,
val id: Long? = null)
val user = User("Taro")
デフォルト値が設定されている
引数は省略可
Slide 78
Slide 78 text
名前付き引数
class User(val name: String,
val id: Long? = null)
val user = User(id = 123,
name = "Taro")
名前で引数を指定できる
→順序を入れ替えられる
→引数が増えても混乱しない
Slide 79
Slide 79 text
データクラス
data class User(val name: String,
val id: Long? = null)
● 修飾子dataを付けるだけ
● equals, hashCode, toStringが手に入る
● copyメソッドが手に入る
● componentNメソッドが手に入る
Slide 80
Slide 80 text
equals, hashCode, toString
val user = User("Taro")
user == User("Taro") //=> true
user.toString() //=> User(name=Taro, id=null)
Slide 81
Slide 81 text
copyメソッド
val user = User("Taro")
val user2 = user.copy(id = 123)
user2.toString() //=> User(name=Taro, id=123)
user === user2 //=> false
Slide 82
Slide 82 text
val user = User("Taro")
val user2 = user.copy(id = 123)
user2.toString() //=> User(name=Taro, id=123)
user === user2 //=> false
copyメソッド
変更したいプロパティを
指定して、コピーを得る
Slide 83
Slide 83 text
val user = User("Taro")
val user2 = user.copy(id = 123)
user2.toString() //=> User(name=Taro, id=123)
user === user2 //=> false
copyメソッド
コピーなので、異なるインスタンス
Slide 84
Slide 84 text
componentNメソッド
val user = User("Taro", 123)
user.component1() //=> "Taro"
user.component2() //=> 123
val (name, id) = user
name //=> "Taro"
id //=> 123
Slide 85
Slide 85 text
componentNメソッド
val user = User("Taro", 123)
user.component1() //=> "Taro"
user.component2() //=> 123
val (name, id) = user
name //=> "Taro"
id //=> 123
Slide 86
Slide 86 text
データクラス簡単&便利
data class User(
name: String,
id: Long? = null
)
class User {
private final Long id;
private final String name;
public User(final Long id, final String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final User user = (User) o;
if (!id.equals(user.id)) return false;
return name.equals(user.name);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Slide 87
Slide 87 text
Null安全
val s1: String = null
val s2: String? = null
s2.toUpperCase()
if(s2 != null)
s2.toUpperCase()
s2?.toUpperCase()
Slide 88
Slide 88 text
Null安全
val s1: String = null // NG
val s2: String? = null
s2.toUpperCase()
if(s2 != null)
s2.toUpperCase()
s2?.toUpperCase()
通常の型の変数には、nullを代入できない
Slide 89
Slide 89 text
Null安全
val s1: String = null // NG
val s2: String? = null // OK
s2.toUpperCase()
if(s2 != null)
s2.toUpperCase()
s2?.toUpperCase()
?付きの型の変数には、nullを代入可
Slide 90
Slide 90 text
Null安全
val s1: String = null // NG
val s2: String? = null // OK
s2.toUpperCase() // NG
if(s2 != null)
s2.toUpperCase()
s2?.toUpperCase()
?な参照は、そのままデリファレンスできない
→ぬるぽる危険性があるから
Slide 91
Slide 91 text
Null安全
val s1: String = null // NG
val s2: String? = null // OK
s2.toUpperCase() // NG
if(s2 != null)
s2.toUpperCase() // OK
s2?.toUpperCase()
nullでないことが保証される文脈では
デリファンレス可能
Slide 92
Slide 92 text
Null安全
val s1: String = null // NG
val s2: String? = null // OK
s2.toUpperCase() // NG
if(s2 != null)
s2.toUpperCase() // OK
s2?.toUpperCase() // OK
安全呼び出し:
s2がnullなら、何もせず直ちにnullを返す
Slide 93
Slide 93 text
便利な標準ライブラリ関数 let
inline fun T.let(f: (T) -> R): R = f(this)
Slide 94
Slide 94 text
let + Null安全
fun reverse(s: String): String {...}
val reverse: String? = str?.let { reverse(it) }
fun findById(id: Long): User? {...}
val user: User? = userId?.let { findById(it) }
user?.let { println(it }
Slide 95
Slide 95 text
let + Null安全
fun reverse(s: String): String {...}
val reverse: String? = str?.let { reverse(it) }
fun findById(id: Long): User? {...}
val user: User? = userId?.let { findById(it) }
user?.let { println(it }
Optional#map的な役割
Slide 96
Slide 96 text
let + Null安全
fun reverse(s: String): String {...}
val reverse: String? = str?.let { reverse(it) }
fun findById(id: Long): User? {...}
val user: User? = userId?.let { findById(it) }
user?.let { println(it }
Optional#flatMap的な役割 (※)
Slide 97
Slide 97 text
let + Null安全
fun reverse(s: String): String {...}
val reverse: String? = str?.let { reverse(it) }
fun findById(id: Long): User? {...}
val user: User? = userId?.let { findById(it) }
user?.let { println(it }
Optional#ifPresent的な役割 (※)
Slide 98
Slide 98 text
letは便利だけど、複数あると...
foo?.let { foo ->
bar?.let { bar ->
baz?.let { baz ->
execute(foo, bar, baz)
}
}
}
Slide 99
Slide 99 text
素直にifでチェックしましょう
if (foo != null && bar != null && baz != null) {
execute(foo, bar, baz)
}
Slide 100
Slide 100 text
3. Kotinを使ってみて
Slide 101
Slide 101 text
Kotlinを実際に使ってみたよ
● 趣味: 某アプリ
○ Android
○ Dagger2, Realm, KotterKnife
● 業務: 医薬品情報アプリ
○ Android
○ Dagger2, Android Extensions
● 業務: 社内用 Web API
○ Spring Boot
○ JPA, Swagger