Kotlin再発見

Kotlin今日の発見

Kotlinは、javaの上にかぶせて動かすもの。
したがって、Kotlinの構文がよくわからない時は、
Javaの構文でほとんど書けてしまう。
しかし、それに甘んじてしまうと、せっかくのKotlinを活かしきることが出来ない。
たまには、マニュアルに戻ってKotlinを知ることにする。

文字列の比較に==が利用できる

今更知ってしまった。

1
2
3
4
5
6
val s1 = "AAA"
val s2 = "BBB"
val s3 = "AAA"
println("s1==s2 ? ${s1 == s2}")
println("s2==s3 ? ${s2 == s3}")
println("s3==s1 ? ${s3 == s1}")

注意:上記の例は、定数の比較なのでjavaでも正しく動くことがある。

Listへの要素追加は+=で行える。

もちろんjavaのように.add()でも追加できるが、やはりKotlin流に。

1
2
3
4
5
6
val l1 = mutableListOf<String>()
l1.add("Zero")
l1.add("One")
l1 += "Two"
l1 += "Three"
println("l1[$l1]")

なんか、スッキリする。

Listから値を取得するときは配列の様に括弧で取得できる。

もちろんjavaのように.get()でも取得できるが、やはりKotlin流に。

1
2
val l2 = listOf("Zero", "One", "Two")
println("l2[1]:${l2[1]} l2.get(2):${l2.get(2)}")

こっちもスッキリ。

Mapへ値を追加するとも+=で行える。

もちろんJavaのようにput()でも行えるが、Kotlin流に。

1
2
3
4
5
6
val m1 = mutableMapOf<String, String>()
m1.put("A", "あ")
m1.put("I", "い")
m1 += "U" to "う"
m1 += "E" to "え"
println("m1:$m1")

素晴らしい。

Mapから値を取得しようとして、キーが存在したらそれを取得し、存在しなかったら、新たに値を作成し、指定したキーで値を追加するgetOrPutというメソッドがある。

1
2
3
4
5
6
val m2 = mutableMapOf("A" to "あ", "U" to "う")
val a = m2.getOrPut("A", {"ああ"})
print("a:$a")
val i = m2.getOrPut("I", {"いい"})
print("i:$i")
print("m2:$m2")

これは便利。入力データの重複を加味しながら処理をすることはよくあるので、このメソッドを使えば繰り返しの処理がかなり綺麗に書ける。

Mapから値を取得しようとして、キーが存在したらそれを取得し、存在しなかったらデフォルト値を取得するgetOrElseというメソッドがある。

1
2
3
4
5
val m3 = mutableMapOf("A" to "あ", "U" to "う")
val a = m3.getOrElse("A", {"ああ"})
print("a:$a")
val i = m3.getOrElse("I", {"いい"})
print("i:$i")

これも使い道はあると思う。getOrPut()ほどの感動はないが。

文字列中に変数を展開するとき、toString()を明示的に書くことは薦められない。

1
2
3
val l4 = listOf("あ", "い", "う")
println("リスト:$l4")
println("リスト:${l4.toString()}")

どちらも同じように動いてくれるが、toString()を呼ぶ必要はない。
toString()も明示的で嫌いじゃないけど。

まとめ

やはり、たまには振り返らなくてはならない。
Kotlin素晴らしい。
でも、一通りわかった気がしてきてしまったので、
次はrubyに手を出したい。

Jythonに変わるJavaモジュールを使える言語として選択したKotlinだったが、
やはりコンパイルは面倒。コンパイル時間も微妙にかかるし。
コンパイルしてしまえば、Jythonの10倍くらい速く動く気がするけど。

ツールを作る上では、気軽にソースを変更したい。
なので、JRubyを。
JythonのPython2系の文字コード関連で悩まされたので、
JRubyにその辺の問題がないのであれば、早く使ってみたい。

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

macにインストールしたjavaをアンインストールする

macにインストールしたjavaをアンインストールする

macを使っていて、気まぐれにjavaをインストールしている。
バージョンも覚えていない。複数のmacにインストールされたjavaのバージョンを
どれか決まったバージョンにしようと思った。
7だったり、8だったり、10だったり。
一旦削除したい。どうすれば良いのか?

下記の3つのディレクトリを順に削除する。

1
2
3
$ sudo rm -rf /Library/Internet Plug-Ins/JavaAppletPlugin.plugin
$ sudo rm -rf /Library/PreferencePanes/JavaControlPanel.prefPane
$ sudo rm -rf /Library/Java/JavaVirtualMachines/jdk1.8.0_*.jdk

ちなみにjdk-10の場合、3つ目のディレクトリは下記の通り

1
$ sudo rm -rf /Library/Java/JavaVirtualMachines/jdk-10.jdk

削除出来たかどうか確認

1
2
$ java
No Java runtime present, requesting install.

このように表示されれば、Javaがアンインストールされている。
最初、javaとかjdkとかが含まれるディレクトリを片っ端から削除しようと思っていたが、
macが管理している部分もあるようなので(インストールされていない時のメッセージ?)、余計なところは消してはいけないらしい。

さあ、mac達のjavaのバージョンは何にしようか・・・

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

KotlinでJAXB

KotlinでJAXB

Kotlinで作成しているツールでXMLの読み書きをしたい。
ネットで調べても、Kotlinで便利なXML Parserが見つからなかった。
しかし、KotlinはJavaなのだから、Javaで便利なJAXBが使えるのではないか。

XML出力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import javax.xml.bind.JAXB
import javax.xml.bind.annotation.XmlElement
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement

class Person(
    @XmlElement(name = "Name") var name: String,
    @XmlElement(name = "Age") var age: Int) {

    constructor() : this("no name", -1)
}

@XmlRootElement(name="ClassRoom")
class Root(name: String, age: Int) {
   
    var teacher = Person()

    @XmlElementWrapper(name="StudentList")
    @XmlElement(name="Student")
    val persons = mutableListOf<Person>()

    init {
        teacher.name = name
        teacher.age = age
    }

    constructor() : this("no teacher", -1)
}

fun main(args: Array<String>) {
    val r = Root("Ishii", 40)
    r.persons.add(Person("Student 01", 20))
    r.persons.add(Person("Student 02", 21))
    r.persons.add(Person("Student 03", 22))
    r.persons.add(Person("Student 04", 23))
    r.persons.add(Person("Student 05", 24))

    JAXB.marshal(r, java.io.File("work_out.xml"))

    println("OK!")
}

クラスの定義が容易なKotlinなので、XMLのデータクラス作成が楽。

XML入力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fun main(args: Array<String>) {
    val xml = """
        <?xml version="
1.0" encoding="UTF-8" standalone="yes"?>
        <ClassRoom>
            <teacher>
                <age>50</age>
                <name>Tokita</name>
            </teacher>
            <StudentList>
                <Student>
                    <age>30</age>
                    <name>Student 301</name>
                </Student>
                <Student>
                    <age>31</age>
                    <name>Student 302</name>
                </Student>
            </StudentList>
        </ClassRoom>
    "
"".trimIndent()
    xml.reader().use {
        val r2 = JAXB.unmarshal(it, Root::class.java)
        println("Teacher name[${r2.teacher.name}] age[${r2.teacher.age}]")
        println("Students")
        r2.persons.forEach {
            println("  Student name[${it.name}] age[${it.age}]")
        }
    }
}

読み込みも簡単。

まとめ

JAXBで読み書きしやすいXMLは、ある程度限られているので、
これで万能とは言えないが、シンプルなXMLはこれで十分対応可能なようだ。

ちなみに、Java 8 + Kotlin 1.2.31で実行。
Java 9だと、Jigsawの関係で、JAXBを使うために–add-modulesする必要がある。

1
2
$ kotlinc -Xadd-modules=java.xml.bind KotlinJaxb.kt -d KotlinJaxb.jar -include-runtime
$ java --add-modules java.xml.bind -jar KotlinJaxb.jar

kotlincの引数に-Xadd-modulesなんて言うのがあるとは。

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

Kotlin 1.2.31がいつの間にかリリースされていた

Kotlin 1.2.31がいつの間にかリリースされていた。

どんな新機能があるのかはわからないが、
java9で発生していた、ウォーニングメッセージやjava10でコンパイルできない問題が回避されている。

Kotlin 1.2.21 java9でのウォーニングメッセージ

1
2
3
4
5
6
7
8
9
10
11
12
13
$ kotlinc -version
info: kotlinc-jvm 1.2.21 (JRE 9.0.4+11)
$ kotlinc Test.kt
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.util.text.StringFactory to constructor java.lang.String(char[],boolean)
WARNING: Please consider reporting this to the maintainers of com.intellij.util.text.StringFactory
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
$ kotlin TestKt
java.class.version:53.0
java.runtime.version:9.0.4+11
java.version:9.0.4
java.vm.version:9.0.4+11

コンパイル時にウォーニングが表示されるだけで、コンパイルできるし、実行もできる。

Kotlin 1.2.21 java10ではコンパイルできない

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ kotlinc -version
info: kotlinc-jvm 1.2.21 (JRE 10+46)
exception: java.lang.ArrayIndexOutOfBoundsException: 446
        at org.jetbrains.org.objectweb.asm.ClassReader.readUnsignedShort(ClassReader.java:2464)
        at org.jetbrains.org.objectweb.asm.ClassReader.readUTF8(ClassReader.java:2525)
        at org.jetbrains.org.objectweb.asm.ClassReader.readModule(ClassReader.java:761)
        at org.jetbrains.org.objectweb.asm.ClassReader.accept(ClassReader.java:646)
        at org.jetbrains.org.objectweb.asm.ClassReader.accept(ClassReader.java:507)
    ...
$ kotlinc Test.kt
exception: java.lang.ArrayIndexOutOfBoundsException: 446
        at org.jetbrains.org.objectweb.asm.ClassReader.readUnsignedShort(ClassReader.java:2464)
        at org.jetbrains.org.objectweb.asm.ClassReader.readUTF8(ClassReader.java:2525)
        at org.jetbrains.org.objectweb.asm.ClassReader.readModule(ClassReader.java:761)
        at org.jetbrains.org.objectweb.asm.ClassReader.accept(ClassReader.java:646)
        at org.jetbrains.org.objectweb.asm.ClassReader.accept(ClassReader.java:507)
    ...

kotlincのバージョンを見ることも、コンパイルすることもできない。

しかし、コンパイルしておけば、実行はできる。

1
2
3
4
5
$ kotlin TestKt
java.class.version:54.0
java.runtime.version:10+46
java.version:10
java.vm.version:10+46

Kotlin 1.2.31 java9、java10どちらも問題なし。

1
2
3
4
5
6
7
8
$ kotlinc -version
info: kotlinc-jvm 1.2.31 (JRE 9.0.4+11)
$ kotlinc Test.kt
$ kotlin TestKt
java.class.version:53.0
java.runtime.version:9.0.4+11
java.version:9.0.4
java.vm.version:9.0.4+11
1
2
3
4
5
6
7
8
$ kotlinc -version
info: kotlinc-jvm 1.2.31 (JRE 10+46)
$ kotlinc Test.kt
$ kotlin TestKt
java.class.version:54.0
java.runtime.version:10+46
java.version:10
java.vm.version:10+46

とりあえず、Kotlinのバージョンを上げることにする。

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

python unpack

python unpack

今更ながら、tuple, list, dictはunpackできることを知った。
これを利用すると、formatがとても使いやすくなる。

1
2
3
4
5
6
7
8
9
10
11
12
tpl = ('a', 'b', 'c')
lst = ['x', 'y', 'z']
dct = {'aa': 'AA', 'bb': 'BB'}

print('tuple 1:{0[1]}'.format(tpl))
print('tuple 2:{1}'.format(*tpl))

print('list 1:{0[1]}'.format(lst))
print('list 2:{1}'.format(*lst))

print('dict 1:{0[bb]}'.format(dct))
print('dict 2:{bb}'.format(**dct))

tuple, listは*が一つ、dictは*が2つ必要。

この方法を使えば、関数の引数もlistなどで指定できる。

1
2
3
import os
lst = ('/tmp', 'test.txt')
print(os.path.join(*lst))

もっと早く知っておきたかった。

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

ちょっとしたフォーマッティング

ちょっとしたフォーマッティング

javaでログ出力をしていると、少しだけフォーマットしたいときがある。
テンプレートエンジンを利用する必要はない。
1行でパパっとフォーマッティングしたい。

例えば、処理件数を出力するとき、下記のように標準出力に処理中の件数をそのまま出力する。

1
2
3
4
5
6
7
8
9
long counter = 0;
for (Dto dto : dtoList) {
    logic(dto);
    counter++;
    if (counter % 1000 == 0) {
        System.out.println("処理中。" + counter);
    }
}
System.out.println("処理完了。" + counter);
1
2
3
4
5
6
7
8
9
10
11
12
13
処理中。1000
処理中。2000
処理中。3000
処理中。4000
処理中。5000
処理中。6000
処理中。7000
処理中。8000
処理中。9000
処理中。10000
処理中。11000
処理中。12000
処理完了。12345

たしかに、処理件数は出力される。
ここで、java.text.MessageFormatを利用すると、件数を3桁カンマ付きで出力できる。

1
2
3
4
5
6
7
8
9
10
11
import java.text.MessageFormat;

long counter = 0;
for (Dto dto : dtoList) {
    logic(dto);
    counter++;
    if (counter % 1000 == 0) {
        System.out.println(MessageFormat.format("処理中。{0}", counter));
    }
}
System.out.println(MessageFormat.format("処理完了。{0}", counter));
1
2
3
4
5
6
7
8
9
10
11
12
13
処理中。1,000
処理中。2,000
処理中。3,000
処理中。4,000
処理中。5,000
処理中。6,000
処理中。7,000
処理中。8,000
処理中。9,000
処理中。10,000
処理中。11,000
処理中。12,000
処理完了。12,345

String.formatを利用すると、出力桁を簡単に固定できるため、この方法も見通しが良い。

1
2
3
4
5
6
7
8
9
long counter = 0;
for (Dto dto : dtoList) {
    logic(dto);
    counter++;
    if (counter % 1000 == 0) {
        System.out.println(String.format("%7d 処理中。", counter));
    }
}
System.out.println(String.format("%7d 処理完了。", counter));
1
2
3
4
5
6
7
8
9
10
11
12
13
   1000 処理中。
   2000 処理中。
   3000 処理中。
   4000 処理中。
   5000 処理中。
   6000 処理中。
   7000 処理中。
   8000 処理中。
   9000 処理中。
  10000 処理中。
  11000 処理中。
  12000 処理中。
  12345 処理完了。

数字の桁数が合ったので、数字を前にした。

String.formatもMessageFormat.formatも、たくさんのフォーマットを持っているので、
すべてを覚えてもよいかもしれないが、やりたいことから覚えてもよいと思う。

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