インドカレーファンクラブ

パソコン、カメラ

C#でASP.NETばっか書いてた後にサーバサイドKotlinを使い始めて半年の感想

時系列順に

  • C#(ASP.NET): 3 年と半年くらい
  • Rails と Go(echo): 半年だけ
  • C#(ASP.NET): 半年
  • Kotlin(SpringBoot): 半年 #now

Rails と Go はあんまり好きじゃなかったので僕の母なる言語は C#にあると言っていい

ということで C#(ASP.NET)から Kotlin(SpringBoot)に移った感想を数年後にしみじみと楽しむために書いておく
IDE は VisualStudio から IntelliJ

リプレイスしたわけじゃなくて、転職しただけなのでそういう話は特にないし、
サーバサイドKotlinの話といいつつデプロイとかその辺作り込んでないからJVM言語だぜ!って話もない

因みに C#で使っていたバージョンは主に C# 7.3 で基本的に.NET Framework を使っていた...
.NET Core と C# 8.0 以降はほぼ趣味でしか触っていない

https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-version-history

よいこと

data class が便利

data class で immutable に作れるので健全
便利な copy だの equal もついてくる

Kotlin

data class User(val id: Int, val name: String, private val hoge: String) {
    fun show() = println("$id $name $hoge")
}

fun main(args: Array<String>) {
    val user = User(1, "John", "xxx")
    val user2 = user.copy(name = "Bob")
    user.show()  // 1 John xxx
    user2.show() // 1 Bob xxx
}

C#
なげ〜〜!

public class User {
    public int Id { get; private set; }
    public string Name { get; private set; }
    private string Hoge { get; set; }

    public User(int id, string name, string hoge) {
        Id = id;
        Name = name;
        Hoge = hoge;
    }

    public void Show() => System.Diagnostics.Debug.WriteLine($"{Id} {Name} {Hoge}");

    public User Copy(int? id = null, string name = null, string hoge = null) {
        var tmp = (User)MemberwiseClone();
        tmp.Id = id ?? Id;
        tmp.Name = (name is null) ? Name : name;
        tmp.Hoge = (hoge is null) ? Hoge : hoge;
        return tmp;
    }
}

class Program {
    static void Main(string[] args) {
        var user = new User(1, "John", "xxx");
        var user2 = user.Copy(name: "Bob");
        user.Show();
        user2.Show();
    }
}

プライマリコンストラク

最初戸惑った(後述)けど、慣れると楽

Kotlin

class User constructor(_id: Int, _name: String) {
    val id: Int
    val name: String
    init {
        id = _id
        name = _name
    }
}

// ↓省略したやつ

class User(val id: Int, val name: String)

C#
安心する長さ

public class User {
    public int Id { get; private set; }
    public string Name { get; private set; }

    public User(int id, string name) {
        Id = id;
        Name = name;
    }

NULL 安全

すごく健全に書ける
大変好ましい

Kotlin

val user: User? = null
// val user2: User = null // error!

// userがnullじゃない場合だけprint
user?.let {
    println(it.name)
}

// userがnullならエラー
println(user?.name ?: throw Exception())

C#

User user = null;

// userがnullじゃない場合だけprint
if (user is not null) System.Diagnostics.Debug.WriteLine(user.Name);

// userがnullならエラー(ついでにnameも見る)
if (user is not null && user.Name is not null) {
    System.Diagnostics.Debug.WriteLine(user.Name);
} else {
    throw new Exception();
}

スコープ関数が便利

https://kotlinlang.org/docs/scope-functions.html

便利だしかっこいいと思うんだけど、会社では使わなくていいとして let 以外使わせて貰えない
(下手に使うとややこしくなるものは確かに多いけど...)

なんでもかんでも式

便利

Kotlin

// 三項演算子はないのでそこだけちょっと寂しく感じる
val x = if (true) { "ttt "} else { "fff" }

val user: User? = null
val y = user?.let { it.name + "さん" }

C#も 8.0 からは switch でそういうことできるようになってた

https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-8#switch-expressions

when がなんだか便利

C#の switch よりなんか色々できる

https://kotlinlang.org/docs/control-flow.html#when-expression

Kotlin

val value = 10
val z = when {
    value !is Int -> throw Exception()
    value > 10 -> "10以上"
    value == 10 -> "ぴったり10"
    else -> "10より小さい"
}

LINQ っぽいのもちゃんとある

ここが大変わかりやすい

LINQ to Objects と Java8-Stream API と Kotlin の対応表 - Qiita

記載されている通り、素の状態だと kotlin.collections は LINQ っていうか Enumerable みたいに遅延でアレしてくれない

.asSequence()する必要がある

KotlinのListとSequenceって何が違うの? - Qiita

interface に既定の実装書ける

C#でも 8.0 から書けるのであんまり違いはない

https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-8#default-interface-methods

拡張メソッドもある

https://kotlinlang.org/docs/extensions.html

Kotlin

data class User(val id: Int, val name: String)

class Util {
    companion object {
        fun User.callName() = println(name + "さん!!")
    }
}

fun main(args: Array<String>) {
    val user = User(1, "Taro")
    user.callName()
}

C#

public class User {
    public int Id { get; private set; }
    public string Name { get; private set; }

    public User(int id, string name) {
        Id = id;
        Name = name;
    }
}

public static class Util
{
    public static void CallName(this User user)
        => System.Diagnostics.Debug.WriteLine(user.Name + "さん!!");
}

class Program
{
    static void Main(string[] args)
    {
        User user = new User(1, "xxx");
        user.CallName();
    }
}

Enum がなんだかすごく便利

すごく便利!!

https://kotlinlang.org/docs/enum-classes.html

enum class Dog(val id: Int, val japaneseName: String, val origin: String) {
    SHIBA(1, "柴犬", "日本"),
    GOLDEN_RETRIEVER(2, "ゴールデンレトリバー", "?" ),
    SAMOYED(3, "サモエド", "ロシア");

    fun show() = println("$id $japaneseName $origin")
}

fun main(args: Array<String>) {
    val dog = Dog.values().first { it.japaneseName == "柴犬" }
    dog.show() // 1 柴犬 日本

    val dog2 = Dog.valueOf("GOLDEN_RETRIEVER")
    dog2.show() // 2 ゴールデンレトリバー ?
}

C#
めんどくさいので端折った

 public enum Dog: int {
    SHIBA = 1,
    GOLDEN_RETRIEVER = 2,
    SAMOYED = 3,
}

public static class DogUtil {
    public static void Show(this Dog dog) {
        // 本当はDisplay attributeとかでいい感じに...
        object result = dog switch
        {
            Dog.SHIBA => "1 柴犬 日本",
            Dog.GOLDEN_RETRIEVER => "2 ゴールデンレトリバー ?",
            Dog.SAMOYED => "3 サモエド ロシア",
            _ => throw new NotImplementedException(),
        };
        System.Diagnostics.Debug.WriteLine(result);
}

class Program
{
    static void Main(string[] args)
    {
        // kotlinのdog相当はDisplay attributeとかでいい感じに...
        var dog2 = Enum.GetValues(typeof(Dog)).Cast<Dog>().First(x => x.ToString() == "GOLDEN_RETRIEVER");
        dog2.Show();
    }
}

SpringBoot 自体ではそんなに困らない

そんなに使いにくいとかは感じない
コアな実装をそんなしていないから気づかないだけかも...

開発者を集めやすいっぽい

Java みたいなもんだしね

混乱したこと

コンストラクタと init

最初はすごく混乱する

Kotlin - コンストラクタ - 覚えたら書く

return 書かなくてもいい

inline function の reified

いまだにこれが必要な仕組みがよく分かっていない
型引数をあれこれして難しいことをしようとするときに登場する

https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters

好きじゃないこと

gradle がマジで意味分からん

Kotlin の言語仕様イイヨネとかいう以上に gradle が嫌いで僕はまだ C#の方が好き

よくわからんエラー吐くし... 依存性の解決面倒くさすぎるし... IntelliJ との噛み合わせも完璧というわけではないし...

その点 C#では全部 MS が作ってたからその辺の気持ち悪さは一切なかった
Nuget 返して

でも gradle はビルドということでなんでもかんでも出来るのは便利!
とはいえ個人的にはライブラリ周りとビルド周りは別のモジュールとして管理された方が親切じゃない?!と思っている...

public がデフォルト

やだ~

総評

Kotlin 自体は C#よりも書いていて楽しい

でも gradle が苦行で辛い
僕は Rails を恐怖・忌避しているけど、gradle への気持ちの大きさはそれに比肩する

今後の課題

  • gradle を渋々勉強する
  • 難しいコアな実装をする
    • 認証とかエラーハンドリングとか CSRF とかそういうの