読者です 読者をやめる 読者になる 読者になる

seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

#akskscala でScalaFlavor4Jを紹介しました

ScalaFlavor4Jは、シンプルなJavaで実装されたライブラリです。
Function,Seq,Range,Option,Map,ParSeqなどScalaのインタフェースと互換のAPIを実装しています。

https://github.com/m3dev/scalaflavor4j

ここでは、もう少し実際に近い形の使い方の例をあげてみます。

Option

class UserService {
  public User findById(String id){
    return DataStore.findUserById(id);
  }
}
User user = userService.findById("123"));
// userはnullかもしれない・・
class UserService { 
  public Option<User> findById(String id) {
    return Option._(DataStore.findUserById(id));
  }
}
Option<User> userOpt = userService.findById("123"));
// userがnullかもしれないことを明確に表現する

後続の処理でnullチェックでこのように書いていたものが・・

String name;
if ( user != null ) {
  name = user.getName();
} else {
  name = "Anonymous";
}

このように書けるようになります。

String name = userService.findById("123").map(new F1<User, String>() {
  public String _(User user) {
    return user.getName();
  }
}).getOrElse(new F0<String>() {
  public String _() {
    return "Anonymous";
  }
});

コレクションの操作

List<Integer> input = Arrays.asList(3,2,5,0,4,1);

List<Integer> result = new ArrayList<Integer>();
for ( Integer i : input ) {
  if ( i != null && i > 2 ) { 
    result.add(i * i); 
  }
}

このようにすると途中のmutableな状態が排除されます。

List<Integer> result = Seq._(input).dropNull().filter(new F1<Integer, Boolean>() {
  public Boolean _(Integer i) { 
    return i >2;
  }
}).map(new F1<Integer, Integer>() {
  public Integer _(Integer i) { 
    return i * i; 
  }
}).toList();

また、「Seq#par()」を呼び出してparallel collectionのfilter&mapにすれば、簡単にFork/Joinを使った並列処理にすることができます(Java7でなくても使えます)。

List<Integer> result = Seq._(input).dropNull().par().filter(new F1<Integer, Boolean>() {
  public Boolean _(Integer i) { 
    return i >2;
  }
}).map(new F1<Integer, Integer>() {
  public Integer _(Integer i) { 
    return i * i; 
  }
}).toList();

Map

java.util.Map#keySet()で取得したキーのSetでforループを回す処理は、foreachやmap、filterなどを使ってこのように書き換えることができます。

import java.util.*;

Map<String, String> names = new HashMap<String, String>();
names.put("John", "Lennon");
names.put("Paul", "McCartney");
names.put("George", "Harrison");
names.put("Ringo", "Starr");

Map<String, String> hasA = SMap._(names).filter(new F1<Tuple2<String, String>, Boolean>() {
  public Boolean _(Tuple2<String, String> name) {
    return name._1().contains("a") || name._2().contains("a");
  }
}).toMap();

SMap._(hasA).foreach(new VoidF1<Tuple2<String, String>>() {
  public void _(Tuple2<String, String> name) {
    System.out.println(name);
  }
});

ConcurrentOps

scala.concurrent.ops互換です。

Thread立ち上げやExecuterServiceを使っていたコードがこのように書けます。これを使った非同期処理はFork/Joinで実行されます。

import static com.m3.scalaflavor4j.ConcurrentOps.*;
spawn(new VoidF0() { 
  public void _() {
    System.out.println(“On a different thread!”);
  }
});

ExceptionControl

scala.util.control.excepiotn互換です(すべては実装していません)。

String result;
try { 
  result = "ok";
} catch (AException ae) { 
  result = "ng"; 
} catch (BException be) { 
  result = "ng"; 
} catch (CException ce) { 
  result = "unexpected"; 
}

このように書けます。

import static com.m3.scalaflavor4j.ExceptionControl.*;
String result = 
  catching(AException.class, BException.class)
    .withApply(new F1<Throwable>() {
      public String _(Throwable t) { 
        return "ng"; 
      } 
  }).or(catching(CException.class)
    .withApply(new F1<Throwable>() {
      public String _(Throwable t) { 
        return "unexpected"; 
      } 
    })
  ).apply(new F0<String>() { 
    public String _() { 
      return "ok"; 
    } 
  });

Source

scala.io.Source互換です(すべては実装していません)。

BufferedSource source = Source.fromFile("input.txt", "UTF-8");
BufferedSource source = Source.fromURL("http://docs.scala-lang.org/cheatsheets/", "UTF-8");

Seq<Byte> bs = source.toByteSeq():
Seq<Character> cs = source.toCharSeq():
Seq<String> lines = source.getLines();

まずはOptionあたりから・・

まずはOptionあたりから使ってみてください。

https://github.com/m3dev/scalaflavor4j