Kotlinを使ってみた

Kotlinを使ってみた

Javaを簡潔に記述できるKotlin。

KotlinでHello, World!下記のソースをHello.ktとして保存する。

1
2
3
fun main(args: Array<String>) {
    println("Hello, Kotlin!")
}

コンパイル、および実行。

1
2
3
$ kotlinc -include-runtime -d Hello.jar Hello.kt
$ java -jar Hello.jar
Hello, Kotlin!

javaコマンドで実行できてしまう。素晴らしい。

Kotlinの特徴

  • 行末のセミコロン不要。
  • javaクラスを自由に扱える。
  • コンパイルするとjavaクラスが作成される。
  • -dオプションで実行形式jarを作成することもできる。
  • 拡張子をktsにすれば、Kotlinスクリプトを記述でき、コンパイルなしに実行できる。
  • List、Mapが拡張され、lambda式で簡単にアクセスできる。

jythonの上を行きそう。

Kotlincでコンパイル

1
$ kotlinc -cp %CLASSPATH% -include-runtime -d Hello.jar Hello.kt

Hello.ktをコンパイルし、Hello.jarが作成される。
-include-runtimeを指定しているので、Kotlinのランタイムクラスがjarファイルに含まれる。
ランタイムは1MB程なので含めてよいと思う。
-include-runtimeを指定しなかった場合は、別途kotlin-runtime.jarを配布する必要がある。

kotlinc,kotlinコマンドの-cpは、jarの*指定はできなかった。それぞれのjarを個別に指定する必要がある。
例えば、Oracle.jdbcとCommonsLoggingを利用しているのであれば下記のように指定する必要がある。

1
2
$ CLASSPATH=/mylib/ojdbc6.jar;/mylib/commons-logging.jar
$ kotlinc -cp %CLASSPATH% -include-runtime -d Hello.jar Hello.kt

外部jarに依存しているのであれば、成果物のjarにランタイムを含めずに、上記の場合なら/mylibにKotlinのランタイムを追加すれば良いと思う。javaコマンドはクラスパスの*指定に対応しているので、下記のように実行できる。

1
$ java -cp /mylib/*:Hello.jar HelloKt

コンパイルしたクラスの実行

Kotlinコンパイル環境があるの場合

1
$ kotlin -cp %CLASSPATH%:Hello.jar HelloKt

HelloKtは実行クラス名。Hello.ktはHelloKtクラスとしてコンパイルされている。
kotlinコマンドでは、Kotlinのランタイムが自動的に参照されるので、コンパイル時にランタイムを含める必要はない。

javaの実行環境のみの場合

コンパイル時にkotlinランタイムを含めた場合

外部jarを参照していないのであれば

1
$ java -jar Hello.jar

外部jarを参照しているのであれば、

1
$ java -cp %CLASSPATH%:Hello.jar HelloKt

コンパイル時にkotlinランタイムを含めなかった場合

1
java -cp %CLASSPATH%:Hello.jar:kotlin-runtime.jar HelloKt

jaraの引数に-jarを指定すると、全てのクラスパスは無視されるので、
作成したjarもクラスパスに含めて、メインメソッドのあるクラスを指定する。
Hello.ktはHelloKtクラスとしてコンパイルされる。
Hello.ktにpackage指定されいている場合は、そのパッケージに所属した形でコンパイルされる。

下記のkotlinをHello2.ktとして保存。

1
2
3
4
package my.first
fun main(args: Array<String>) {
  print("Hello.")
}
1
2
3
$ kotlinc -include-runtime -d h.jar Hello2.kt
$ java -cp ./h.jar my.first.Hello2Kt
Hello.

Kotlinスクリプト

kotlinをコンパイルせずに、スクリプトとして実行できる。Kotlinスクリプトの拡張子はktsにしなければならない。

1
kotlinc -script Hello.kts

考察:jythonスクリプトをkotlinに置き換えるべきか

jythonもkotlinもJavaで実装されている。
Javaで実装されているため、Windows、Linux、macを問わず開発/実行できる。
Javaの豊富なライブラリを利用できる点はどちらも同じ。

では、jythonをkotlinに置き換えるべきなのだろうか。

jythonは、pythonのバージョンが2.7で止まっている。
python2.7なので、文字列の扱いがデリケートでる。
ここがとても煩わしい。jythonで色々なタイプの文字が出て来る場合は、
文字列結合も、jythonではなく、java.lang.StrinBuilderやjava.text.MessageFormatを使ったりしている。
kotlinでは、ここはあまり気にしなくて良さそうだ。

しかし、jythonの有利な点として、ファイルI/Oやディレクトリ操作、FTP、メール送信などがpythonとして簡単に記述することができる。
kotlinでも、色々kotlin拡張モジュールがあったり、強力なラムダ式を利用すれば、そこそこ簡単に実装できるようにも思えるが、pythonほどお手軽ではない。

pythonに慣れているために、こう考えてしまうのか。
もう少しkotlinでツールを作ってみよう。

同じタグの記事
同じカテゴリの記事

JavaでZip圧縮

JavaでZip圧縮

サーバ間のファイル連携で、XMLファイルを送信しているが、サイズが結構大きくなってしまう。
shellでZip圧縮してからFTP転送していたが、Javaから直接Zipファイルで出力する事にした。

JAXB.marshalの引数にOutputStreamを指定できるので、ZipOutputStreamを渡す。

1
2
3
4
5
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("out.zip")))) {
    ZipEntry ze = new ZipEntry("out.xml");
    zos.putNextEntry(ze);
    JAXB.marshal(dat, zos)
}

これで、out.zipの中に、out.xmlというファイル名で出力できる。
ディスクの遅いマシンで実行すると、Zipに出力した方がファイルサイズが小さくなり、出力処理が早くなった。
しかし、マシンによってはZip圧縮にコストがかかってしまい、処理が遅くなることもあった。

同じタグの記事
同じカテゴリの記事

Java9でJAXB

Java9でJAXB

単純なサーバ間のデータ連携アプリをjava9で動かすことにした。
すると

1
2
3
4
5
6
7
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXB
        at Jaxb.main(Jaxb.java:72)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXB
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
        ... 1 more

JAXBのライブラリは、Java9からmoduleとして定義され、自動で読み込まれなくなった。
Java9からはモジュールとして、自分で読み込む必要がある。
モジュールの読み込みは、javaコマンドの引数の–add-modulesで指定する。

1
$ java --add-modules java.xml.bind MyProgram

javax.xml.binはjava.xml.binモジュールに定義されているらしい。

ちなみに、どんなモジュールを利用しているかどうかは、Java9付属のjdepsというコマンドで確認できる。
MyProgram.classを確認してみた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ jdeps -cp . -verbose MyProgram.class
MyProgram.class -> .
MyProgram.class -> java.base
MyProgram.class -> java.xml.bind
    MyProgram  -> Col                         .
    MyProgram  -> Data                        .
    MyProgram  -> Row                         .
    MyProgram  -> java.io.File                java.base
    MyProgram  -> java.io.PrintStream         java.base
    MyProgram  -> java.lang.Class             java.base
    MyProgram  -> java.lang.Exception         java.base
    MyProgram  -> java.lang.Integer           java.base
    MyProgram  -> java.lang.Object            java.base
    MyProgram  -> java.lang.RuntimeException  java.base
    MyProgram  -> java.lang.String            java.base
    MyProgram  -> java.lang.StringBuilder     java.base
    MyProgram  -> java.lang.System            java.base
    MyProgram  -> java.lang.Throwable         java.base
    MyProgram  -> java.util.Iterator          java.base
    MyProgram  -> java.util.List              java.base
    MyProgram  -> javax.xml.bind.JAXB         java.xml.bind

最初の方に、利用しているモジュールの一覧が表示された。
その次に、利用しているクラスの一覧が表示された。
Java9のjdepsからは、利用しているクラスの右に、それらが格納されるモジュールが表示される。

これで、利用しているモジュールがわかるので、実行時に指定するようにする。
Project Jigsawは、他人事かと思っていたが、関係してきているようだ。

同じタグの記事
同じカテゴリの記事

javaでダミー画像作成

javaでダミー画像作成

商用環境のデータをテスト環境へコピーした。
データベースの個人情報にはマスクをした。
何が保存されているかわからない、画像のコンテンツはすべて削除した。

しかし画像コンテンツが何もないと、テストでの表示がうまくいっているのかわからない。
それぞれのデータ用に、ファイル名が書かれたPNGイメージを一括作成した。

java 8 の jjs (Nashorn) で作成した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var File = Java.type('java.io.File');
var Color = Java.type('java.awt.Color');
var Font = Java.type('java.awt.Font');
var BufferedImage = Java.type('java.awt.image.BufferedImage');
var RenderingHints = Java.type('java.awt.RenderingHints');
var ImageIO = Java.type('javax.imageio.ImageIO');

function createImage(filename) {
    var bufferedImage = new BufferedImage(200, 60, BufferedImage.TYPE_INT_RGB);
    var graphics2D = bufferedImage.createGraphics();
    //
    graphics2D.setColor(Color.BLUE);
    graphics2D.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
    //
    var font = new Font('Consolas', Font.PLAIN, 30);
    graphics2D.setFont(font);
    graphics2D.setColor(Color.WHITE);
    graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    graphics2D.drawString(filename, 5, 40);
    // output
    ImageIO.write(bufferedImage, "png", new File(filename));
}

createImage('test.png');

概要

1.BufferedImageを作り、そこからGraphics2Dを取り出す。
2.Graphics2Dに対して描画する。
3.ImageIOを利用して、ファイルに書き出す。

これだけ。

簡単にpngを作成できた。
java8だと、ほかにJPGやGIF、BMPが利用できる。
java9だと、TIFFなども利用できる。
しかし、BMPやTIFFはファイルサイズが大きくなるので利用するときはディスク容量に気を付ける必要がある。

また、文字を出力する場合、何も指定しないとアンチエイリアスされず、ギザギザした文字が描画されてしまう。
そこで、RenderingHintを設定して、アンチエイリアスした文字を書き込むようにする。
これで、綺麗な文字が出力される。

同じタグの記事
同じカテゴリの記事

javaでイメージフォーマット変換

javaでイメージフォーマット変換

書類を管理しているシステムがある。
書類の解像度を落としたくないため、TIFFフォーマットで保存されている。
しかし、TIFFフォーマットはブラウザで直接ビューイングできないので、ウェブアプリでは利用しにくい。
javaでTIFFからJPGに変換してみる。

javaでテストするのは面倒なので、jjs (java9 Nashorn)で作ってみる。

1
2
3
4
5
6
7
8
9
var ImageIO = Java.type('javax.imageio.ImageIO');
var File = Java.type('java.io.File');

function convert(filename, toFormat) {
    var imageIo = ImageIO.read(new File(filename));
    ImageIO.write(imageIo, toFormat, new File(filename + '.' + toFormat))
}

convert('test.tif', 'png');

簡単にpngに変換できてしまう。
ただし、TIFFを扱うためには java 9 が必要。
java 8でもpng,jpg,gif,bmp間の変換は可能。

コマンドラインツールを使ってもいいけど、サーバによってはプログラムのインストールが禁止されているので、
簡単に実行環境が作成できるjavaで実装した。

同じタグの記事
同じカテゴリの記事