Java
昨年、20周年を迎えたJava。
最近は関数型言語が流行ってきたり、Script系言語でサッとつくることが多くなってきたので、使ってる人は、昔に比べると減ったのかなと思いますが、それでも、多いですよね。
20周年のイベントもたくさんあったりしました。
そんなJavaですが、まだまだ発展中です。「嫌い」という声も聞いたりします。
私が個人的に「嫌なところ」を書いてみたいと思います。
プリミティブ型が中途半端
プリミティブ型(int,long等)とそれを表現するクラス(java.lang.Integer, java.lang.Long等)の両方があるため、どちらを使おうか迷ったりします。また、その影響で混乱することがあります。
例えば、
- int型
- Integerクラス
がありますね。
IntegerクラスはImmutableなので、単純にIntegerクラスだけでいいとかいうわけではありませんが、いろいろ混乱します。
代表的なのはこんな例
public class IntegerSample {
static public void main(String[] args){
System. out.println("new Integer(100)" );
Integer new100_1 = new Integer(100);
Integer new100_2 = new Integer(100);
System. out.println(" == :" + (new100_1 == new100_2 ));
System. out.println("equals:" + new100_1 .equals(new100_2 ));
}
}
これを実行すると
new Integer(100)
== :false
equals:true
ふむふむ。オブジェクトを==で比較してるので、こっちはfalseになるね。
じゃあ、少し変えて、こんなふうに。
public class IntegerSample {
static public void main(String[] args){
System. out.println("Integer.valueOf(100)" );
Integer valueOf100_1 = Integer.valueOf(100);
Integer valueOf100_2 = Integer.valueOf(100);
System. out.println(" == :" + (valueOf100_1 == valueOf100_2 ));
System. out.println("equals:" + valueOf100_1 .equals(valueOf100_2 ));
}
}
Integerのオブジェクトをnewじゃなくて、Integer.valueOf()で取得するように変えました。
これを実行すると、
Integer.valueOf(100)
== :true
equals:true
あれ?trueだ。
ふーん、valueOfで取得すると、同じインスタンスになるのかー。ほんとかなー。確かめてみよう。
今度は、
public class IntegerSample {
static public void main(String[] args){
System. out.println("Integer.valueOf(1000)" );
Integer valueOf1000_1 = Integer.valueOf(1000);
Integer valueOf1000_2 = Integer.valueOf(1000);
System. out.println(" == :" + (valueOf1000_1 == valueOf1000_2 ));
System. out.println("equals:" + valueOf1000_1 .equals(valueOf1000_2 ));
}
}
数字を100じゃなくて、1000にしてみた。
実行すると、
Integer.valueOf(1000)
== :false
equals:true
おー、今度はfalseだ。なんだこれ??
となったので、ソースコードを確認してみました。
すると、Integerクラスの中には、IntegerCacheという内部クラスがあり、-128~127までのIntegerクラスのオブジェクトがキャッシュされているではありませんか!
Integer.valueOf()で取得したときに、-128~127は同じインスタンスが返されて、それ以外の場合は別のインスタンスが返されています。
実は、Byteクラス、Shortクラス、Longクラスも同じように-128~127はXxxCacheという名前の内部クラスがあり、キャッシュされているのです!
もちろん、==じゃなくて、euqals()で比較するんだってはなしですが。このような実装になっていると、バグを発見しづらいことがあります。
※ただ、これは、「Javaの嫌なところ」じゃなくて、「Oracle Javaの嫌な実装」なんですけどね。
※この前、このバグを発見できなくて、悔しかったです。
※operatorのオーバーライドがあればなーと久しぶりに思いました。
日時のクラス群がカオス
最初からある、java.util.Dateクラス、JDK1.1で追加されたjava.util.Calendarクラス、そして、Java8で新たに導入されたjava.timeパッケージの仲間たち。
なかなかカオスです。
java.timeパッケージは新しい日時表現のクラス群として、登場してきました。java.util.Dateクラス、java.util.Calendarクラスの良くないところを改善して、導入されました(joda-timeという、日時を扱うライブラリをベースに導入されたようです)。
なので、こちらを使うことを推奨されていくと思います。
ただし、まだ導入するメリットはなかなか味わえないかなと感じています。
単純に時間を扱うだけであれば、java.timeパッケージを使った実装をすればよいですが、既存のライブラリやフレームワークを使う際には、java.timeパッケージに対応していなければ、せっかくjava.timeパッケージを使って実装していても、ライブラリやフレームワークとのやり取りの部分で変換しなければ使えません。
例えば、DBと日時のデータをやり取りする場合は、java.util.Data(たぶん、java.sql.Dateだったりしますが)と変換してあげなければなりません。めんどくさいですね。そして、せっかくjava.timeパッケージで処理してるのに、、、と思ってしまいます。
もう少し時間が経過して、java.timeパッケージが少し広まったとき、このライブラリはjava.timeパッケージで実装できるけど、こっちはjava.util.Dateだから、、、うーーん。。。ってなりそうです。
Stop The World
時間が止まります。
FullGCのときに。
まあ、それ自体は諦めてます。仕方ないと思ってます。
つらいのはStop The Worldが原因の問題が出るとき。対処方法が難しいのです。
Heapサイズを変えるとか、EdenとOldとサイズをチューニングするとか。GCのアルゴリズムを変えてみるとか。いろいろいろいろ。
これ!って対応方法を見つけるのがすごく難しいんですよね。
getter/setterをそろそろなんとかしてほしい
ずーーっと言われてますね。
最近はlombokがよく使われますが、それでもアノテーションを付けなければならないです。
このあたりの話をするとpublicでいいじゃないかとか論争がはじまることがよくあって、もーってなります。
※フレームワークがgetter/setterを要求することがおおいので、「publicでいいじゃないか論」はちょっと違うんじゃないかと思ってます。
そろそろ、アクセス修飾子の代わりにpropertyつければgetter/setterができるようになってほしいなぁと思うわけです。もちろん、getterだけ/setterだけができる修飾子もほしいです!
「JAVA」って書かれる
いや、別に、いいんですけど。
なんか、気持ち悪くて。
でも、公式も「あなたとJAVA」( https://www.java.com/ja/ )なんだよなぁ。
※公式はここ以外"JAVA"という表記見たことがない気がしますが、あるのかな?
まとめ
Java大好きです!
最初から「嫌い」なんて言ってないですよ。「嫌なところ」です。
今後とも、よろしくお願いします。Javaさん!