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) 
共通テーマ:資格・学び