- 投稿日:2020-03-27T21:58:22+09:00
iOS13でUIStoryboardから生成するUIViewControllerを独自initでビルドする拡張メソッドを作る
はじめに
取り急ぎ考えてみたので、手元で動かしてないです..!(Xcode11.4でコンパイルは通ります。)
Declare IBInjectable
@available(iOS 13, *) protocol IBInjectable { associatedtype Input var input: Input { get set } init?(coder: NSCoder, input: Input) } extension IBInjectable where Self: UIViewController { var className: String { String(describing: self) } static func build(input: Input) -> Self? { return UIStoryboard(name: Self.className, bundle: nil).instantiateInitialViewController { coder -> Self? in Self.init(coder: coder, input: input) } } init?(coder: NSCoder, input: Input) { self.init(coder: coder) self.input = input } }Implement IBInjectable
class SampleViewController: UIViewController, IBInjectable { typealias Input = String var input: String! } guard let sampleViewController: SampleViewController = SampleViewController.build(input: "Sample") else { fatalError() } sampleViewController.input // print: "Sample"まとめ
var input: Input { get set }
にすると、コンパイルが通らなく渋々IUOを使用しているのが改善の余地ありそうです。?
- 投稿日:2020-03-27T18:19:57+09:00
AWS AppSync用にaws-appsync-codegenでiOS(Swift)のソースコードを生成する
AWS AppSyncでiOS(Swift)用のコードを生成する時に勘違いしていたことがあったので、それについて公式のサンプルから理解できるように細かく書いておきます。私自身がAppSync歴1日なので正確に理解できているかもわかりませんので、その点についてはお手数おかけいたしますがコメントなどでご指摘よろしくお願いします。
また、現状(2020年)ではAppSyncの公式ドキュメントはAmplifyを使うことが前提とされており、Amplifyを使わない場合はやり方を探すのはかなり難しい。難しいので書き残しておきますというモチベーションです。
サンプルはAWS AppSync公式で説明されているサンプルスキーマ「イベントアプリ」からです。
最初に結論
おそらくこのページを見ている方が最も知りたいのはコード生成のコメント
aws-appsync-codegen
についての使い方でしょうから最初に書いておきます。# aws-appsync-codegen generate tmp/*.graphql --schema tmp/schema.json --target swift --output tmp/GraphQLAPI.swift
- tmp/*.graphql
- GraphQL リクエスト用のクエリを指定
*
にしているのはリクエストが複数ある場合にそれをtmpディレクトリに置いているためです- 後述の「クエリ画面」からコピペしてファイルにします
- tmp/schema.json
- GraphQLのための仕様
- 後述の「スキーマ画面」から「スキーマをエクスポート」でschema.jsonを選びます
- 注意: jsonを選びましょう。schema.graphqlではありません
--target
はswiftを指定
- 省略できますがこの説明のため丁寧に書いておきます
画面での説明
クエリ画面
スキーマ画面
aws-appsync-codegenの導入
npmをmacにDockerで入れることについて書いておきます。
iOSアプリ開発者的には、仕事でnpmの情報を追っているわけではなくバージョンの違いによる動作の違いを把握していないためシステムにnpm自身を入れたくないですし、なにか環境によるエラーが出た場合に面倒というのが強いです。
おそらくnpmをバージョン別に入れてそれを切り替えて使う方法(nodeenv?nodebrew?)もあるんでしょうが、そこはnpmをそこまで使うわけではないので考えたくないです。
nodeのimageをダウンロード
$ docker pull nodedocker runしてコンソールでnpmを利用する
nodeのコンテナを起動し、コンソールに入ります
$ docker run -v (pwd)/graphql:/tmp -it --rm node /bin/bash
-it
- インタラクティブにターミナルを起動
-v
- ローカルとコンテナ内のボリュームを共有し、GraphQL リクエスト用のクエリとschema.jsonをコンテナ内でも使えるようにします
- (pwd)
- fishなので(pwd)
- bashだと$(pwd)かも
コンテナ内でnpmを使いaws-appsync-codegenをインストール
コンテナ内なのでグローバルにインストールしても良いかな、という感じです
# npm install -g aws-appsync-codegenaws-appsync-codegenでコード生成する
# aws-appsync-codegen generate tmp/queries.graphql --schema tmp/schema.json --target swift --output tmp/GraphQLAPI.swift
- コード生成した結果も共有のボリュームに出力しています
- 投稿日:2020-03-27T09:42:32+09:00
Firebaseのルール設定
Firebase firestoreでのルール設定について[備忘録]
個人開発アプリにてFirebaseを利用しているのですが、認証機能をつけずに運用していたところ、急にデータを取れなくなったのでメモ。
新規firebaseProject作成後、ルール設定を(誰でも閲覧可能)にしていたところ、1,2ヶ月が過ぎた段階で以下の警告がでて動かなくなった。
ちなみにその時のセキュリティソースが以下rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // This rule allows anyone on the internet to view, edit, and delete // all data in your Firestore database. It is useful for getting // started, but it is configured to expire after 30 days because it // leaves your app open to attackers. At that time, all client // requests to your Firestore database will be denied. // // Make sure to write security rules for your app before that time, or else // your app will lose access to your Firestore database match /{document=**} { allow read, write: if request.time < timestamp.date(2020, 3, 21); } } }これをいかに変更して、認証機能をつけることで解決。ちなみに匿名の認証機能を採用しました。(規約変更しないとなあ、、、)
service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth.uid != null; } } }より良い解決方法ありましたら、ご教授いただけると幸いです。m(_ _)m
- 投稿日:2020-03-27T02:05:30+09:00
Java 8のOptionalをSwiftのOptionalと比較する
概要
本記事ではOptionalのApple公式ドキュメントの概要に掲載されている項目を元にして、Swiftの
Optional
型とJava 8のOptional
型を比較します。
なお本記事に掲載してあるスニペットは多少簡略化されているので、そのままコピペしても動かないものがあります。ご了承ください。Nil結合演算子(Nil-Coalescing Operator)
Swift
中身があるならそれを、なければ指定したデフォルト値を返すように
Optinal
をアンラップします。??
という演算子を使います。複数のOptional
型をオペランドとしてつなぐこともできます。Swiftlet wrappedValue: String? = nil let defaultValue = "default" let value = wrappedValue ?? defaultValue print(value) // "default"Swiftlet wrappedValue: String? = nil let defaultValue = "default" let otherWrappedValue: String? = nil // 右オペランドがOptionalである限り??でつなぐことができる let value = wrappedValue ?? otherWrappedValue ?? defaultValue print(value) // "default"Java 8
Java 8では演算子ではなく
orElse
、orElseGet
というメソッドです。orElse
ではデフォルト値そのものを、orElseGet
ではデフォルト値を提供するSupplier<? extends T>
型のオブジェクトを渡します。しかしいずれのメソッドもラップしている型を返すので、SwiftのようにorElse
で複数のOptional
型をつなぐことはできません。JavaOptional<String> wrappedValue = Optional.empty(); String defaultValue = "default"; String value = wrappedValue.orElse(defaultValue); System.out.println(value); // "default" String otherValue = wrappedValue.orElseGet( () -> defaultValue ); System.out.println(otherValue); // "default"JavaOptional<String> otherWrappedValue = Optional.of("other"); // これはできない // String unwrappedValue = wrappedValue.orElse(otherWrappedValue).orElse(defaultValue);短絡評価
Swift
SwiftのNil結合演算子は短絡評価します。したがって左オペランドが
nil
でない限り右オペランドが評価されることはありません。Swiftfunc hoge() -> String { print("called") return "hoge" } let value = nil ?? hoge() print(value) // "called" // "hoge" // 短絡評価なのでhoge()は呼ばれない let value = "non nil" ?? hoge() print(value) // "non nil"Java 8
Java 8の
orElseGet
も短絡評価ですが、orElse
はしません。デフォルト値そのものを渡しているので当然と言えば当然ですね。Javapublic String hoge() { System.out.println("called"); return "hoge"; } String value = Optional.<String>empty().orElseGet(this::hoge); System.out.println(value); // "called" // "hoge" // 短絡評価なのでhoge()は呼ばれない String value = Optional.of("non null").orElseGet(this::hoge); System.out.println(value); // "non null"Java// orElseは短絡評価しない String value = Optional.of("non null").orElse(hoge()); System.out.println(value); // "called" // "non null"使い分けについてですが、デフォルト値生成にコストがかかる場合などには短絡評価する
orElseGet
を使うと良いでしょう。Javapublic String fetchValue() { // 時間のかかる処理 return result; } // △ - 必ずfetchValue()が呼ばれるので少なくともパフォーマンスに影響 String value = wrappedValue.orElse(fetchValue()); // ◯ - 短絡評価なので中身があれば呼ばれない String value = wrappedValue.orElseGet(this::fetchValue);Java// △ - 中身があると無駄なオブジェクト生成となる Person person = wrappedPerson.orElse(new Person()); // ◯ - 短絡評価なので中身があればオブジェクトは生成されない Person person = wrappedPerson.orElseGet(Person::new);強制アンラップ(Unconditional Unwrapping)
Swift
中身があるかどうかにかかわらず値をアンラップします。演算子
!
を使います。もしnil
であった場合は実行時エラーを引き起こします。Swiftlet number = Int("42")! print(number) // 42Java 8
Java 8では
get
メソッドを使います。もしnull
であった場合は非検査例外であるNoSuchElementException
が投げられます。
※ なお、intFromString
は説明の便宜上Optional<Integer>
型を返していますが、通常整数を包む場合はOptionalInt
型を使う方が良いでしょう。Javapublic Optional<Integer> intFromString(String string) { try { return Optional.of(Integer.parseInt(string)); } catch (NumberFormatException ignored) { return Optional.empty(); } } int number = intFromString("42").get(); System.out.println(number); // 42変換
Swift
値を変換します。
map
というメソッドを使います。中身があれば与えられた(Wrapped) -> U
型のクロージャに従って値を変換し、nil
であればnil
のまま流します。したがってOptional
型を返します。Swiftlet intString: String? = "42" let percentage: String? = intString.map { "\($0)%" } print(String(describing: percentage)) // Optional("42%")クロージャ内で
Optional
型を返したいときは、(Wrapped) -> Optional<U>
型のクロージャを引数にとるflatMap
メソッドを使います。Swiftlet intString: String? = "42" // Int(String)はInt?を返す let integer: Int? = intString.flatMap { Int($0) } print(String(describing: integer)) // Optional(42)Java 8
Java 8でも同様に
map
、flatMap
というメソッドを使います。使い分けも同様です。JavaOptional<String> intString = Optional.of("42"); Optional<String> percentage = intString.map( str -> str + "%" ); System.out.println(percentage); // Optional[42%]JavaOptional<String> intString = Optional.of("42"); Optional<Integer> integer = intString.flatMap(this::intFromString); System.out.println(integer); // Optional[42]オプショナルバインディング(Optional Binding)
Swift
安全なアンラップ手段として馴染み深いことと思います。
if
やguard
といったキーワードと併せることで、中身がある場合とない場合とで処理を分岐することができます。Swiftlet wrappedValue: String? = "value" if let value: String = wrappedValue { print(value) } else { print("no value") } // "value"Swiftlet wrappedValue: String? = "value" guard let value: String = wrappedValue else { print("no value") return } print(value) // "value"Java 8
Java 8では
ifPresent
というメソッドを介して安全にアンラップすることができます。Consumer<? super T>
型のオブジェクトを引数にとり、その中に中身があった場合の処理を記述します。JavaOptional<String> wrappedValue = Optional.of("value"); wrappedValue.ifPresent( str -> { System.out.println(str); }); // "value"しかしながら中身がない場合の処理を記述できるメソッドはありません。したがって両方の処理を記述したい場合は
isPresent
を使って中身の有無を確認する方法をとる必要があります。ただし、中身がある場合明示的に強制アンラップget
を記述しなければいけないためSwiftと比較すると冗長です。JavaOptional<String> wrappedValue = Optional.of("value"); if (wrappedValue.isPresent()) { // 冗長だが明示的に強制アンラップする必要がある String value = wrappedValue.get(); System.out.println(value); } else { System.out.println("no value"); } // "value"JavaOptional<String> wrappedValue = Optional.of("value"); // カード節的に記述する if (!wrappedValue.isPresent()) { System.out.println("no value"); return; } // 冗長だが明示的に強制アンラップする必要がある String value = wrappedValue.get(); System.out.println(value); // "value"JavaOptional<String> wrappedValue = Optional.of("value"); wrappedValue.ifPresent( str -> { System.out.println(str); })// .orElse( () -> { // これはできない // System.out.println("no value"); // }) ;また
ifPresent
に渡す処理のスコープはラムダ式内であるため、その処理内で呼び出し元のメソッドのリターンはできません。したがって早期リターンなどを行いたい場合はisPresent
を使った方法をとる他ありません。Javapublic boolean foo(Optional<String> wrappedValue) { // 値があるなら早期リターンする if (wrappedValue.isPresent) { String str = wrappedValue.get(); // 何かする return true; } // 値がない場合の処理 return result; }Javapublic boolean foo(Optional<String> wrappedValue) { // 値があるなら早期リターンする wrappedValue.ifPresent( str -> { // 何かする // return true; // スコープがこのラムダ式内にあるため、これはできない }); // 値がない場合の処理 return result; }オプショナルチェイニング(Optional Chaining)
Swift
中身があるときのみプロパティやメソッドへのアクセスを行います。後置演算子
?
を使います。Swiftlet wrappedValue: String? = "value" let uppercased: String? = wrappedValue?.uppercased() print(String(describing: uppercased)) // Optional("VALUE")Java 8
Java 8では先に紹介した
map
メソッドが使えるでしょう。メソッド参照を用いればある程度簡潔に書くことができます。JavaOptional<String> wrappedValue = Optional.of("value"); Optional<String> uppercased = wrappedValue.map(String::toUpperCase); System.out.println(uppercased); // Optional[VALUE]まとめ
本記事ではSwiftでの
Optional
型の扱いを元に、Java 8とOptional
型との比較を行いました。特に、オプショナルバインディングについては両者の違いがよくあらわれていました。言語レベルでNullセーフであるSwiftと比べてしまうと扱いづらさが目立ってしまうものの、ラムダ式やメソッド参照のおかげでかなり書きやすくなっているなと感じています。
もし何か間違いなどございましたらコメントにてご指摘ください。オマケ - Java 9のOptional
ここではJava 9で追加された
Optional
のメソッド、or
とifPresentOrElse
を紹介します。Java 8の話は出てきません。完全なオマケです。or
中身がない場合に与えられた
Supplier<? extends Optional<? extends T>>
型のオブジェクトを実行してOptional
型のデフォルト値を得ます。これはちょうどSwiftのNil結合演算子で右オペランドにOptional
型を持ってきた場合と対応しています。
また短絡評価をするため、中身がある限り与えられたSupplier
オブジェクトが実行されることはありません。Swiftlet wrappedValue: String? = nil let defaultValue = "default" let otherWrappedValue: String? = nil let value = wrappedValue ?? otherWrappedValue ?? defaultValue print(value) // "default"Java// available Java 9 or later Optional<String> wrappedValue = Optional.empty(); String defaultValue = "default"; Optional<String> otherWrappedValue = Optional.empty(); // Java 9以降ならデフォルト値にOptional型を指定できる String value = wrappedValue.or( () -> otherWrappedValue ).orElse(defaultValue); System.out.println(value); // "default"ifPresentOrElse
中身がある場合とない場合とで処理を分岐することができます。中身がある場合には
Consumer<? super T>
型のオブジェクトが、ない場合にはRunnable
型のオブジェクトが実行されます。null
だった場合の処理も記述できるので、Java 8と比べるとSwiftのオプショナルバイディングにかなり近づいています。Swiftlet wrappedValue: String? = "value" if let value = wrappedValue { print(value) } else { print("no value") } // "value"Java// available Java 9 or later Optional<String> wrappedValue = Optional.<String>empty(); wrappedValue.ifPresentOrElse( str -> { System.out.println(str); }, () -> { System.out.println("no value"); }); // "no value"
- 投稿日:2020-03-27T02:05:30+09:00
Java 8のOptionalをSwiftと比較する
概要
本記事ではOptionalのApple公式ドキュメントの概要に掲載されている項目を元にして、Swiftの
Optional
型とJava 8のOptional
型を比較します。若干今更感のある内容にはなりますが個人的な備忘録も兼ねて投稿に至りました。
記事の最後にはそれぞれの記法の比較表も掲載しています。あわせてご覧ください。
なお本記事に掲載してあるスニペットは多少簡略化されているので、そのままコピペしても動かないものがあります。ご了承ください。Nil結合演算子(Nil-Coalescing Operator)
Swift
中身があるならそれを、なければ指定したデフォルト値を返すように
Optinal
をアンラップします。??
という演算子を使います。複数のOptional
型をオペランドとしてつなぐこともできます。Swiftlet wrappedValue: String? = nil let defaultValue = "default" let value = wrappedValue ?? defaultValue print(value) // "default"Swiftlet wrappedValue: String? = nil let defaultValue = "default" let otherWrappedValue: String? = nil // 右オペランドがOptionalである限り??でつなぐことができる let value = wrappedValue ?? otherWrappedValue ?? defaultValue print(value) // "default"Java 8
Java 8では演算子ではなく
orElse
、orElseGet
というメソッドです。orElse
ではデフォルト値そのものを、orElseGet
ではデフォルト値を提供するSupplier<? extends T>
型のオブジェクトを渡します。しかしいずれのメソッドもラップしている型を返すので、SwiftのようにorElse
で複数のOptional
型をつなぐことはできません。JavaOptional<String> wrappedValue = Optional.empty(); String defaultValue = "default"; String value = wrappedValue.orElse(defaultValue); System.out.println(value); // "default" String otherValue = wrappedValue.orElseGet( () -> defaultValue ); System.out.println(otherValue); // "default"JavaOptional<String> otherWrappedValue = Optional.of("other"); // これはできない // String unwrappedValue = wrappedValue.orElse(otherWrappedValue).orElse(defaultValue);短絡評価
Swift
SwiftのNil結合演算子は短絡評価します。したがって左オペランドが
nil
でない限り右オペランドが評価されることはありません。Swiftfunc hoge() -> String { print("called") return "hoge" } let value = nil ?? hoge() print(value) // "called" // "hoge" // 短絡評価なのでhoge()は呼ばれない let value = "non nil" ?? hoge() print(value) // "non nil"Java 8
Java 8の
orElseGet
も短絡評価ですが、orElse
はしません。デフォルト値そのものを渡しているので当然と言えば当然ですね。Javapublic String hoge() { System.out.println("called"); return "hoge"; } String value = Optional.<String>empty().orElseGet(this::hoge); System.out.println(value); // "called" // "hoge" // 短絡評価なのでhoge()は呼ばれない String value = Optional.of("non null").orElseGet(this::hoge); System.out.println(value); // "non null"Java// orElseは短絡評価しない String value = Optional.of("non null").orElse(hoge()); System.out.println(value); // "called" // "non null"使い分けについてですが、デフォルト値生成にコストがかかる場合などには短絡評価する
orElseGet
を使うと良いでしょう。Javapublic String fetchValue() { // 時間のかかる処理 return result; } // △ - 必ずfetchValue()が呼ばれるので少なくともパフォーマンスに影響 String value = wrappedValue.orElse(fetchValue()); // ◯ - 短絡評価なので中身があれば呼ばれない String value = wrappedValue.orElseGet(this::fetchValue);Java// △ - 中身があると無駄なオブジェクト生成となる Person person = wrappedPerson.orElse(new Person()); // ◯ - 短絡評価なので中身があればオブジェクトは生成されない Person person = wrappedPerson.orElseGet(Person::new);強制アンラップ(Unconditional Unwrapping)
Swift
中身があるかどうかにかかわらず値をアンラップします。演算子
!
を使います。もしnil
であった場合は実行時エラーを引き起こします。Swiftlet number = Int("42")! print(number) // 42Java 8
Java 8では
get
メソッドを使います。もしnull
であった場合は非検査例外であるNoSuchElementException
が投げられます。
※ なお、intFromString
は説明の便宜上Optional<Integer>
型を返していますが、通常整数を包む場合はOptionalInt
型を使う方が良いでしょう。Javapublic Optional<Integer> intFromString(String string) { try { return Optional.of(Integer.parseInt(string)); } catch (NumberFormatException ignored) { return Optional.empty(); } } int number = intFromString("42").get(); System.out.println(number); // 42変換
Swift
値を変換します。
map
というメソッドを使います。中身があれば与えられた(Wrapped) -> U
型のクロージャに従って値を変換し、nil
であればnil
のまま流します。したがってOptional
型を返します。Swiftlet intString: String? = "42" let percentage: String? = intString.map { "\($0)%" } print(String(describing: percentage)) // Optional("42%")クロージャ内で
Optional
型を返したいときは、(Wrapped) -> Optional<U>
型のクロージャを引数にとるflatMap
メソッドを使います。Swiftlet intString: String? = "42" // Int(String)はInt?を返す let integer: Int? = intString.flatMap { Int($0) } print(String(describing: integer)) // Optional(42)Java 8
Java 8でも同様に
map
、flatMap
というメソッドを使います。使い分けも同様です。JavaOptional<String> intString = Optional.of("42"); Optional<String> percentage = intString.map( str -> str + "%" ); System.out.println(percentage); // Optional[42%]JavaOptional<String> intString = Optional.of("42"); Optional<Integer> integer = intString.flatMap(this::intFromString); System.out.println(integer); // Optional[42]オプショナルバインディング(Optional Binding)
Swift
安全なアンラップ手段として馴染み深いことと思います。
if
やguard
といったキーワードと併せることで、中身がある場合とない場合とで処理を分岐することができます。Swiftlet wrappedValue: String? = "value" if let value: String = wrappedValue { print(value) } else { print("no value") } // "value"Swiftlet wrappedValue: String? = "value" guard let value: String = wrappedValue else { print("no value") return } print(value) // "value"Java 8
Java 8では
ifPresent
というメソッドを介して安全にアンラップすることができます。Consumer<? super T>
型のオブジェクトを引数にとり、その中に中身があった場合の処理を記述します。JavaOptional<String> wrappedValue = Optional.of("value"); wrappedValue.ifPresent( str -> { System.out.println(str); }); // "value"しかしながら中身がない場合の処理を記述できるメソッドはありません。したがって両方の処理を記述したい場合は
isPresent
を使って中身の有無を確認する方法をとる必要があります。ただし、中身がある場合明示的に強制アンラップget
を記述しなければいけないためSwiftと比較すると冗長です。JavaOptional<String> wrappedValue = Optional.of("value"); if (wrappedValue.isPresent()) { // 冗長だが明示的に強制アンラップする必要がある String value = wrappedValue.get(); System.out.println(value); } else { System.out.println("no value"); } // "value"JavaOptional<String> wrappedValue = Optional.of("value"); // カード節的に記述する if (!wrappedValue.isPresent()) { System.out.println("no value"); return; } // 冗長だが明示的に強制アンラップする必要がある String value = wrappedValue.get(); System.out.println(value); // "value"JavaOptional<String> wrappedValue = Optional.of("value"); wrappedValue.ifPresent( str -> { System.out.println(str); })// .orElse( () -> { // これはできない // System.out.println("no value"); // }) ;また
ifPresent
に渡す処理のスコープはラムダ式内であるため、その処理内で呼び出し元のメソッドのリターンはできません。したがって早期リターンなどを行いたい場合はisPresent
を使った方法をとる他ありません。Javapublic boolean foo(Optional<String> wrappedValue) { // 値があるなら早期リターンする if (wrappedValue.isPresent) { String str = wrappedValue.get(); // 何かする return true; } // 値がない場合の処理 return result; }Javapublic boolean foo(Optional<String> wrappedValue) { // 値があるなら早期リターンする wrappedValue.ifPresent( str -> { // 何かする // return true; // スコープがこのラムダ式内にあるため、これはできない }); // 値がない場合の処理 return result; }オプショナルチェイニング(Optional Chaining)
Swift
中身があるときのみプロパティやメソッドへのアクセスを行います。後置演算子
?
を使います。Swiftlet wrappedValue: String? = "value" let uppercased: String? = wrappedValue?.uppercased() print(String(describing: uppercased)) // Optional("VALUE")Swiftclass Hoge { func hoge() { print("hoge") } } let wrappedValue: Hoge? = Hoge() wrappedValue?.hoge() // "hoge"Java 8
Java 8では先に紹介した
map
メソッドが使えます。対象がメソッドであればメソッド参照を用いて簡潔に書くことができます。ただし、対象がOptional
型を返す場合にはflatMap
を使う必要があります。JavaOptional<String> wrappedValue = Optional.of("value"); Optional<String> uppercased = wrappedValue.map(String::toUpperCase); System.out.println(uppercased); // Optional[VALUE]Javaclass User { final String id; Optional<String> mail = Optional.empty(); User(String id) { this.id = id; } } Optional<User> wrappedUser = Optional.of(new User("user1")); Optional<String> mail = wrappedUser.flatMap( user -> user.mail ); System.out.println(mail); // Optional.empty使い分けを含め変換のときと全く同じ記法ですね。Java 8ではラップしている値のフィールドやメソッドにアクセスするためだけの特別な記法やメソッドが用意されているわけではありません。
値を返さないメソッドへのアクセスは先に紹介したifPresent
を使います。Javaclass Hoge { void hoge() { System.out.println("hoge"); } } Optional<Hoge> wrappedValue = Optional.of(new Hoge()); wrappedValue.ifPresent(Hoge::hoge); // "hoge"まとめ
本記事ではSwiftでの
Optional
型の扱いを元に、Java 8とOptional
型の比較を行いました。以下にそれぞれの記法の比較表を掲載します。特にオプショナルバインディングとオプショナルチェイニングについては両者の違いがよくあらわれています。Java 8のオプショナルバインディングでは分岐と同時にアンラップできない分Swiftより少し冗長です。またJava 8にはSwiftの?
演算子のようなラップしている値にアクセスするための専用手段は用意されていません。
Swift Java Nil結合演算子 ??
orElse
※ 非短絡評価
orElseGet
強制アンラップ !
get
変換 map
flatMap
map
flatMap
オプショナルバインディング if let
guard let
ifPresent
もしくはisPresent
で分岐した後にget
で強制アンラップ
オプショナルチェイニング ?
Optional
型を返すならflatMap
それ以外を返すならmap
値を返さないならifPresent
もし何か間違いなどございましたらコメントにてご指摘ください。
オマケ - Java 9のOptional
ここではJava 9で追加された
Optional
のメソッド、or
とifPresentOrElse
を紹介します。Java 8の話は出てきません。完全なオマケです。or
中身がない場合に与えられた
Supplier<? extends Optional<? extends T>>
型のオブジェクトを実行してOptional
型のデフォルト値を得ます。これはちょうどSwiftのNil結合演算子で右オペランドにOptional
型を持ってきた場合と対応しています。
また短絡評価をするため、中身がある限り与えられたSupplier
オブジェクトが実行されることはありません。Swiftlet wrappedValue: String? = nil let defaultValue = "default" let otherWrappedValue: String? = nil let value = wrappedValue ?? otherWrappedValue ?? defaultValue print(value) // "default"Java// available Java 9 or later Optional<String> wrappedValue = Optional.empty(); String defaultValue = "default"; Optional<String> otherWrappedValue = Optional.empty(); // Java 9以降ならデフォルト値にOptional型を指定できる String value = wrappedValue.or( () -> otherWrappedValue ).orElse(defaultValue); System.out.println(value); // "default"ifPresentOrElse
中身がある場合とない場合とで処理を分岐することができます。中身がある場合には
Consumer<? super T>
型のオブジェクトが、ない場合にはRunnable
型のオブジェクトが実行されます。null
だった場合の処理も記述できるので、Java 8と比べるとSwiftのオプショナルバイディングにかなり近づいています。Swiftlet wrappedValue: String? = "value" if let value = wrappedValue { print(value) } else { print("no value") } // "value"Java// available Java 9 or later Optional<String> wrappedValue = Optional.<String>empty(); wrappedValue.ifPresentOrElse( str -> { System.out.println(str); }, () -> { System.out.println("no value"); }); // "no value"