覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

「社会は変えられる: 世界が憧れる日本へ」

「社会は変えられる: 世界が憧れる日本へ」
たまたまこの本を知る機会があり、読んでみました。

社会は変えられる: 世界が憧れる日本へ

社会は変えられる: 世界が憧れる日本へ

  • 作者:江崎禎英
  • 発売日: 2018/06/25
  • メディア: 単行本


内容紹介は以下のようになっています

超高齢社会を迎え、医療費・介護費の膨張には歯止めがかからず、今や世界に冠たる国民皆保険制度は風前の灯火。
ところが医療関係者や製薬企業などの“専門家"は、古い制度や体制に守られ、同時に縛られ、「沈みゆく豪華客船」の中での席取り合戦に終始するばかり。
この苦境を乗り切るため、現役官僚の著者は、社会・経済システムの見直しによる「生涯現役社会」の創設を説く。
社会全体が変わる中で初めて持続可能な社会保障制度の構築が可能になるという。
前途多難に違いないが、関係者がより広い視点から問題を捉えて行動することができれば、
誰一人切り捨てることなく国民皆保険制度を維持する道が見えてくると主張する。
著者は実際にこれまでも、業界内では「不可能」と考えられていた数々の課題に、“部外者"の視点から切り込み、改革を成し遂げてきた。
その経験から、絶望するのは、まだ早いと説く。著者が思い描くのは、次世代に残すべきこの国の未来であり、
世界が羨望と畏敬の念を持って見つめる「憧れの国」日本の姿だ。


目次は以下の通りです

  • 第一章: 問題の本質を問い直す
  • 第二章: 時代に合わなくなった社会保障制度
  • 第三章: 社会は変えられる! ー 時代に合わない「制度」、業界の「常識」への挑戦
  • 第四章: 世界が憧れる日本へ


以下は、導入となっている「はじめに」の部分からの一部抜粋です

先が見えない難しい状況に陥った時に、私たちは往々にしてこれまで通りのやり方を押し通そうとするか、
「仕方がない」と言ってなにもしないことを正当化してしまうものです。
ところが、一歩引いてより広い視座から全体を俯瞰できるかどうかで、その後の展開は大きく変わります。
対応策が見つからなかった課題でも、違った視点から眺めることで、思いがけないヒントが見つかるものです。
現在、日本の社会保障制度は聞き的な状況にあります。
・・・なかでも「年金」の問題は、将来自分が受け取るお金の話ですから、不安に感じる人も多いでしょう。
・・・実は「年金」よりも遥かに深刻な問題を抱えているのが、日本の医療を支える「国民皆保険制度」です。
誰もが当たり前のように利用している公的医療保険が危機的な状況にあります。
・・・

これら含めて日本の現在の医療に関わる制度を、一度乗船すればいつでも自由に最高の食事などを楽しむことができて目的地まで運んでくれる「豪華客船」に例えています。
ただし、この「豪華客船」は今のままでは沈みゆくことがほぼ確実です。
これは、この筆者が指摘しているだけではなく、多くの場面で課題として上がっており、間違いないことです。

筆者は、今一度日本の医療制度の問題点を整理して、どう対策を打つかというよりも、社会の変化に対してどう考え方や構造を変えるべきかといったところを主張しています。

特に、発症した病気を「治す」医療から「予防」や「管理」を基本とする医療へ転換する という主張は完全に同意します。


ただ、日本の医療制度の問題に対して、あまりに大きな変革が必要となるため、いくら主張されても本当にそんなことが可能なのかと思えてしまいます。
しかし、筆者は官僚としてこれまで多くの無理だとしか思えない課題に取り組み、なんとか解決へ導いてきた事例を三章で述べています。
正直、この章で少ない文章にまとめて書かれていますが、
筆者がいくつものとんでもない苦難へ立ち向い、ギリギリのところまで努力・実行することを諦めず、解決へ導いてきた話は驚きしかありません。

三章の内容を読んでいくつもの難題を解決したこの筆者の主張であれば、医療に関する各種変革に対する話も可能性はゼロではないと感じました。
そしてこの筆者が警鐘を鳴らす問題であるからこそ、日本の医療に関する問題が本当に深刻で、タイムリミットが迫っているとも思えました。


この本を読むべきターゲットは、基本的に日本人全員だと思います。
おおげさな言い方かもしれませんが、現代の日本人がこの筆者の主張を一旦理解することが重要な気がします。
(もちろん、単純な賛成だけではなく批判も出てくると思いますが、とにかくこの本で分かりやすく書かれている現在の日本の課題を理解すべきだと思います)

Kotlin - 簡単なラムダ式を試す

ラムダ式(lambda exoression)または単にラムダ(lambda)とは、本質的には他の関数に渡すことが可能なコードの断片です。
ラムダを利用することで、共通のコード構造を抜き出してライブラリ関数へ渡すこことが可能です。
Kotlin の標準ライブラリはラムダを多用していて、ラムダの最も一般的な利用用途はコレクションの操作です。


以下でKotlinのコードで簡単なコレクションの操作を行ってみます。

data class Product(val name: String, val price: Int)

上記クラスのインスタンスを複数詰め込んだリスト内から最も高い価格(price)のものを見つけるという処理を行います。

サンプルコードで対象のリストは以下のものとします

val products = listOf(Product("A", 10), Product("B", 1), Product("C", 1000))


命令的に、この処理を素直に記述すると以下のようになります。

fun findHighestProduct(products: List<Product>): Product? {
    var maxPrice = 0
    var highestProduct: Product? = null

    for (product in products) {
        if (product.price > maxPrice) {
            maxPrice = product.price
            highestProduct = product
        }
    }

    return highestProduct
}

実行コードは以下のよになります

val products = listOf(Product("A", 10), Product("B", 1), Product("C", 1000))
val highestProduct1 = findHighestProduct(products)  // -> Product(name=C, price=1000)


ラムダ式

Kotlin ではコレクションに対して maxBy 関数を利用することが可能です。
引数には、最大要素を見つけるために比較する値を指定する関数を指定する必要があります。

以下のように記述できます。波括弧内のコードはラムダ式であり、関数の引数として渡します。

val highestProduct = products.maxBy({p: Product -> p.price})

この記述はもっと簡単な記述にできます。
Kotlin では構文規約でラムダ式が関数呼び出しの最終引数であれば括弧の外に移動することが可能です。
この規約を利用すると以下のように記述できます

val highestProduct = products.maxBy() {p: Product -> p.price}

ラムダが関数の唯一の引数であれば、関数呼び出しから空の括弧を取り除いて記述できます。そうすると以下のようになります。

val highestProduct = products.maxBy {p: Product -> p.price}

上記3つの構文は同じことを意味しています。


ラムダの引数の型を推論できる場合は、明示的な指定は不要です。maxBy関数では、その引数の型は常にコレクションの要素の型と同じです。
そのため以下のように型を省いて記述することもできます。

val highestProduct = products.maxBy {p -> p.price}


ちなみにさらなる短絡構文で it を利用して以下のように記述することもできます

val highestProduct = products.maxBy { it.price }

it を用いてコレクション要素を引数として受け取っています。


今回比較する値は price であり、この値は price プロパティに保持されています。
ラムダが単に関数やプロパティを呼び出すだけの場合は、ラムダはメンバ参照を用いて記述することも可能です。

val highestProduct = products.maxBy(Product::price)


まとめ

今回は、Kotlinにおけるコレクションの簡単な操作をラムダ式を使って試してみました。

Kotlin - オブジェクト式

Kotlin の object キーワードは、無名オブジェクト(anonymous object)の宣言のためにも使用できます。
無名オブジェクト は Javaにおける無名内部クラスを置き換えるものとなります。

Javaで無名内部クラスの典型的な利用シーンというと、イベントリストが必要となるケースがあります。

例えば以下のようなJavaのコードがあったとした場合(import 等もろもろ省略)

JWindow window = new JWindow();
window.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("Mouse Clicked");
    }

    @Override
    public void mousePressed(MouseEvent e) {
        System.out.println("Mouse Pressed");
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        System.out.println("Mouse Released");
    }
});

上記コードは Kotlin ではオブジェクト式で以下のように記述できます。

val window = JWindow()
window.addMouseListener(
    object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            println("Mouse Clicked")
        }

        override fun mousePressed(e: MouseEvent) {
            println("Mouse Pressed")
        }

        override fun mouseReleased(e: MouseEvent) {
            println("Mouse Released")
        }
    }
)

オブジェクトの名前を省略しているという点をを除いて、オブジェクト宣言と同じです。
オブジェクト式はクラスを宣言し、そのクラスのインスンスを生成しています。


Javaの無名クラスと同様にオブジェクト式の中のコードは、それが生成された関数内の変数を参照することができます。
しかし、Javaとは違って、その変数がfinalでなければならないという制約はありません。
そのため、オブジェクト式の中から変数の値を変更することも可能です。

以下のようなコードを書くことも可能です

var count = 0
val window = JWindow()
window.addMouseListener(
    object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            println("Mouse Clicked")
            count++
        }

        override fun mousePressed(e: MouseEvent) {
            println("Mouse Pressed")
            count++
        }

        override fun mouseReleased(e: MouseEvent) {
            println("Mouse Released")
            count++
        }
    }
)


まとめ

オブジェクト式により Java の無名内部クラスを置き換えることができることがわかりました。

Kotolin - コンパニオンオブジェクト 2

Kotlin のコンパニオンオブジェクトは、クラス内に宣言された通常のオブジェクトであり、
名前を付けたり、インターフェースを実装したり、拡張関数やプロパティを持つこともできます。


コンパニオンオブジェクトに名前を付ける

前回のエントリですでにやってやってますが、コンパニオンオブジェクトに名前を付けることが可能です。

import com.google.gson.Gson

class Person(val name: String, val age: Int) {
    // 名前付きのコンパニオンオブジェクトの宣言
    companion object Decoder {
        fun fromJSON(jsonStr: String): Person {
            return Gson().fromJson(jsonStr, Person::class.java)
        }
    }
}

呼び出し側のコードは以下の通りで、コンパニオンオブジェクトの名前を明示することも省略することも可能です。

// コンパニオンオブジェクトの名前を指定して関数を呼び出せる
val p1 = Person.Decoder.fromJSON("""{"name": "Kotlin Taro", "age": 32}""")
println("${p1.name} is ${p1.age} years old.")  // -> Kotlin Taro is 32 years old.

// コンパニオンオブジェクトの名前を省略することもできる
val p2 = Person.fromJSON("""{"name": "Java Taro", "age": 39}""")
println("${p2.name} is ${p2.age} years old.")  // -> Java Taro is 39 years old.


コンパニオンオブジェクトでインターフェースを実装する

他のオブジェクト宣言と同じくコンパニオンオブジェクトはインターフェースを実装できます。
インターフェースを実装しているオブジェクトのインスタンスとして、そのオブジェクトを含むクラスの名前を使用できます。

JSONFactory というインターフェースを介してオブジェクトが生成されるものとする場合、
以下のようなサンプルコードを書くことができます。

import com.google.gson.Gson

interface JSONFactory<T> {
    fun fromJSON(jsonStr: String): T
}

class Person(val name: String, val age: Int) {
    // 名前付きのコンパニオンオブジェクトの宣言
    companion object : JSONFactory<Person> {
        override fun fromJSON(jsonStr: String): Person {
            return Gson().fromJson(jsonStr, Person::class.java)
        }
    }
}


JSONFactoryを引数に取る関数を定義し多とした場合、
その関数を呼び出す場合には、JSONFactoryをコンパニオンオブジェクトが実装しているので Person オブジェクトを引数に渡すことが可能です。

fun <T> loadFromJSON(jsonStr: String, factory: JSONFactory<T>): T {
    return factory.fromJSON(jsonStr)
}

fun main() {
    // JSONFactoryインターフェースの
    val p1 = loadFromJSON("""{"name": "Java Taro", "age": 45}""", Person) // コンパニオンオブジェクトのインスタンスを関数に渡す
    println("${p1.name} is ${p1.age} years old.")
}


コンパニオンオブジェクトの拡張関数

クラスがコンパニオンオブジェクトを持っていれば、コンパニオンオブジェクトに対して拡張関数を定義することが実現できます。
例えば ClazzA がコンパニオンオブジェクトを持つ時、ClazzA.Companion に対する拡張関数 function を定義したと仮定すると、
その関数を ClazzA.function() として呼び出すことができます。


すでに定義した Person クラスは、そのドメインに特化したビジネスロジックのみを持つものとしておきたい場合に、
データのシリアライズやデシリアライズのルールは分離しておきたい(Personの核となるドメインとは別にしておきたい)ことがあります。
このような場合に拡張関数を利用すると実現可能です。

import com.google.gson.Gson

// domain core module
class Person(val name: String, val age: Int) {
    // 空のコンパニオンオブジェクトを宣言しておく
    companion object {
    }
}

// client / server communication module
fun Person.Companion.fromJSON(jsonStr: String): Person {
    return Gson().fromJson(jsonStr, Person::class.java)
}

以下のように実行可能です。

val p1 = Person.fromJSON("""{"name": "Java Taro", "age": 45}""")
println("${p1.name} is ${p1.age} years old.")


まとめ

今回は、Kotlin のコンパニオンオブジェクトへの名前付け、インターフェースの実装、拡張関数の定義が可能であることを確認しました。



関連エントリ

Kotlin - コンパニオンオブジェクト

Javaと異なり Kotlin のクラスは静的なメンバを持つことができません。Javaにおける static キーワードは Kotlin には含まれていません。

この Java における static の代替手段として以下を利用します。

  • トップレベル関数関数
    • 多くの状況で Java の staticメソッドを置き換えることが可能
  • オブジェクト宣言
    • Javaの静的フィールドと静的メソッドの置き換えをします

多くの状況では トップレベル関数 を使うことが推奨されているようです。
しかし、トップレベル関数は、クラスのprivateメンバにアクセスできないという制約が存在します。
クラスのインスタンスなしで、クラス内部にアクセスする必要がある関数を記述する必要がある場合、
対象クラスの内部のオブジェクト宣言のメンバとして関数を書くことが可能です。


クラス内で定義されているオブジェクトの1つに companion というキーワードを付けることが可能です。
このキーワードを付与することで、オブジェクトの名前を明示せずに、クラス名を使って直接そのオブジェクトのメソッド名やプロパティにアクセス可能となります。
Javaにおける静的メソッド呼び出しと全く同じ状態となります。

class Printer {
    companion object {
        fun print(data : String) {
            println("Printer printed... [$data]")
        }
    }
}

呼び出し側のコードは以下のようになります

Printer.print("Kotlin")  // -> Printer printed... [Kotlin]


companion のキーワードを付ける際にどこまで意味があるのか不明ですが、オブジェクト宣言で名前を指定することも可能です

class Printer {
    companion object Java {
        fun print(data : String) {
            println("Printer printed... [$data]")
        }
    }
}

呼び出し側のコードでは companionを付与したオブジェクト宣言の名前を経由してもしなくても内部の関数にアクセス可能です

Printer.print("Kotlin")  // -> Printer printed... [Kotlin]
Printer.Java.print("Java Lang")  // -> Printer printed... [Java Lang]


このコンパニオンオブジェクトの利用の好例が ファクトリメソッド(Javaでいうところのstaticファクトリ) になると思われます。

Javaでも同じですが複数のコンストラクタを持つクラスの場合、コンストラクタには名前を付けることができないためコード上の意味づけがしにくくなります。
そういった場合にはファクトリメソッド(staticファクトリ)を用意して、意味のある名前を付けます。


Kotlin で以下のような2つのセカンダリコンストラクタを持つクラスが存在したとします。

class User {
    val userName : String
    
    constructor(handleName: String) {
        this.userName = handleName
    }

    constructor(firstName: String, lastName: String) {
        this.userName = "$firstName-$lastName"
    }
}


これはコンパニオンオブジェクトを利用して以下のように書き換え可能です。

class User {
    val userName : String

    private constructor(handleName: String) {
        this.userName = handleName
    }

    private constructor(firstName: String, lastName: String) {
        this.userName = "$firstName-$lastName"
    }

    companion object {
        fun handleNamUser(handleName: String) =
                User(handleName)

        fun fullNameUser(firstName: String, lastName: String) =
            User(firstName, lastName)
    }
}


呼び出しコードは以下のようになります

val user1 = User.handleNamUser("handle-001")
println(user1.userName)  // -> handle-001

val user2 = User.fullNameUser("Taro", "Kotlin")
println(user2.userName)  // -> Taro-Kotlin


まとめ

サンプルコードとしては微妙だった気もしますが、コンパニオンオブジェクトによってファクトリメソッド(staticファクトリ)が実現できることがわかりました。



関連エントリ