2014-09-13 関西Kotlin勉強会
JAX-RSで使うアレとかソレをKotlinで書いてみよう!
- JSR 339
- The Java API for RESTful Web Services
- POJOにアノテーション付けて嬉しいHTTPの薄いやつ
-
名前受けとってこんにちは返すやつ
-
必要なクラスはふたつ
- Applicationのサブクラス
- リソースクラス
@ApplicationPath("rest")
public class HelloApplication extends Application {
}
@Path("hello")
public class HelloResource {
@Context
private UriInfo uriInfo;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayHello(
@QueryParam("name") @DefaultValue("world") String name) {
return String.format("Hello, %s!", name);
}
@Path("path")
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getPath() {
return uriInfo.getPath();
}
}
- セミコロン要らない
- アノテーションに@要らない
- importで別名付けられる
- 型推論効く
- メソッド一行で書ける
- 他にもいろいろ
ApplicationPath("rest")
public class HelloApplication : Application()
Path("hello")
public class HelloResource {
Context
var uriInfo : UriInfo? = null
GET
Produces(MediaType.TEXT_PLAIN)
fun sayHello(
//デフォルト引数は使えず、@DefaultValueを使うしかない
QueryParam("name") DefaultValue("world") name: String
): String = "Hello, $name!"
Path("path")
GET
Produces(MediaType.TEXT_PLAIN)
fun getPath(): String = uriInfo?.getPath()!!
}
コンストラクタインジェクションを使えば良い……?
Path("hello")
public class HelloResource(
Context val uriInfo : UriInfo
) {
GET
Produces(MediaType.TEXT_PLAIN)
fun sayHello(
QueryParam("name") DefaultValue("world") name: String
): String = "Hello, $name!"
Path("path")
GET
Produces(MediaType.TEXT_PLAIN)
fun getPath(): String? = uriInfo.getPath()
}
- コンストラクタ引数だけでなくフィールドにも@Contextが付いてしまう
- フィールドはfinalなのでフィールドインジェクションは出来ない
- でも@Contextが付いているのでインジェクション対象
- エラー乁( ˙ω˙ )厂
- フィールドには@Contextを付けないようにできるの?(trsn)
- JAX-RSはクエリパラメータやリクエストヘッダなどをオレオレクラスで受けとれる
- 詳しくはJAX-RSでパラメータの受け取り方をいろいろ試すを参照ください。
public class ValueObject1 {
private final String value;
public ValueObject1(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
class ValueObject1(val value: String)
data class ValueObject1(val value: String)
public class ValueObject2 {
private final String value;
private ValueObject2(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static ValueObject2 valueOf(String value) {
return new ValueObject2(value);
}
}
staticメソッドの定義の仕方が分からない_(:3」∠)_ (trsn)
public interface ValueObject3 {
String getValue();
}
public class ValueObject3Impl implements ValueObject3 {
private final String value;
public ValueObject3Impl(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}
public class ValueObject3Converter implements ParamConverter<ValueObject3> {
@Override
public ValueObject3 fromString(String value) {
return new ValueObject3Impl(value);
}
@Override
public String toString(ValueObject3 value) {
return value.getValue();
}
}
@Provider
public class ValueObject3ConverterProvider implements ParamConverterProvider {
@Override
public <T> ParamConverter<T> getConverter(Class<T> rawType,
Type genericType, Annotation[] annotations) {
if (rawType == ValueObject3.class) {
return (ParamConverter<T>) new ValueObject3Converter();
}
return null;
}
}
public trait ValueObject3 {
val value: String
}
public class ValueObject3Impl(
override val value: String
) : ValueObject3
public class ValueObject3Converter : ParamConverter<ValueObject3> {
override fun fromString(value: String?): ValueObject3 = ValueObject3Impl(value!!)
override fun toString(value: ValueObject3?): String = value!!.value
}
Provider
public class ValueObject3ConverterProvider : ParamConverterProvider {
override fun <T> getConverter(rawType: Class<T>?,
genericType: Type?, annotations: Array<out Annotation>?): ParamConverter<T>? {
if (rawType!! == javaClass<ValueObject3>()) {
return ValueObject3Converter() as ParamConverter<T>
}
return null
}
}
- Cookieを確認して認証してなかったら403返す
- 特定のクエリパラメータがあればPUTメソッドに変える
などができるやつ
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Greedy {
}
@Provider
@Greedy //最初に作ったアノテーション
public class GreedyFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
String paid = requestContext.getHeaderString("payment");
if (paid == null || Integer.parseInt(paid) < 1000) {
throw new WebApplicationException(Response.Status.PAYMENT_REQUIRED);
}
}
}
@Greedy //ここにもアノテーション。これでフィルターが適用される
@Path("CuteGirl")
public class CuteGirlResource {
@Path("Smile")
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getSmile() {
return "(๑•̀ᴗ-♡ॢ)";
}
}
NameBinding
Retention(RetentionPolicy.RUNTIME)
//アノテーションの要素に配列を設定する方法が分からない。(trsn)
//Target(array(ElementType.TYPE, ElementType.METHOD))
public annotation class Greedy
Provider
Greedy
public class GreedyFilter : ContainerRequestFilter {
override fun filter(requestContext: ContainerRequestContext?): Unit {
val paid = requestContext!!.getHeaderString("payment")
if (paid == null || paid.toInt() < 1000) {
throw WebApplicationException(Response.Status.PAYMENT_REQUIRED)
}
}
}
Greedy
Path("CuteGirl")
public class CuteGirlResource {
Path("Smile")
GET
Produces(MediaType.TEXT_PLAIN)
fun getSmile(): String = "(๑•̀ᴗ-♡ॢ)"
}
- JAX-RS関連のクラス定義するだけじゃあんまりKotlinの恩恵を受けられない
- でもガワじゃなくて中身を実装するときはKotlinは楽そうで良い
- JAX-RSはJava EEのわりに良い
- kotlincして出来たクラスファイルをjavapしてどんな感じか見る
public data class Point(val x: Int, val y: Int)
Compiled from "data-class.kt"
public final class Point implements kotlin.jvm.internal.KObject {
public static final kotlin.reflect.jvm.internal.KClassImpl $kotlinClass;
static {};
public final int getX();
public final int getY();
public Point(int, int);
public final int component1();
public final int component2();
public final Point copy(int, int);
public static Point copy$default(Point, int, int, int);
public java.lang.String toString();
public int hashCode();
public boolean equals(java.lang.Object);
}
val p1 = Point(2, 3)
val p2 = p1.copy(4)
val p3 = p2.copy(y = 5)
val (x, y) = p3
println("p1=$p1 p2=$p2 p3=$p3 x=$x y=$y")
// p1=Point(x=2, y=3) p2=Point(x=4, y=3) p3=Point(x=4, y=5) x=4 y=5
public class Hello {
fun say(name: String = "world") = "Hello, $name!"
}
Compiled from "default-parameter.kt"
public final class Hello implements kotlin.jvm.internal.KObject {
public static final kotlin.reflect.jvm.internal.KClassImpl $kotlinClass;
static {};
public final java.lang.String say(java.lang.String);
public static java.lang.String say$default(Hello, java.lang.String, int);
public Hello();
}
public static java.lang.String say$default(Hello, java.lang.String, int);
descriptor: (LHello;Ljava/lang/String;I)Ljava/lang/String;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: iload_2
2: iconst_1
3: iand
4: ifeq 10
7: ldc #48 // String world
9: astore_1
10: aload_1
11: invokevirtual #50 // Method say:(Ljava/lang/String;)Ljava/lang/String;
14: areturn
LineNumberTable:
line 2: 7
StackMapTable: number_of_entries = 1
frame_type = 74 /* same_locals_1_stack_item */
stack = [ class Hello ]
- aload_0 : 第1引数をプッシュする
- iload_2 : 第3引数をプッシュする
- iconst_1 : 定数1をプッシュする
- iand : ポップした2つのintの論理積をプッシュ
- ifeq : ポップしたintが0に等しければジャンプする
- ldc : コンスタントプールから取った項目をプッシュする
- astore_1 : ポップしたオブジェクトをローカル変数に入れる
- aload_1 : ローカル変数をプッシュする
- invokevirtual : インスタンスメソッドを実行する
- areturn : ポップしたオブジェクトを返す
- デフォルト値の要・不要は最後の引数との論理積で決まる
- intは32ビット
- フラグにすると32個分
- 33個の引数があるとどうなんの?
fun hoge(
a1: Int = 1,
a2: Int = 2,
a3: Int = 3,
a4: Int = 4,
a5: Int = 5,
a6: Int = 6,
a7: Int = 7,
a8: Int = 8,
a9: Int = 9,
a10: Int = 10,
a11: Int = 11,
a12: Int = 12,
a13: Int = 13,
a14: Int = 14,
a15: Int = 15,
a16: Int = 16,
a17: Int = 17,
a18: Int = 18,
a19: Int = 19,
a20: Int = 20,
a21: Int = 21,
a22: Int = 22,
a23: Int = 23,
a24: Int = 24,
a25: Int = 25,
a26: Int = 26,
a27: Int = 27,
a28: Int = 28,
a29: Int = 29,
a30: Int = 30,
a31: Int = 31,
a32: Int = 32,
a33: Int = 33) {
println( a1)
println( a2)
println( a3)
println( a4)
println( a5)
println( a6)
println( a7)
println( a8)
println( a9)
println(a10)
println(a11)
println(a12)
println(a13)
println(a14)
println(a15)
println(a16)
println(a17)
println(a18)
println(a19)
println(a20)
println(a21)
println(a22)
println(a23)
println(a24)
println(a25)
println(a26)
println(a27)
println(a28)
println(a29)
println(a30)
println(a31)
println(a32)
println(a33)
}
fun main(args: Array<String>) {
hoge(a1 = 100)
}
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
- 33も引数使うな
- NullableをMaybeモナドとみなしていくつかの関数を書いてモナド則を満たそう
- これを満たすとモナドである
- のかな?
- よく分からないけどたぶんそう
(return x) >>= f == f x
m >>= return == m
(m >>= f) >>= g == m >>= (\x -> f x >>= g)
// return
fun <T> some(t: T): T? = t
// >>=
fun <T, U> T?.flatMap(f: (T) -> U?): U? = if(this != null) f(this) else null
some(x).flatMap(f) == f(x)
m.flatMap { some(it) } == m
m.flatMap(f).flatMap(g) == m.flatMap { x -> f(x).flatMap(g) }
- Scalaのfor式に相当するものがないと嬉しさが少ない
//これはScala
val a = Some(2)
val b = Some(3)
val c = a.flatMap { x => b.map { y => x + y }}
//for式で書ける
val c = for {
x <- a
y <- b
} yield x + y
- たぶんKotlinにモナドは要らん
- Nullableは便利だけどなんかキモい
// flatMapとmapが同じコードで書けて変な感じ……
fun <T, U> T?.flatMap(f: (T) -> U?): U? = if(this != null) f(this) else null
fun <T, U> T?.map(f: (T) -> U): U? = if(this != null) f(this) else null
- JAX-RSは素敵
- 引数は32個以下でお願いします
- Nullableは奥が深そう
- 今回紹介したのはKotlinの能力のほんの一部
- なかなか実用的な気がした
- 公式リファレンスが結構充実している
- Kotlinの日本語の資料はほとんどたろうさん作
- アイドルにKotlinのこと聞いても何も返ってこない