So-net無料ブログ作成
検索選択

【Java】 Stream API 勉強中 (補足)

> entryArchiveのところで例外食いつぶしてますが、もちろんワザとです。

試しに例外をthrow(s)するようにしたところ、コンパイルが通らないことが判明。
いつもの「キャッチしろよクソボケが!」と怒られちゃう。
あぁ、forEachが例外に対してなーんもしてくれないからかな?

色んなブログなんかを舐め回したところentryArchiveをブロックで囲んでtry-catchしないとイケナイらしい。
うっは、これは面倒くさいし、見難いし醜い。
もしくはRuntimeExceptionに包むしかないと。

まぁでもファイル出力をStreamに噛ますのが邪道なのかもしれないしなぁ。
副作用だか何だかで。
てかIOと副作用の関係性も謎だけど。(近いうちに真面目に調べよう。。。)
あとまぁ例外をcatchせなならんことがどれ位有るかって話よなぁ。
(個人的に)例外は極力避ける方針だからまぁ余り問題は出ないとは思うけれども。

C言語なんかは例外無い訳だから戻り値で頑張ろうと思えば出来るんでしょうし、
「例外っていらなくね?」って論に転じるのも有りか?
(いや、APIが例外指向になっちゃってる時点で無いな…)
コメント(0) 
共通テーマ:資格・学び

【Java】 Stream API 勉強中 (ソース)

package application;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class FileManager {
 
 private static final Logger log = LogManager.getLogger(FileManager.class);
 private Path rootpath;
 
 public static void main(String[] args) {
  
  Path filepath = Paths.get("E:/RootDir");
  FileManager fmanager = new FileManager(filepath);
  
  Path zippath = Paths.get("E:/DestDir/save.zip");
  String[] subdirs = {"subdir1", "subdir2"};
  
  fmanager.packageOut(subdirs, zippath);
 }
 
 public FileManager(Path path) {
  rootpath = path;
 }
 
 
 public void packageOut(String[] subdirs, Path zippath) {
  
  Path[] targets = Arrays.stream(subdirs).map(subdir -> rootpath.resolve(subdir)).toArray(Path[]::new);
  Predicate<Path> selecter = path -> Arrays.stream(targets).anyMatch(target -> path.startsWith(target));
  
  try (
   Stream<Path> stream = Files.walk(rootpath);
   ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zippath)));
  ) {
   stream.filter(Files::isRegularFile).filter(selecter::test).forEach(path -> entryArchive(zos, path));
  } catch(IOException e) {
   log.error("圧縮に失敗しました。");
   throw new Error(e);
  }
 }
 
 
 private void entryArchive(ZipOutputStream zos, Path target) {
  
  String entryName = rootpath.relativize(target).toString();
  ZipEntry entry = new ZipEntry(entryName);
  
  try (BufferedInputStream is = new BufferedInputStream(Files.newInputStream(target));) {
   
   zos.putNextEntry(entry);
   
   byte[] buf = new byte[1024];
   int len = 0;
   
   while ((len = is.read(buf)) != -1) {
    zos.write(buf, 0, len);
   }
   
   zos.closeEntry();
   
  } catch (IOException e) {
   log.error("エントリ[{}]の圧縮に失敗しました。", entryName);
   e.printStackTrace();
  }
 }
}

コメント(0) 
共通テーマ:資格・学び

【Java】 Stream API 勉強中

Streamやらラムダ式やらを勉強中。

正直な話、「関数型」についてはあんまりピンと来なかったっていうか、
なんか馬鹿げた論争とか有ってシラけたのでやる気ゼロになってしまった訳ですけれども、
こと「関数を扱う」という指向と言いますか、集合指向と言いますか、
については大いに感じ入る所が有ります。

この辺りイマイチ、ピンと来ない方も居られるかもしれませんが、
集合を扱うとか、コマンドラインのパイプ処理とか、Perlのワンライナー辺りをイメージすると
結構理解が早かったりしますね。
…逆に混乱するかも分からんけど(笑)

とりあえず勉強の一環で
「あるディレクトリをフィールドとして持ち、そのディレクトリを(とある目的で)管理するクラス」
というのを個人的に作っていてですね、
その機能の一部である、「自身のサブディレクトリの幾つかをZipで出力する」機能をリファクタリングしてみました。

例での使い方はE:/RootDirの下にsubdir1~nまで有る内の2つをZip圧縮しています。
元のソース(※)からすると大分スッキリした感じですが、もうちょっと何とか出来そうな気もしています。

※ 元のソースは面倒なんで載せませんっていうか、ローカルに持ってないのです。

関数型インターフェイスの実装をメソッドに渡して別の関数型実装とクロスさせて使ったりするのが
本領だと思うんですがそこまでは至ってないですね。
まぁ昨日今日の突貫知識なので許してちょ。

entryArchiveのところで例外食いつぶしてますが、もちろんワザとです。
圧縮出来るものだけ圧縮するイメージですね。
もちろん一個失敗したらもう止めってのも良いと思いますけど、この場合はそんな必要もないかなと。
(失敗する可能性も低いでしょうし、失敗する場合は全部失敗するでしょう。)
本気でやるならゴミを削除する処理とかも必要でしょうかね。。。

ただこのStream APIですけど、やはりワンライナーよろしくやり過ぎると可読性が一気に下がります。
第三者から見たら何やってんだかさっぱり分からなくなる訳ですね。
その辺のバランス感覚を保ちつつ程々で手を打つのがポイントかと思います。


ソースは長くなったので次の記事で。
(ソネブロのうんこ仕様により一部全角となっております)
コメント(0) 
共通テーマ:資格・学び

【Java】 例外は難しい ⑤

さて、長い間が空いてしまって結局何書きたかったのかすっかり忘れてしまった訳ですが。。。

結局、例外はどうさばけば良いか?という話ですが(強引に戻したっ)
「例外は極力書かない、書かさない」
これな。

まぁその名の如く例外というはやはり例外的な処理な訳ですから、
やたらめったら書くような機会があるのは多分設計が良くない…という話に落ち着くのではないかと。
だからといって使うのに萎縮してしまうのも、それはそれで違う訳なので勘違いされたくはないのですが。


■例外を使わざるを得ないポイント

① APIが検査例外をthrowする場合。
まぁ良くあるのはIO例外とSQL例外ですな。Web系なら…うーん忘れた(笑)
最近はバッチマンですからな!ハハハ。
(IO例外とSQL例外については別途記事を上げるかもしれない。)

キャッチしたらリトライするってのも一つの考えですが、まぁそんな面倒なことしたくないですわな。(酷い)
なもんでバッチ系ならどうせ終わらせるしかないのでError、
Web系ならキャッチしてエラーページ出す場合が多いでしょうから例外サブクラスに包んでポイすれば良いかと。
(ちゃんと専用の例外クラスを作ると尚良いね)

あぁでもFileNotFoundExceptionはthrow(s)しても良いね。
これはデフォルト設定なんかがある場合は対処しようがある。


② 成功・失敗を知らせる必要があるが戻り値をそれに使えない場合。
例えば良く例に出るログイン処理なんかの場合。
DBにユーザ情報が無かったFailed(※)とDBがぶっ壊れてそもそも問い合わせ出来なかったFailedは
明確に区別したい訳で。
片や再入力を促し、片やエラーページへ、みたいな。
そういうのは戻り値をbool値にしても判断できないし、数値(エラーコード)で返すって手も有るけど、
処理上、ログインチェックの戻りがユーザ情報だったりする場合は困っちゃいますね。
そういうときはやはり専用の例外を吐くとスッキリしたコードに出来そうです。

※ DBにユーザ情報が無かったFailedはそもそも正常処理(終了)であるので
  ユーザ情報有りの正常終了と判断がつかない…と言うべきかもしれない。

戻り値がプリミティブな場合も同じことが言えますね。
Integer.parseIntのNumberFormatExceptionの例がそのまんまですが、
これでエラーなら-1を返すとかやってしまうと変換された正しい-1なのか何なのか?と。

とは言え、これは使う側との約束(※)がしっかりしていれば発生しない場合もあるので、
極力そのお約束を文書化するなりして無くす方向に持って行きたいですね。
parseIntに数値を表す文字以外は渡すなよ…と。
(やはりチェックメソッド欲しいよなぁ。。。)

※ 例えばparseInt(に近い処理)の場合は変な数値は0に丸めますよ…と言ったルールを明文化できれば
  RuntimeExceptionを吐く必要すらない筈です。
  (公に使うAPIでそんな丸めが許される訳はないのでInteger.parseIntはRuntimeExceptionで正しい)


その他
なんかあるかもしれない。
まぁ予防線だ。ハハハ。
コメント(0) 
共通テーマ:資格・学び

【Java】 例外は難しい ④

…といった辺りの論議も実に数年前からなされていたりする訳ですけれども、
IBMさんの文書
https://www.ibm.com/developerworks/jp/java/library/j-jtp05254/
でもやはり当初は「例外はキャッチして回復すべし」…が基本であったことが述べられてますね。

つまり良く見るコード
 public static void main(String[] args) throws Exception {
   ……
 }

は宜しくなかった訳です。敢えてこれを表現するなら

 public static void main(String[] args) {
  try {
   ……
  } catch (XxxException e) {
   System.exit(1);
  }
 }

という風にキャッチして「正常に」エラーコードを返すのが基本であった(過去形)と。
(ちなみに私は上の書き方を否定しません)


【歴史的な流れ(半分位憶測)】
Exceptionは「全て」キャッチして回復すべし!

いやいや、何だかtry-catchだらけで面倒くせーし、コードもきったないんですけどー

事前にチェックできるもんは、免除していいんじゃね?
よっしゃ、RuntimeException作ったろ!

うっは、これ便利すわ。
寧ろチェック例外が要らんかったんや!!

いやいや、ちょっと待って、チェック例外はチェック例外で有用だよ?

(´・ω・`)

という訳で結局どうすれバインダー。

超個人的な意見を言わせて頂くとですね、そもそも例外が要らないんです(´・ω・`)
だって知りたいのは処理がコケたかコケなかっただけじゃん。
Errorが有れば充分じゃないか?えぇ?

…と声高に言うと方々から反発を喰らいそうなので、これは心に仕舞っといてですね、
(割と本気で思ってはいる訳ですが)
「キャッチすべきではない重大な問題」であるErrorと「キャッチして回復」するExceptionの両極端だったのが、
やはり失敗だったんですね。
つまり「回復不能なあまり重要でもない問題」という考え方がすっぽ抜けてる。
(っていうかそれは美しくないと考えられたのだろう)
でも「回復不能なあまり重要でもない問題」って絶対有るんですよ。
例えばIOExceptionとかIOExceptionとかIOExceptionとかIOExceptionな。
(この話は後でする…筈)

で、現状それに対してウザさ解消だけの為に後付けやっつけで追加されたような
RuntimeExceptionを使っている訳です。ハッキリ言って訳が分からないよ!!
Errorだろそれ?Errorだるぉぉぉぉぐわぁぁぁぁぁっ!!!
…と言いたくなるもんです。

っていうかねErrorは「重大な問題」であって「VMの問題」なんて一言も述べられてないのに
VMエラーにしか使ったらダメという通説がまかり通ってしまったのがまぁ不幸ですわな。
コメント(0) 
共通テーマ:資格・学び

【Java】 例外は難しい ③

さて、ここで一つ、例外に関する処理を見てみたい。

 String strnum = [何らかの処理];
 int intnum = 0;
 try {
  intnum = Integer.parseInt(strnum);
 } catch (NumberFormatException e) {
  intnum = -1;
 }

果たしてこれは有りなのだろうか?
正直な話、私はこういうコードを書いたことが有る。
なぜなら「例外はキャッチして復帰すべき」と考えていたからだ。
そう、「例外は難しい ①」だけを読めばこの考え方は正しいと思う。

が、NumberFormatExceptionはRuntimeExceptionのサブクラスである。
プログラマが発生しないことを確信できる」から強制的にキャッチやthrows節に書くことを免除されているのだ。
parseIntの説明にも「文字列内の文字はすべて、10進数の桁である必要があります。」と明示されている。
であれば、

 String strnum = [何らかの処理];
 [strnumが10進数であることをチェックする処理]
 int intnum = Integer.parseInt(strnum);

とするのがより良いようにも思える。
[strnumが10進数であることをチェックする処理]により「プログラマが発生しないことを確信できる」状態にする訳です。
さらにstrnum = [何らかの処理]の処理で10進数の整数を表現する文字列が確信できるのであれば

 String strnum = [何らかの処理];
 int intnum = Integer.parseInt(strnum);

でも問題ない訳です。
…のですが、、、
RuntimeExceptionがキャッチまたはthrows説に書くことがコンパイラに検査されない、というのは
キャッチすることが禁止されている…という訳ではないですよね。
なので「例外はキャッチして復帰すべき」という考え方も間違っているとは言いきれないのです。
上記3つのパターンのどれが最善かは最早前後の処理や規約で判断するしかなく、
これが例外をして難しいと言わしめる原因になっているのでは?と考える次第です。

個人的な見解としては、
NumberFormatExceptionは検査例外、つまり通常のExceptionのサブクラスであるべきだったのでは?と。
もしくはRuntimeExceptionであるのであれば、Integerに値検査用のメソッドがあるべきだったのでは?
と思う訳ですが、残念ながらそうはなっていないですね。

ちなみにintnumが復帰することが許されない…という状況も充分有りうるので1番目と2番目の処理において
チェックに違反した場合はErrorをスローすることも有り得るのかな…と。
コメント(0) 
共通テーマ:資格・学び

【Java】 例外は難しい ②

さて、前回でほぼ例外の仕様面は分かったような気になる訳ですが、
Error以外にもコンパイラの検査を免れる奴が居るらしいですぞ。

> 尚、Errorと『RuntimeException』およびそれらのサブクラス以外は
> コンパイラにより例外チェック機構が走り、

そう、RuntimeExceptionとそのサブクラスも検査を免れるのです!
なんということでしょう!!

なぜRuntimeExceptionは検査を免れるのだろうか?

プログラマには発生しないことが確信できるのに、
いちいち処置を強制させるのはお互いの得にならない…ということのようだ。
(この辺英語が難しくてちょっと良く分からないけど。。。)

例えばNullPointerExceptionならNullチェックをしておけば発生しないし、
NumberFormatExceptionなら数値の範囲チェックをすれば発生しない。

まぁそれはそれとして、そもそもRuntimeExceptionとは何なのだろうか?
直訳すると実行時例外となる訳ですが、実行時に発生する…ってことならほぼ全部そうだよなと(笑)
RuntimeExceptionの説明を見てもExceptionと違ってチェックされませんよ位のものである。
うーむ、解せぬ!

なぜRuntimeExceptionという名前を付けたのだろうか?
名前をつけるならUncheckedExceptionではないのか?
もっと言うとThrowableの直下に置くべきではないのか?

といった疑問が沸く訳ですが、この疑問、晴れる事は無さそうです。

単純に事前の入力値チェック等で充分に回避可能であればRuntimeExceptionのサブクラスにすることで
コンパイラの検査を回避出来ますよ、煩わしくないでしょ?
…的なものであって深い意味は無さそうである。
Exceptionのサブクラスであるところからして後付け感も感じる訳でして。。。



【参考】
https://docs.oracle.com/javase/jp/8/docs/api/
https://docs.oracle.com/javase/specs/jls/se8/html/jls-11.html
コメント(0) 
共通テーマ:資格・学び

【Java】 例外は難しい ①

本ブログの方でもチラっと触れたことがあるけど、
まぁ偉そうな事を言っておいて私自身も実は例外周りはイマイチしっくり書けたことがない。。。
Javaの例外周りの仕様は残念ながらモヤっとしている部分が有るので、
「こう書くとOK」みたいなのが無いのが原因なのかなと。
…という訳でまずは仕様周りから攻めてみたい。

【Throwable】
言わずと知れたException(とError)の親玉。以下のような特徴を持つ。
・実行スタックを積んでくれる。(ありがたやー)
・その名の通りスロー可能になる。もっと言うと
 - メソッドの右肩にthrowsとしてリスト出来るよ
 - インスタンス化してスロー(throw)出来るよ
 - スローされたオブジェクトを呼び元でcatch出来るよ
尚、ErrorとRuntimeExceptionおよびそれらのサブクラス以外はコンパイラにより例外チェック機構が走り、
それらがスローされる可能性(+外に伝播する可能性)がある場合にはthrows宣言しなければならない。
「- メソッドの右肩にthrowsとしてリスト出来るよ」が強制される訳です。
さらに踏み込むとthrows宣言したくない場合は潰す、即ちcatchしなければならない…ということです。

【Exception】
Throwableの子供その1。
キャッチされる可能性のあるThrowableの形式の一つ。ちなみにもう一つの形式が次のError。
Exceptionがスローされてそのメソッドやコンストラクタの外に出る場合はthrows節で宣言しなければならない。

【Error】
Throwableの子供その2。
キャッチすべきではない重大な問題。
こちらはスローされる可能性が有ってもthrows節で宣言する必要はない。

という訳で、
例外機構としてはThrowableを継承したExceptionとErrorの2パターンに大別される。

ExceptionとErrorの仕様上の大きな違いはそれぞれ3行目に記述したthrows宣言の強制性である。
throws宣言が強制されない…というのはそのメソッドの外に出てもいいよ、という事なので、
必然的にcatchも強制されない。
Exception…コンパイル時にcatchまたはthrows宣言されているかが検査される。(検査例外)
Error…コンパイル時にcatchまたはthrows宣言されているかが検査されない。(非検査例外※)
という事である。

※ 厳密には例外ではないのかもしれないが。

Errorの方の説明をよく読むと「キャッチすべきではない」とある。
即ち、「強制されない」どころの話ではなくそもそもキャッチしてはいけないのだ。
キャッチしてはいけないのでキャッチさせてもいけない(≒throwsに書かなくて良い)訳ですな。
なのでコンパイラが検査する必要もないと。

なぜErrorを検査しないかについては言語仕様の方でさらなる言及がされている。
・いつ、どこで発生するか分からない (主にVM起因のエラーについてだろうか?)
・仮にキャッチしても回復が困難又は不可能
・そもそもErrorが発生してはならない
ということらしい。

「基本的に」キャッチすべきではない(※)理由については、最後まで伝播させるべきであるし、
下手にキャッチして潰されるのは困るということのようだ。

※ 「してはならない」ではない辺りイヤラシイ表現である。

つまり、いつ発生するかも分からないし、キャッチして欲しくないし、キャッチされたところでどうしようもないし、
そもそも通常は発生することがないから、
いちいちキャッチされているかthrows宣言されてるかは検査しないよってことのようだ。

ちなみにThrowable及びそのサブクラスが伝播して終了した場合のプログラム終了コードは1になります。
(通常は0)


というのが概ねの仕様であるらしいのだが、ここまで頑張って書いたけどまだ本題には入っていない。。。


【参考】
https://docs.oracle.com/javase/jp/8/docs/api/
https://docs.oracle.com/javase/specs/jls/se8/html/jls-11.html
コメント(0) 
共通テーマ:資格・学び

はじめに

こっそりと仕事用のブログを作っては消し作っては消しとしていたけど、
今回また懲りずに作成してみた。

本ブログの方でちょこちょこ書けばいいかな、とか思ってたんだけど、
如何せんアホな内容が多いので(笑)やはり仕事中にでも見れるようなシンプルなブログがいいなと。

仕事と言ってもITドカタなので、技術的な備忘録がメインになるのではと思っているが、
いい加減な性格なので先のことは分からない。
普段使っているJavaをメインにDB (Oracle)やシェルなんかも触れるかもしれない。

でもやっぱり3日坊主で終わるかも分からない。
nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:資格・学び

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。