No Programming, No Life

プログラミング関連の話題や雑記

Javaはつまらなくなったな

f:id:fumokmm:20191223002811p:plain:w500

これは、Javaに限った話ではないが、最近の流れとして、プログラミング言語がどれもこれも似てきてしまっているように思う*1。 あまりに言語が似すぎてしまうと、何を選んでも大差ない、選ぶ基準は動かせるプラットフォームとか、動作速度とか、そういった部分になってきてしまう。言語を選ぶ基準がその言語の特徴的な機能や、その言語でしか出来ないことでなくなってしまうことに、ワクワク感が薄れてしまうのは私だけだろうか。

AltJava言語達

このブログの読者の皆さんならきっとご存知と思うが、JavaはJava8でついに重い腰を上げて関数型の構文が取り入れた。そのあたりから進化のスピードが加速してきており、その後はJava10で型推論が導入されたり、Java12ではSwitch式が取り入れられたりと枚挙にいとまがない。 しかしJava8が世に出る以前から、AltJava言語*2では既に取り入れられていたものが多くある。 諸事情により、Java6で長い間更新が滞ってしまっていたこともあり、その間に多くのAltJavaが栄えたのではないかと思う。

その機能をよこせ

さて、このように「公式がなかなかやってくれないから俺がやるぜ!」と名乗りを上げた多数の言語達が、その当時Javaがまだ持っていなかった先進的な機能をどんどん繰り出すことによって優位性を誇示していた。 しかし現在、Javaは貪欲にそれらの機能を吸収し続けていっており、どんどん複雑になってきている*3。 こんな状況だから、心無い開発者から「もうその機能はJava自体で使えるんだから、AltJavaはいらないよねー」と言った声が聴こえてくると、とても悲しい。 誤解しないで欲しいのは、公式に採用されたことは非常に喜ばしいことであることに違いはない。世の中に広く普及するための第一歩を踏み出したのだ。だが、それでAltJavaのことを忘れないであげて欲しいのだ。牙を抜かれたライオンのような扱いはやめてあげて欲しい。

あえて削る

上であげたようなJavaの貪欲さとは対照的な美しさ、それはシンプルであることだと思う。 JavaJVMというシンプルなスタックマシンで動いている。コアはシンプルな方が美しい。本当に必要な機能だけを卒なく美しく提供してくれればそれでいい。それでよかったのだ。

Javaもかつては無駄な*4機能は削ぎ落す選択をしていた。だから未だにプロパティ*5を扱えなかったり、演算子オーバーロードできないし、もちろんポインタ*6も使えない。しかし、この状況だから、そのうちJavaでもプロパティが使えて、演算子オーバーロードできて、そしてついにはポインタが使えるようになってしまうかもしれない。そんなJavaはたぶんつまらない。 私はJavaをJava1.4の頃から使っているのだが、あの頃の不自由さが今思うと非常に懐かしい*7

多様性の海に

とりとめもなく書いてきたが、Javaに限らず、プログラミング言語は他の多種多様な言語の様々な機能に囲まれ、影響され、磨かれて進化してきたのだ。 たとえAltJavaが衰退しようとも、太りすぎたJavaがシンプルな新言語に将来もしかしたら駆逐されようとも、それはそれで。 この弱肉強食のプログラミングの多様性を最後まで見届けようと私は思う。

参考

Java8の新機能を徹底解説!(ラムダ式、Streamなど) | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト

Java9の新機能とサンプルコード - Qiita

JDK9 Jigsaw を試してみた - Qiita

Reactive Programming with JDK 9 Flow API | Oracle Community

Java 10新機能まとめ - Qiita

Java9,10,11の新機能 - Qiita

Java8からJava11への変更点 - Qiita

Java12新機能まとめ - Qiita

[速報]Java 13が登場。ZGCの改善やSwitch式の実現など新機能。 Oracle Code One 2019 - Publickey

*1:JavaよりもむしろJavaScriptなんかの方が状況がひどいことになっているかもしれないが、それはまた別の機会に

*2:Scala, Kotlin, Groovyなど

*3:例えるなら、千と千尋の神隠しに出てくるカオナシみたいな感じだろうか

*4:少なくとも、複雑になることを犠牲にしてまで取り入れなかったという意味で

*5:.propertiesではなくて、クラスのプロパティのこと

*6:参照先アドレスの直接操作の話

*7:こんなことを考えるようになったというのは、歳を取ったからなのだろうか

Javaで型トークンを利用したファクトリクラスを書いてみた

はじめに

Javaインスタンスを生成するクラス、いわゆるファクトリクラスを書く場合、色々な書き方があると思いますが、特定のクラスのインスタンスのみしか作れないとわりと不便なので、今回は、型トークン(クラスクラスと呼ばれるやつですね: Class<T> です)を引数として渡してそのクラスのインスタンスを返却してくれるとファクトリを書いてみました。
さらにそのファクトリを通して一度生成しているインスタンスについては同じものを返してくれるとメモリの節約にもなってよいですね。

というわけで、早速ソースとテストコードです

説明

とりあえず、クラスがないと説明しにくいのでテスト用に以下のような階層のクラスを作ってあります。

Alphabet  <-- 抽象クラス
 |- A     <-- 具象クラスその1
 |- B     <-- 具象クラスその2
 +- C     <-- 具象クラスその3  

さて、ここで、Aのインスタンスを取得するためのファクトリの使い方は以下のようになります。

// Alphabetのサブクラス用のファクトリ
Factory<Alphabet> factory = new Factory<Alphabet>();
// Aのインスタンスを取得
A a = factory.getOrCreate(A.class);

#getOrCreateメソッドに型トークン(Class<A>)を渡すことにより、Aのインスタンスを取得できています。ファクトリ内部では、型トークンをキーとしたマップにインスタンスが保持されているため、再度 #getOrCreate(A.class) を呼び出した場合は同じインスタンスが取得されます。

A a1 = factory.getOrCreate(A.class);
A a2 = factory.getOrCreate(A.class);
a1 == a2; // => true: 同じインスタンス

もちろん、型トークンを変えれば違う型のインスタンスも取得できます。

B b = factory.getOrCreate(B.class);
C c = factory.getOrCreate(C.class);

ただし、Factory<T>はジェネリックなクラスとし、Tという型パラメータで指定されたクラスのサブクラスのみ生成可能となるよう制限をかけています。
#getOrCreateメソッドもジェネリックになっており、型パラメータUは<U extends T>としてありますので、Tのサブクラスの型トークンのみを引数として取れる、という風になっています。

public <U extends T> U getOrCreate(Class<U> clazz) { ... }

蛇足

昔、Java1.4の頃、これと同様のことをやりたくて、ファクトリクラスには生成する必要があるクラス分だけ、getterメソッドを作った記憶があります…今となってはいい思い出です。

class Factory14 {
  Foo createFoo() { ... }
  Bar createBar() { ... }
  Baz createBaz() { ... }
       .
       .
       .
}

おわりに

Javaジェネリックは理解して使うとコードもすっきりと整理できるし、そのクラスを利用する側も負担が減り、みんなが幸せになれる素晴らしいモノですね!これからも活用してゆきたいと思います。

参考書籍

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)


主に第5章:ジェネリックスと項目29を参照

javaとかScalaで全角を半角に変換する方法をGroovyでもやってみた

はじめに

javaとかScalaで全角を半角に変換する方法 - ブログなんだよもんをGroovyでもやってみた。
元記事でも触れられておりますが、確かにこういう処理は再発明はしたくない。

ソース


Groovyの場合、Grape(@Grabアノテーション)でMavenリポジトリなどからライブラリを取得できる。今回のICU4Jの取得方法は現場で使L Java ライブラリ - 倭マン日記を参考にした。

追記1 (2012-05-12)

Spockでも \(・ω・)/ 書いてみた。

Happy Coding!

Javaでenumを使ってプロパティファイルのキーを定義する

はじめに

enumでインタフェースが実装できることが分かったので、その応用例の一つとして、Javaenumを使ってプロパティファイルのキーを定義するサンプルを書いてみました。

プロパティファイル読み込みは何が問題か

Javaでの外部プロパティファイルの機構は、

key=value

の形で外部ファイル *.properties にて定義し*1、それをjava.util.Properties を利用して取得する。

// 例
Properties prop = new Properties();
prop.load(...省略...):
String key = "key1"; // ← キーが文字列!
String value = prop.getProperty(key); // ← 文字列のキーを渡してプロパティの値を取得!

ですが、プロパティファイルは、決められた値の設定を記述しておくといったような用途が多いと思いますので、キーは最初から分かっているはずですよね。しかし、上記例のような方法だと、文字列にてキーを引き当てていますので、タイポや、キー変更による実行時に値が取得できないといったようなエラーが懸念されます。

できれば、キーは定数化したい

そんなの簡単じゃないかブラザー!定数で定義すればいいんだろ?

// 定数としてキーを定義
public static final String PROP_KEY_KEY1 = "key1";
public static final String PROP_KEY_KEY2 = "key2";
public static final String PROP_KEY_KEY3 = "key3";
 ・
 ・
 ・

// 使用
String value = prop.getProperty(PROP_KEY_KEY1); // 定数化してプロパティの値を取得!

わぉ、レガシー!・・・これでもまぁ、キーの文字列を直接指定していた場合よりはましですが。

もっといい方法はないか

Javaだったら定数は全てenumを使えばいい。

そうですね、enumを使えばいいんです。こんな感じに。

// enumでキーを定義
enum PropKey {
  key1, key2, key3
}
// 使用
String value = prop.getProperty(PropKey.key1.name()); // enumの要素(の文字列)を使ってプロパティの値を取得!
問題点

ただenumを使った場合、一つだけ問題が残ります。それはプロパティファイルが複数に増えてきた場合のお話です。たとえば、プロパティファイルの読み込みは実際には例外処理などを行う必要があり、煩雑となるので、だいたいユーティリティクラスを作って読み込み処理を実装すると思いますが、その場合のキーをPropKeyのようにしてしまうと、複数のプロパティファイルへの対応できなくなってしまいます。
仕方ないので、キー名を工夫して、name() でキー文字列を取得するのではなく、getKey() などのメソッドを定義して、うまいことキーやファイルを特定するなどの痛々しい努力をするなんてシナリオも考えられます。

// enumで複数プロパティ分のキーを定義
enum PropKey {
  hoge_key1, hoge_key2, hoge_key3, // ← hoge.properties 用のキー
  fuga_key1, fuga_key2, fuga_key3  // ← fuga.properties 用のキー

  public String getKey() {
    return 先頭の "xxx_" を取り除いた文字列;
  }
}
// 使用
String value = PropertyReader.getProp(PropKey.hoge_key1);

そこでenumにインタフェースを持たせて、プロパティファイルのキーを定義してみる

インタフェースの定義

まずはこんなインタフェースを作成

/** プロパティのキーを表現するインタフェース */
interface PropKey {
  Properties getProperties();
  String name();
}
enumの定義

次にこのインタフェースを実装するプロパティのキーとなるenumを定義

/** hoge.properties で利用するキー */
enum HogeKey implements PropKey {
  key1,
  key2,
  ;
  public static final String PATH = "hoge.properties";
  @Override public Properties getProperties(){
    return PropertyReader.getProperties(new File(PATH));
  }
}

/** fuga.properties で利用するキー */
enum FugaKey implements PropKey {
  key1,
  key2,
  ;
  public static final String PATH = "fuga.properties";
  @Override public Properties getProperties(){
    return PropertyReader.getProperties(new File(PATH));
  }
}
読み込みユーティリティの定義

最後は実際にプロパティファイルから値を読み込む

/** プロパティ読み込みクラス */
class PropertyReader {
    ... 省略 ...
  /**
   * プロパティファイルから値を読み込む
   * @param key PropKey
   * @return
   */
  public String getProp(PropKey key) {
    ... 省略 ...
    Properties prop;
    ... 省略 ...
    return prop.getProperty(key.name());
  }
    ... 省略 ...
}

読み込みユーティリティ(PropertyReader)の読み込みメソッドの引数はenumのインタフェース型としています。これにより、プロパティの定義時(enum, *.properties)とプロパティ読み込みのためのキー指定時は明示的に、ユーティリティ内部での実際のプロパティファイル読み込み時は汎用的にキーを扱えるようにしています。

使用例
PropertyReader propReader = new PropertyReader();
propReader.getProp(HogeKey.key1); // => "hoge-value-1"
propReader.getProp(HogeKey.key2); // => "hoge-value-2"
propReader.getProp(FugaKey.key2); // => "fuga-value-2"
ソース

実際に動くサンプルを載せておきます。

補足

おまけで、enumで定義したキーがプロパティに存在しているかのテストも追加してあります。また、一回読み込んだプロパティ(Properties)はキャッシュするようにしています。

*1:もしくはxml形式にすることも可能。