あちこちで Option を使用するのは少し面倒に感じます。私は何か間違ったことをしているでしょうか?
-
21-09-2019 - |
質問
について読んだ記事の結果、 Option
NullPointerException を回避するのに役立つクラスを、私はあちこちで使い始めました。次のようなことを想像してください。
var file:Option[File] = None
そして後でそれを使用するとき:
val actualFile = file.getOrElse(new File("nonexisting"))
if(actualFile.getName.equals("nonexisting")) { // instead of null checking
}
else { // value of file was good
}
このようなことをすることは、私にとってそれほど「正しい」とは思えません。私もそれに気づきました .get
は非推奨になりました。。皆さんも Option でこのようなことをやっているのでしょうか、それとも私が間違った方向に進んでいるでしょうか?
解決
一般的に返品は得策ではありません Option
そして使用します getOrElse
「見つからない」ことを意味するセンチネル値を生成します。それが何です Option
以下のために設計されています:値が見つからないことを示します!
Option
のような関数型プログラミング構造と組み合わせて使用すると、その威力を発揮します。 map
そして foreach
. 。これは、複数のオプションを扱う場合に最も効果的です。たとえば、文字列を受け取り、ファイルが存在し、ディレクトリではなくファイルである場合に限り、ファイルを返すメソッドを作成するとします。
import java.io._;
def niceFile1(s: String): File = {
val f = new File(s);
if (f.exists && !f.isDirectory) f else null
}
def niceFile2(s: String): Option[File] = {
val f = new File(s);
if (f.exists && !f.isDirectory) Some(f) else None
}
これまでのところ、使用して null
少なくとも、これによって得られる可能性があることを忘れるまでは、その方が簡単です。 null
そしてNPEを取得します。とにかく、今度は使ってみましょう。
def niceFopen1(s: String) = {
val f = niceFile1(s);
if (f!=null) new FileInputStream(f) else null;
}
def niceFopen2(s: String) = niceFile2(s).map(f => new FileInputStream(f))
何が起こったのか見てみましょう!前者の場合は、手動でロジック テストを実行し、一時変数を作成する必要がありました。うーん!2 番目のケースでは、 map
私たちのために汚い仕事をすべてやってくれました。None は None にマップされ、 Some(file)
にマッピングされました Some(fileinputstream)
. 。簡単!
しかし、それはまだ良くなります。おそらく、一連のファイルのサイズを調べたいと思うでしょう。
def totalSize2(ss: Seq[String]) = {
(0L /: ss.flatMap(niceFile2)){(sum,f) => sum+f.length}
}
待って、ここで何が起こっているのか、すべてはどうなるのでしょうか? None
?注意して何とか対処する必要はないでしょうか?さて、そこです flatMap
入って来る:すべての回答を 1 つのリストに結合します。 None
は長さ 0 の答えなので無視されます。 Some(f)
答えは1つです--f
--それでリストに入れます。次に、折り畳んですべての長さを合計します。これで、リスト内のすべての要素が有効になります。かなりいい!
他のヒント
これは、への Option
のの値が解決していないが、の適用のロジックにそれが含まれているものは何でもすることをお勧めします
findFile.foreach(process(_))
は1が見つかった場合は基本的にこれはFile
を処理し、それ以外の場合は何もしません(とfor
がfor
への呼び出しにコンパイルするので、トーマスの最初のforeach
理解と等価です)。それは、より簡潔なバージョンです。
findFile match {
case Some(f) => process(f)
case None =>
}
多くはである何、これについての素晴らしいところは何か、あなたはのチェーンの操作をできるということです
(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)
あなたはパターンマッチングを使用するほとんどの場合、
file match {
case Some(f) => { .. } //file is there
case _ => { .. } //file is not there
}
それがある場合は、ファイルにのみ興味があるなら、あなたが表現のために使用することができます。
for(f <- file) { //file is there
}
コンテナに複数のレベルで動作するように表現のためのあなたがすることができ、チェーン
for{
option <- List(Some(1), None, Some(2))
f <- option
} yield f
res0: List[Int] = List(1, 2)
別の方法としては、isDefinedを使用して取得することができます:
if(option.isDefined) {
val x = option.get;
} else {
}
GETはスカラ2.8.0.Beta-1で非推奨されていません。
ここでの代替です:
var file:Option[File] = None
// ...
file map (new File(_)) foreach { fh =>
// ...
}
しかし、あなたは、ファイルが存在し、他の何かそうでない場合は、match
文がより適切である場合には何かをする必要がある場合:
var file:Option[File] = None
// ...
file map (new File(_)) match {
case Some(fh) =>
// ...
case None =>
// ...
}
これはかなりif
文と同じですが、私はそれは私も値を抽出したいOption
のようなもの、より良い自然の連鎖だね。
は、ほとんどすべての人が言っている修正再表示が、私はあなたがこのようなNULL可能VARを持っているときに発生します、少なくとも4つの異なる状況があると思います。
この 1.ファイルがnullであってはなりません。
load(file.get())
あなたが実際にファイルを使用しようとすると例外がスローされます。イェーイ、速い失敗!
この
2.ファイルがnullであるが、この場合には、我々は賢明なデフォルトを持っている:
load(file.getOrElse(new File("/defaultFile")))
ファイルが存在するかどうかに基づいて、論理の3つの支店ます:
file match {
case Some(f) => { .. } //file is there
case _ => { .. } //file is not there
}
4.無OPファイルがnullの場合、別名黙って失敗します。私はこの1つは実際の生活の中で私にとって非常に好ましいことが多いとは思いません。
for (f <- file) {
//do some stuff
}
あるいは、多分より明確に?
if (f.isDefined) {
//do some stuff
}