転載公開番号:Silent Wang Er
特にJava 8の優れた点について学ぶのに遅すぎることはありません。Optionalはその1つです。このクラスは、null参照の代わりにオプションの値を表すためのクラスレベルのソリューションを提供します。Javaプログラマーとして、私はNullPointerException(NPE)にうんざりしていますが、古くからの友人のように慣れていますが、それも必要であることを知っています。プログラムはオブジェクトを使用していますが、このオブジェクトの値がnullであることがわかりました、それでJava仮想マシンは怒りでそれを捨て、それをスケープゴートとして使用しました。
もちろん、私たちのプログラマーが責任を負い、座って監視することはありません。そのため、多くのnull値チェックがあります。そのような検査は時々不要ですが、私たちは日常的な事柄に慣れています。最後に、Java 8はもう我慢できないので、オプションが導入されました。これにより、私たちが作成するコードはもはやそれほど厳格ではなくなります。
01、オプションなしの問題は何ですか
実際のアプリケーションシナリオをシミュレーションしてみましょう。Xiao Wangは初日に出勤し、Lao Maにタスクの手配を依頼し、メンバーIDに基づいてデータベースからメンバーの名前を引き出し、コンソールに名前を出力するように依頼しました。新しくはありますが、このタスクではXiao Wangを失敗させることはできないため、このコードを10分で書きました。
public class WithoutOptionalDemo {
class Member {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
Member mem = getMemberByIdFromDB();
if (mem != null) {
System.out.println(mem.getName());
}
}
public static Member getMemberByIdFromDB() {
// 当前 ID 的会员不存在
return null;
}
}
現在のIDのメンバーが存在しないため、メンバーを取得しないとgetMemberByIdFromDB()メソッドはnullを返します。つまり、メンバーの名前を出力するときは、最初にmemを空であると判断する必要があります。それ以外の場合は、NPE例外がスローされます。 、それを信じませんか?Xiaowangがif(mem!= Null)を削除しようとすると、コンソールはすぐにエラースタックを出力して表示します。
Exception in thread "main" java.lang.NullPointerException
at com.cmower.dzone.optional.WithoutOptionalDemo.main(WithoutOptionalDemo.java:24)
02.オプションはこの問題をどのように解決しますか
Xiao Wangがコードを提出した後、喜んでLao Maに新しい仕事を依頼しました。Xiao Wangは心を開いて、Lao Maに自分のコードを確認するように依頼したので、Lao Wangは不要なnull値チェックを回避するためにOptionalを試す必要があると彼に言いました。次に、Xiao WangがOptionalを使用して上記の問題をどのように解決するかを見てみましょう。
public class OptionalDemo {
public static void main(String[] args) {
Optional<Member> optional = getMemberByIdFromDB();
optional.ifPresent(mem -> {
System.out.println("会员姓名是:" + mem.getName());
});
}
public static Optional<Member> getMemberByIdFromDB() {
boolean hasName = true;
if (hasName) {
return Optional.of(new Member("沉默王二"));
}
return Optional.empty();
}
}
class Member {
private String name;
public String getName() {
return name;
}
// getter / setter
}
getMemberByIdFromDB()メソッドは、結果としてOptional <Member>を返します。つまり、Memberが存在する場合と存在しない場合があります。現時点では、OptionalのifPresent()メソッドでLambda式を使用して、結果を直接出力できます。
OptionalがNPEの問題を解決できるのは、空であると判断する必要がないことを明確に示しているからです。それは交差点の道路標識のようなもので、どこに行くべきかはっきりと教えてくれます。
03、オプションオブジェクトを作成する
1)静的メソッドempty()を使用して空のOptionalオブジェクトを作成できます
Optional<String> empty = Optional.empty();
System.out.println(empty); // 输出:Optional.empty
2)()の静的メソッドを使用して、空でないOptionalオブジェクトを作成できます
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt); // 输出:Optional[沉默王二]
もちろん、of()メソッドに渡されるパラメーターはnull以外にする必要があります。つまり、nullにすることはできません。それ以外の場合は、NullPointerExceptionがスローされます。
String name = null;
Optional<String> optnull = Optional.of(name);
3)staticメソッドofNullable()を使用して、空でnullにできないOptionalオブジェクトを作成できます
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 输出:Optional.empty
ofNullable()メソッドは内部に三項式を持っています。パラメータがnullの場合、プライベート定数EMPTYを返します。それ以外の場合、新しいキーワードを使用して新しいオプションオブジェクトが作成されます。これ以上NPE例外はスローされません。
04.値が存在するかどうかを確認します
isPresent()メソッドを使用して、Optionalオブジェクトが存在するかどうかを判断できます。存在する場合、メソッドはtrueを返し、それ以外の場合は、obj!= Nullではなくfalseを返します。
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 输出:true
Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false
Java 11以降では、メソッドisEmpty()によってisPresent()の反対の結果を判断することもできます。
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 输出:false
Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:true
05、空でない式
Optionalクラスには非常に最新のメソッドifPresent()があり、関数型プログラミングを使用してコードを実行できるため、空でない式と呼んでいます。そのようなメソッドがない場合、通常、isPresent()メソッドでOptionalオブジェクトを判断し、対応するコードを実行する必要があります。
Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {
System.out.println(optOrNull.get().length());
}
ifPresent()を使用すると、状況がまったく異なり、Lambda式をメソッドに直接渡すことができ、コードがより簡潔で直感的になります。
Optional<String> opt = Optional.of("沉默王二");
opt.ifPresent(str -> System.out.println(str.length()));
Java 9以降、メソッドifPresentOrElse(action、emptyAction)を介して2つの結果を実行し、空でない場合にアクションを実行し、空の場合にemptyActionを実行することもできます。
Optional<String> opt = Optional.of("沉默王二");
opt.ifPresentOrElse(str -> System.out.println(str.length()), () -> System.out.println("为空"));
06.デフォルト値を設定(取得)する
場合によっては、Optionalオブジェクトを作成(取得)するときにデフォルト値が必要になることがあり、orElse()およびorElseGet()メソッドが役立ちます。
orElse()メソッドは、Optionalオブジェクトにラップされた値を返すために使用されます。値がnullでない場合は戻り、それ以外の場合はデフォルト値を返します。このメソッドのパラメーターの型は、値の型と一致しています。
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("沉默王二");
System.out.println(name); // 输出:沉默王二
orElseGet()メソッドはorElse()メソッドに似ていますが、パラメータタイプが異なります。Optionalオブジェクトの値がnullの場合、パラメーターの関数が実行されます。
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
System.out.println(name); // 输出:沉默王二
出力結果とコードの形式から判断すると、これらの2つのメソッドは非常によく似ています。これは必然的に疑問を引き起こします。Javaクラスライブラリの設計者がこれを行う必要があるのでしょうか。
非常に伝統的な方法である、デフォルト値を取得するためのそのような方法があるとしましょう。
public static String getDefaultValue() {
System.out.println("getDefaultValue");
return "沉默王二";
}
次に、orElse()メソッドとorElseGet()メソッドをそれぞれ介してgetDefaultValue()メソッドを呼び出し、デフォルト値を返します。
public static void main(String[] args) {
String name = null;
System.out.println("orElse");
String name2 = Optional.ofNullable(name).orElse(getDefaultValue());
System.out.println("orElseGet");
String name3 = Optional.ofNullable(name).orElseGet(OrElseOptionalDemo::getDefaultValue);
}
注:クラス名::メソッド名は、Java 8で導入された構文です。メソッド名の後に()はなく、メソッドが必ずしも呼び出されないことを示しています。
出力は次のとおりです。
orElse
getDefaultValue
orElseGet
getDefaultValue
出力は似ていますが、それほど大きな違いはありません。これは、Optionalオブジェクトの値がnullの場合です。Optionalオブジェクトの値がnullでない場合はどうなりますか?
public static void main(String[] args) {
String name = "沉默王三";
System.out.println("orElse");
String name2 = Optional.ofNullable(name).orElse(getDefaultValue());
System.out.println("orElseGet");
String name3 = Optional.ofNullable(name).orElseGet(OrElseOptionalDemo::getDefaultValue);
}
出力は次のとおりです。
orElse
getDefaultValue
orElseGet
こんにちは、orElseGet()はgetDefaultValue()を呼び出しませんでした。どちらの方法がパフォーマンスが優れていますか?
07、値を取得
直観的には、意味的には、get()メソッドがOptionalオブジェクトの値を取得するための最も信頼できるメソッドですが、残念ながら、このメソッドには欠陥があります。Optionalオブジェクトの値がnullの場合、メソッドはNoSuchElementException例外をスローするためです。これは、Optionalクラスを使用するという当初の意図に完全に反しています。
public class GetOptionalDemo {
public static void main(String[] args) {
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull.get());
}
}
このプログラムは実行時に例外をスローします:
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.get(Optional.java:141)
at com.cmower.dzone.optional.GetOptionalDemo.main(GetOptionalDemo.java:9)
スローされる例外はNPEではなくNoSuchElementExceptionですが、私たちの意見では、それは明らかに「5ステップと100ステップ」です。orElseGet()メソッドでOptionalオブジェクトの値を取得することをお勧めします。
08、フィルター値
Xiao WangはOptionalクラスを介して以前のコードをアップグレードし、幸いにも古い馬を見つけてタスクを完了しました。Lao Maは、この人は優秀で柔軟性があり、活発で、育成する価値があると感じたため、Xiao Wangに新しいタスクを与えました。ユーザーが登録するときにパスワードの長さをチェックすることです。
Xiao Wangがタスクを取得した後、LeはOptionalクラスのfilter()メソッドを習得するだけで花を開きました。
public class FilterOptionalDemo {
public static void main(String[] args) {
String password = "12345";
Optional<String> opt = Optional.ofNullable(password);
System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent());
}
}
filter()メソッドのパラメータータイプはPredicate(Java 8で追加された機能インターフェイス)です。つまり、ラムダ式を条件としてメソッドに渡すことができ、式の結果がfalseの場合はEMPTYオプションオブジェクト。それ以外の場合は、フィルタリングされたオプションオブジェクトを返します。
上記の例では、パスワードの長さが5であるため、プログラム出力の結果はfalseです。パスワードの長さが6〜10桁である必要があると仮定すると、別の条件を追加できます。Xiao Wangが難易度を上げた後のコードを見てください。
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result);
今回はパスワードが6桁から10桁の7桁になっているため、プログラム出力の結果はtrueです。Xiao Wangがif-elseを使用してこのタスクを実行した場合のコードの長さを想像してみてください。
09、コンバージョン値
パスワードの長さをチェックした後、Xiao Wangはそれでも十分ではないと感じており、パスワードの強さもチェックする必要があると感じています(たとえば、パスワードを「パスワード」にすることはできません)。そこで、彼はmap()メソッドを研究し始めました。これは、特定のルールに従って元のオプションオブジェクトを新しいオプションオブジェクトに変換でき、元のオプションオブジェクトは変更されません。
まず、Xiao Wangが作成した簡単な例を見てみましょう。
public class OptionalMapDemo {
public static void main(String[] args) {
String name = "沉默王二";
Optional<String> nameOptional = Optional.of(name);
Optional<Integer> intOpt = nameOptional
.map(String::length);
System.out.println( intOpt.orElse(0));
}
}
上記の例では、パラメーターString :: map()メソッドの長さは、元の文字列型Optionalの文字列長に従って、Integer型の新しいOptionalオブジェクトが生成されることを意味します。
map()メソッドの基本的な使用法を理解した後、Xiao Wangはmap()メソッドをfilter()メソッドと組み合わせることにしました。前者はパスワードを小文字に変換するために使用され、後者は長さとそれが「パスワード」であるかどうかを決定するために使用されます。
public class OptionalMapFilterDemo {
public static void main(String[] args) {
String password = "password";
Optional<String> opt = Optional.ofNullable(password);
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
Predicate<String> eq = pwd -> pwd.equals("password");
boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
System.out.println(result);
}
}