ラムダ式の例外処理を綺麗にする

ラムダ式の中で例外が発生する場合は厄介なもので、何とかしようとするならラップに近い形で自分で用意するしかないみたい。

Consumer 、Function 、Predicate これらの例外捕捉処理バージョンを作っておけばかなり便利かもしれません。

import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
 * ThrowableConsumer
 */
@FunctionalInterface
public interface ThrowableConsumer<T> extends Serializable{
   void accept(T t) throws Exception;

   default Consumer<T> andThen(Consumer<? super T> after, BiConsumer<T, Exception> onCatch){
      Objects.requireNonNull(after);
      return (T t)->{
         try{
            accept(t);
         }catch(Exception e){
            onCatch.accept(t, e);
         }
         after.accept(t);
      };
   }
   /**
    * ThrowableConsumer 生成.
    * @param consumer 例外スローする Consumer<T>処理
    * @param onCatch Exception捕捉処理
    * @return Consumer<T>
    */
   public static <T> Consumer<T> of(ThrowableConsumer<T> consumer, BiConsumer<T, Exception> onCatch){
      return t->{
         try{
            consumer.accept(t);
         }catch(Exception ex){
            onCatch.accept(t, ex);
         }
      };
   }
   /**
    * ThrowableConsumer 生成(外に例外スロー).
    * @param consumer 例外スローする Consumer<T>処理
    * @return Consumer<T>
    */
   public static <T> Consumer<T> of(ThrowableConsumer<T> consumer){
      return t->{
         try{
            consumer.accept(t);
         }catch(Throwable ex){
            throw new RuntimeException(ex);
         }
      };
   }
}
import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
 * ThrowableFunction
 */
@FunctionalInterface
public interface ThrowableFunction<T, R> extends Serializable{
   R apply(T t) throws Exception;

   default <V> Function<V, R> compose(Function<? super V, ? extends T> before, BiFunction<V, Exception, R> onCatch){
      Objects.requireNonNull(before);
      return (V v)->{
         try{
            return apply(before.apply(v));
         }catch(Exception e){
            return onCatch.apply(v, e);
         }
      };
   }

   default <V> Function<T, V> andThen(Function<? super R, ? extends V> after, BiFunction<T, Exception,  ? extends V> onCatch){
      Objects.requireNonNull(after);
      return (T t)->{
         try{
            return after.apply(apply(t));
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }

   static <T> Function<T, T> identity(){
      return t -> t;
   }
   /**
    * ThrowableFunction 生成.
    * @param function 例外スローする Function<T, R>処理
    * @param onCatch Exception捕捉処理 , R を返すもしくは、null を返さなければならない。
    * @return Function<T, R>
    */
   public static <T, R> Function<T, R> of(ThrowableFunction<T, R> function, BiFunction<T, Exception, R> onCatch){
      return  t->{
         try{
            return function.apply(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }
   /**
    * ThrowableFunction 生成(外に例外スロー).
    * @param function 例外スローする Function<T, R>処理
    * @return Function<T, R>
    */
   public static <T, R> Function<T, R> of(ThrowableFunction<T, R> function){
      return  t->{
         try{
            return function.apply(t);
         }catch(Throwable ex){
            throw new RuntimeException(ex);
         }
      };
   }
}
import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Predicate;
/**
 * ThrowablePredicate
 */
@FunctionalInterface
public interface ThrowablePredicate<T> extends Serializable{

   boolean test(T t) throws Exception;

   default Predicate<T> and(Predicate<? super T> other){
      Objects.requireNonNull(other);
         return (t)->{
         try{
            return test(t) && other.test(t);
         }catch(Exception e){
            return false;
         }
      };
   }
   default Predicate<T> and(Predicate<? super T> other, BiFunction<T, Exception, Boolean> onCatch){
      Objects.requireNonNull(other);
         return (t)->{
         try{
            return test(t) && other.test(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }

   default Predicate<T> negate(){
      return (t)->{
         try{
            return !test(t);
         }catch(Exception e){
            return false;
         }
      };
   }
   default Predicate<T> negate(BiFunction<T, Exception, Boolean> onCatch){
      return (t)->{
         try{
            return !test(t);
         }catch(Exception e){
            return !onCatch.apply(t, e);
         }
      };
   }

   default Predicate<T> or(Predicate<? super T> other){
      Objects.requireNonNull(other);
      return (t)->{
         try{
            return test(t) || other.test(t);
         }catch(Exception e){
            return false;
         }
      };
   }
   default Predicate<T> or(Predicate<? super T> other, BiFunction<T, Exception, Boolean> onCatch){
      Objects.requireNonNull(other);
      return (t)->{
         try{
            return test(t) || other.test(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }

   static <T> Predicate<T> isEqual(Object targetRef){
      return (null==targetRef)  ? Objects::isNull : object -> targetRef.equals(object);
   }
   /**
    * ThrowablePredicate 生成.
    * @param p 例外スローする Predicate<T>処理
    * @param onCatch Exception捕捉処理 , boolean値を返さなければならない。
    * @return Predicate<T>
    */
   public static <T> Predicate<T> of(ThrowablePredicate<T> p, BiFunction<T, Exception, Boolean> onCatch){
      return t->{
         try{
            return p.test(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }
   /**
    * ThrowablePredicate 生成(外に例外スロー).
    * @param p 例外スローする Predicate<T>処理
    * @return Predicate<T>
    */
   public static <T> Predicate<T> of(ThrowablePredicate<T> p){
      return t->{
         try{
            return p.test(t);
         }catch(Throwable ex){
            throw new RuntimeException(ex);
         }
      };
   }
}

これを使うものとして、
Javaで MACアドレスを抽出する - Oboe吹きプログラマの黙示録
の処理を書いてみましょう。

最初の NetworkInterface.getNetworkInterfaces() だけは、SocketException の捕捉を外側(呼出し側)で処理を書かなくては
ならないですが、
NetworkInterface の isUp() や、getHardwareAddress() で発生の Exception 捕捉処理は
ThrowableXxxxx . of メソッドに、ラムダで書くことができます。
  特に、Predicate で、(t, error)->false と、問答無用に無視を指定できるのが良いです。

Collections.list(NetworkInterface.getNetworkInterfaces()).stream()
.filter(ThrowablePredicate.of(t->t.isUp(), (t, error)->false))
.forEach(ThrowableConsumer.of(nf->{
	String address = Optional.ofNullable(nf.getHardwareAddress())
			.map(e->IntStream.range(0, e.length).mapToObj(i->String.format("%02X", e[i])).collect(Collectors.joining(" "))
		).orElse(null);

        System.out.printf("%d,%d,%s,\"%s\",%s%n"
            , nf.isLoopback() ? 1 : 0
            , nf.isUp() ? 1 : 0
            , nf.getName()
            , nf.getDisplayName()
            , address
        );
}, (e, error)->{}));

Javaで MACアドレスを抽出する

java.net.NetworkInterface を利用して抽出します。
NetworkInterface から、getHardwareAddress() で、ハードウエアのアドレス=MAC アドレスに相当が見れます。

NetworkInterface は、以下の static メソッドがあります。

getByIndex(int index)               → 指定インデックスのネットワークインターフェース
getByInetAddress(InetAddress addr) → 指定IPアドレスのネットワークインターフェース
getByName(String name) → 指定する名前のネットワークインターフェース
getNetworkInterfaces() → 実行マシン上のすべてのネットワークインターフェース

だいたい、getNetworkInterfaces か、getByInetAddress を使用するのが実践向きと考えます。

getNetworkInterfaces が返すのは、残念ながら、Enumeration です。

getNetworkInterfaces を実行すると Windows PC の場合、
Teredo Tunneling Pseudo-Interface
  IPv6 over IPv4トンネリングプロトコルとして割り当てたものまで採取できてしまいます。
これはよくみると、6バイトで表す MACアドレスになってませんでした。


全てを見る場合、Enumeration なので、仕方なく while ループで書いてみます。

try{
   Enumeration<NetworkInterface> nfs = NetworkInterface.getNetworkInterfaces();
   while(nfs.hasMoreElements()){
      NetworkInterface nf = nfs.nextElement();

      String address = Optional.ofNullable(nf.getHardwareAddress())
      .map(e->IntStream.range(0, e.length).mapToObj(i->String.format("%02X", e[i]))
               .collect(Collectors.joining(" ")))
      .orElse(null);

      System.out.printf("%d,%d,%s,\"%s\",%s%n"
         , nf.isLoopback() ? 1 : 0
         , nf.isUp() ? 1 : 0
         , nf.getName()
         , nf.getDisplayName()
         , address
      );
   }
}catch(SocketException ex){
   ex.printStackTrace();
}

NetworkInterface の isUp()メソッドが返す動作してるものだけ抽出は、これだとwhile文中にif文書かなければ
なりません。

isUp()メソッドをつかうものを単純に Stream で書いてみます。

try{
   Collections.list(NetworkInterface.getNetworkInterfaces())
   .stream().filter(n->{
      try{
         return n.isUp();
      }catch(SocketException e){
      }
      return false;
   }).forEach(nf->{
      try{
         String address = Optional.ofNullable(nf.getHardwareAddress())
            .map(e->IntStream.range(0, e.length)
                     .mapToObj(i->String.format("%02X", e[i])).collect(Collectors.joining(" ")))
            .orElse(null);
         System.out.printf("%d,%d,%s,\"%s\",%s%n"
            , nf.isLoopback() ? 1 : 0
            , nf.isUp() ? 1 : 0
            , nf.getName()
            , nf.getDisplayName()
            , address
         );
      }catch(SocketException ex){
         ex.printStackTrace();
      }
   });
}catch(SocketException ex){
   ex.printStackTrace();
}

ラムダの中に try~catch を書くハメになってみっともない。Predicate や Consumer の例外スローするものでも
用意するか。。。

getByInetAddress で1つの MACアドレスを抽出する場合、、、

try{
   NetworkInterface nf = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());

   String address = Optional.ofNullable(nf.getHardwareAddress())
         .map(e->IntStream.range(0, e.length).mapToObj(i->String.format("%02X", e[i]))
                  .collect(Collectors.joining(" ")))
         .orElse(null);

   System.out.printf("%d,%d,%s,\"%s\",%s%n"
      , nf.isLoopback() ? 1 : 0
      , nf.isUp() ? 1 : 0
      , nf.getName()
      , nf.getDisplayName()
      , address
   );

}catch(SocketException ex){
   ex.printStackTrace();
}catch(UnknownHostException ex){
   ex.printStackTrace();
}

InetAddress.getLocalHost() の実行で、UnknownHostException 捕捉も必要になります。

byte[] を IntStream で、、、

byte配列 を IntStream として処理する方法、

int配列 ならば、

    int[] i = { 25, 26, 27, 28 };
    Arrays.stream(i)

は、成立するが、

     byte[] b = { 25, 26, 27, 28 };
     Arrays.stream(b)

というのは成立しない。それでも byte配列 を IntStream として処理したい時はどうするか?
同じ悩みを抱えた人はいたみたいで、以下、stackoverflow に、策が載ってました。
stackoverflow.com
byte配列 なんだから、ByteArrayInputStream で処理するというのも自然と納得はするけど、一手多くて
どうもスッキリしません。

byte[] b = { 25, 26, 27, 28 };

ByteArrayInputStream inst = new ByteArrayInputStream(b);
String result = IntStream.generate(inst::read).limit(inst.available())
                .mapToObj(i->String.format("%02X", i))
                .collect(Collectors.joining(" "));

これで、result は、、

19 1A 1B 1C

にはなります。

やはり1つの実行文で、、、

byte[] b = { 25, 26, 27, 28 };

String result = IntStream.range(0, b.length).mapToObj(i->String.format("%02X", b[i]))
                .collect(Collectors.joining(" "));

で、、、

19 1A 1B 1C

の方が、スマートです。

stackoverflow に書かれたように、つまり、、

    IntStream.range(0, b.length).map(i->b[i])

これに尽きるのですが、map変換の目的によっては、map の中で、そのまま、String.format で変換が無理なので、
mapToObj を使います。

ファイルPATH の階層によるコンパレータ

使わないかもしれないけど。

public final class FilePathComparator implements Comparator<File>{

   private String  splitCharacter = File.separator.equals("\\") ? "\\\\" : "/";
   private OrderBy orderby;

   public enum OrderBy{
      ASC(1),
      DESC(-1);

      private int v;
      private OrderBy(int v){
         this.v = v;
      }
      public int getVector(){
         return v;
      }
   }

   public FilePathComparator(OrderBy orderby){
      this.orderby =orderby;
   }

   @Override
   public int compare(File o1, File o2){
      return new Integer(o1.getPath().split(splitCharacter).length)
      .compareTo(new Integer(o2.getPath().split(splitCharacter).length)) * orderby.getVector();
   }
}

この中の enum OrderBy で、昇順、降順を指定する。
使用例、

List<File> list;

// 省略...

list.stream().sorted(new FilePathComparator(OrderBy.ASC)).forEach(e->{

});

Wicket 作成した Panel の中のListView に置いた Form field の数

ページに、配置する Panel を作り、Panel の中に、ListView を配置して、
ListView で、フォームフィールドのコンポーネントを用意したときで、ListView の行数が可変の場合、
submit した時、行数を求めるのは、どうしたらいいか悩む。

そこで、考えたのが、、、

getRequestCycle() → getPostParameters() → getParameterNames() → Stream で ListView を探して
インデックスの Max を求める。。。

int listvewSize = getRequestCycle().getRequest().getPostParameters().getParameterNames()
.stream()
.filter(e->e.startsWith("panel:listview:"))
.map(e->e.replace("panel:listview:", "").split(":")[0])
.mapToInt(e->new Integer(e))
.max().orElse(-1) + 1;

これで、ListView が増えたり減ったりした後でもサイズを求めて適切に処理できる。

File.separator の処理

ファイルのPATH  Windows でも Linux でも、階層に分けた同じ結果リストを求める。
どういう課題かというと、、、

Windows

c:\a\b\c

Linux

/a/b/c

とある時、

a
a/b
a/b/c

というリストを改行出力した結果を得たい。
Windows 環境の PATH であろうが、Linux環境であろうが、同じ結果が欲しい。
ちょうど、Stream の collect 集計の練習になる。

String path に、格納されているとして、以下のようになる。

List<String> list = Stream.of(path.split(File.separator.equals("\\") ? "\\\\" : "/"))
.filter(s->s.length() > 0 && !s.matches("^[a-zA-Z]:$"))
.collect(()->new ArrayList<String>(), (r, s)->{
   if (r.size() < 1){
      r.add(s);
   }else{
      r.add(r.get(r.size()-1) + "/" + s);
   }
}, (r, t)->r.addAll(t));

もっと短く書けるはずで、、

List<String> list = Stream.of(path.split(File.separator.equals("\\") ? "\\\\" : "/"))
.filter(s->s.length() > 0 && !s.matches("^[a-zA-Z]:$"))
.collect(()->new ArrayList<String>()
, (r, s)->r.add((r.size() < 1 ? "" : r.get(r.size()-1) + "/") + s)
, (r, t)->r.addAll(t));

これで、

list.stream().forEach(e->{
   System.out.println(e);
});
a
a/b
a/b/c

Files.lines を使おう。

Java で 有名なフレームワークを使った Webアプリ開発ばかりやっていると、
ファイルシステム上のテキストファイルを1行ずつ読込み処理するというものを作る機会は
案外、少ないもので、どうするんだっけ。。と思いだすまで数十秒かかってしまい自己嫌悪。。。

たいていは、BufferedReaderを使って、、

try(BufferedReader reader = new BufferedReader(new FileReader("/root/work/foo"))){
   String line;
   while((line = reader.readLine()) != null){
       // 1行分文字列 line を処理
   }
}catch(IOException ex){
   
}

このように書くけど。。。Java8 以降は、以下の書き方もあるんですね。

try{
   Files.lines(FileSystems.getDefault().getPath("", "/root/work/foo"), StandardCharsets.UTF_8).forEach(s->{
      // 1行分文字列 s を処理
   });
}catch(IOException ex){
   
}

1行しか差がないと嘆くことない!
Reader の IO だと、文字セット指定できないけど、Files.lines なら、文字セットを指定できる。