〇前置き
今回からただ書くだけでなく、心の声も入れて記事書いてみます。
Java Gold取得に向け勉強中ですので同じく勉強している人に共感してもらえたらうれしいな...
※主は愛知県三河出身
「StreamAPI?なんじゃそりゃ」
StreamAPIが唐突に表れた!
黒本しか持ってない勢としては、「いきなり出てきてごっめーん!まことにすいまめーん!(ジョイマン)」って感じ
なにやらJava Goldでは頻出の問題らしい...
「第一問 Optionalクラスについて...(え、StreamAPIじゃないじゃん!)」
「StreamAPIのメソッドの使い方覚えればいいんだら?」と思っていたのになんだよOptionalって...
とりあえず、調べたことと黒本の内容を確認します。
## Optionalクラス
Java SE 8で導入されたjava.util.Optionalはメソッドの処理結果を扱うためのクラスで処理結果の正常・以上に関わらず同じ型で扱うことができます。このクラスを利用することにより、例外処理に関するコードを減らすことができ、その結果、コードの可読性を上げることができます。 --黒本解説より引用
「...でたでた、すぐ便利だから使えとか言うんだら?田舎者はついて行けないんじゃ!」
「...とりあえず、サンプルコード見るか...」
サンプルコード
※勝手にソースにコメントつけてます。
public class OldTest {
public void test() {
try {
///getFromArrayメソッドは配列からインデックス番号に一致する要素を戻す
String result = getFromArray(new String[] { "A", "B", null }, 3);
//resultがnullの時はemptyと出力
if (result == null) {
System.out.println("empty");
return;
}
//resultがnullでなければresultを出力
System.out.println(result);
} catch (IllegalArgumentException e) {
System.out.println("exception");
}
}
private static <T> T getFromArray(T[] array, int index) {
//arrayがnullの時は例外をthrow
if (array == null) {
throw new IllegalArgumentException();
}
//指定されたindex番号の要素を返す
try {
return array[index];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException();
}
}
}
(解説)「このgetFromArrayメソッドを利用するときに注意することは次の4点です。
1.引数の配列は参照型であるため......」
「ちょっとまてい!サンプルコードで疑問を増やさんでくれ!」
分からないこと調べていたら更に分からないことが出てくる。それを調べたら更に分からいことが出てくる...
そして宇宙の起源まで調べのです...
以下、悪魔の魔導書(黒本)から生まれた疑問点です。
①private static T getFromArray(T[] array, int index) {...「'<T>'←これなに!?」
②「なんかいっぱい例外処理してるけど何の例外だよ。try-catchやらthrowやらなんかこいつ忙しいな」
幸いにも今回は宇宙の起源まで調べなくてもChatGPT先生に聞いたら何とかなりました。
「...もうお前(ChatGPT)が試験受けてきてくれ(´;ω;`)」
①'<T>'の正体
「Tってなに?Twice?TT兄弟?Dの一族?ワンピースの話?」
つまらないボケは置いといて...
ChatGpt先生によると
は、Javaのジェネリックス(Generics)を表しています。ジェネリックスは、コードの再利用性と型安全性を向上させるために導入された機能です。
は型パラメータ(Type Parameter)と呼ばれ、任意の型を表すために使用されます。この場合、メソッドの戻り値型および引数の型をジェネリックな型にするために使用されています。
「つまりメソッドの前のと書けば、その型で受け取ってその型で返すってことね」
よく見るのはListクラスにつけて型を指定するやつ
///入れられる型がString型に限定される
List<String> arr = new ArrayList<String>();
ジェネリクス型についてはこれくらいにしておこう...(深く調べ始めると宇宙に行ってしまう...)
今回の例でいうとgetFromArrayメソッドはどんな型の配列でも受け取ってその配列の指定したインデックス番号の要素を返すってことね
②例外の紹介
とりあえず、今回出てきた例外は以下の3種類
例外 | throwの条件 |
---|---|
IllegalArgumentException | メソッドに渡された引数が無効または不正 |
ArrayIndexOutOfBoudsException | 配列のインデックスが範囲外 |
NullPointerException(←これは解説で出てきたので一応紹介) | nullの参照を使用しようとする |
↑は非検査例外(Unchecked Exception)なのでメソッドのシグネチャ(メソッド名とその引数の型・数・順序を含む部分)で
throwsキーワードを使用して宣言する必要はありません。
「そーいえばJava Silverで例外について問題でたなー」
検査例外は、コンパイル時にチェックされるため、例外処理を強制することでエラーを捕捉し、適切なリカバリー処理を行うことができます。一方、非検査例外は予期しないエラーやランタイムエラーを表し、プログラムの正常な実行を続けることが困難な状況を示します。
--ChatGpt先生
「ありがと!ChatGpt先生!いつもeditorが勝手に生成してくれるから忘れがちじゃんね」
「え?Silverでやったことぐらい覚えとけよって?」
「...」
「...ん?どした?」
宇宙から帰還そして本題へ
やっと本題に戻れる...
解説によるとサンプルコードでは以下の4つの問題があるらしい。しかもOptionalクラスを使用すれば解決できるとのこと
- 引数の配列は参照型だからnullが渡される可能性がある
- 引数で渡されるインデックスが配列の要素を超える可能性がある
- 配列の要素がnullの可能性があり、戻り値を受け取った側でnullの確認をしないとjava.lang.NullPointerExceptionが発生する
- スローされてくるjava.lang.IllegalArgumentExceptionは非検査例外のため、例外処理が無視される可能性がある
「ほんとかなー」
1. 引数の配列は参照型だからnullが渡される可能性がある
2. 引数で渡されるインデックスが配列の要素を超える可能性がある
String result = getFromArray(new String[] { "A", "B", null }, 3);
「確かにString型の配列を参照しているし、インデックスに3を指定しているから要素数を超えちゃってるけど...」
「いやーサンプルコードだから仕方ないのかもだけどメソッドの引数を使ってるとかではなく、
メソッド内で生成した配列使ってるんだからわかるやん...そもそも可能性とかじゃなくて変なつくりじゃん...」
「学生の頃は動く点Pにも同じ時間に家を出ない兄弟にも何も思わなかったのですが、なんだかむかつくな」
3. 配列の要素がnullの可能性があり、戻り値を受け取った側でnullの確認をしないとjava.lang.NullPointerExceptionが発生する
getFromArrayメソッドでインデックス数を2にするとnullの要素を指定して例外が発生してしまう
String result = getFromArray(new String[] { "A", "B", null }, 3);
//NUllPointerExceptionが発生
System.out.println(result.length();
「これも同じく処理の結果でnullになってるのではなく、自分でnullにしてるから、あほな感じがする」
「サンプルだから僕のために馬鹿を演じているのか!...ぐす(´;ω;`)ありがとう!」
4. スローされてくるjava.lang.IllegalArgumentExceptionは非検査例外のため、例外処理が無視される可能性がある
「...例外が無視!?どゆこと???」
「ChatGptせんせーーい!」
ChatGptの回答:例外が無視されるパターン
- catchブロックが存在しない場合:非検査例外が発生しても、該当する例外をキャッチするcatchブロックが存在しない場合、例外処理は無視されます。
public void someMethod() {
throw new RuntimeException("An exception occurred");
}
public void anotherMethod() {
someMethod(); // 例外がスローされるが、catchブロックが存在しないため無視される
}
- catchブロック内で例外が再度スローされない場合:非検査例外がキャッチされた場合でも、それを再度スローせずに無視することができます。
public void someMethod() {
try {
// 何らかの処理
} catch (RuntimeException e) {
// 例外がキャッチされるが、再度スローされずに無視される
}
}
「...え、無視ってシステムが無視するとかじゃなくてプログラマーが無視するってことかよ」
「まーなくもないか...」
Optionalクラスを使った解決策
「やっとOptionalクラスの話に戻ってこれた」
「あれ、今日はstreamAPIの勉強してたんだらー...?」
とりあえずサンプルをコメント多めに付けといたので見てみましょう!
import java.util.Optional;
public class OldTest {
public void test() {
//おバカなので配列のindex数を超えた値を指定してしまってる!
Optional<String> result = getFromArray(new String[] { "A", "B,", null }, 3);
//getFromArrayメソッド側で例外が出るときは"empty"を返すようになっていて
//try-catchをなくすことができたね!
if (result.isEmpty()) {
System.out.println("empty");
return;
}
//例外がなければコンソールに出力
System.out.println(result.get());
}
private static <T> Optional<T> getFromArray(T[] array, int index) {
//NullPointerExceptionを回避しようとしている
if (array == null) {
return Optional.empty();
}
//指定されたindex番号の要素を返す
try {
return Optional.ofNullable(array[index]);
} catch (ArrayIndexOutOfBoundsException e) {
return Optional.empty();
}
}
}
「<T> Optional <T> getFromArray....ってなんやねん」
「<T> <T>ってやっぱりtwiceやん...」
この疑問については納得のいく答えが見つからなかったのでわかり次第追記します。
今回は以上となります。結局StreamAPIまでたどり着けなかったし、
黒の魔術書(黒本)の一問を解いただけ...
SE辛すぎ
※次回もOptionalの調査だ!