【Kotlin練習問題】演算子のオーバーロード:応用編

演算子のオーバーロードとは

演算子のオーバーロード:比較演算子で演算子をオーバーロードする方法について解説しました。

復習のために簡単に説明すると、

自分で作ったクラスなどで演算子をオーバーロードすることで、+、-、*、/といった演算子をそのクラスのインスタンスに直接使用することができるようになります。

もっと具体的に言うと、1+1はInt型同士の演算なので当たり前のように2という数値が計算できますが、自分で作ったclass MyAge(val age: Int)というクラスがあったとして、

fun main() {
    class MyAge(val age: Int)
    val age = MyAge(20)
    
    print("あなたは5年後" + (age + 5) + "歳です")
 //コンパイルエラー    
}

上のコードはMyAgeクラスで+演算子をオーバーロードしていないのにageに対して使ってしまっているので、コンパイルエラーがおきてしまいます。下のコードでは+演算子をオーバーロードしているので、25歳が出力されます。

fun main() {
    class MyAge(val age: Int)
    val age = MyAge(20)
    operator fun MyAge.plus(num: Int) =  //+演算子のオーバーロード
    	this.age + num
    
    print("あなたは5年後" + (age + 5) + "歳です")
    
}

二項演算子の対応リスト

a+bやa-bのように、二つの値を計算させる際に使われる演算子を二項演算子と呼びます。

Kotlinで二項演算子をオーバーライドするには以下の表で対応している関数をオーバーライドすることで、その演算子が独自クラスでも機能するようになります。

コンパイル時に使われる関数
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b)a.mod(b) (非推奨)
a..ba.rangeTo(b)

上のコードにもあるように、+という演算子をオーバーロードするには実際にはplusという関数を実装するようになります。

問題

今回の問題は少し難易度高めです。

以下のコードを日付の計算をできるようにしましょう。dateに対して、年・週・日の加算に対応させましょう。

例えばdate + YEAR * 2 + WEEK * 3 + DAY * 15のようにコードを書けるようになります。

①はじめにMyDateクラスにTimeIntervalを引数として取るplus()拡張関数を追加してください。
拡張関数の実装にはDateUtil.ktに宣言されているMyDate.addTimeIntervals()関数を使用してください。

②その後、TimeIntervalを乗算(*)もできるようにしてください。新しいクラスを追加する必要があります。

問題コード:

import TimeInterval.*

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)

// 日付に追加される期間を表すenumクラス
enum class TimeInterval { DAY, WEEK, YEAR }

operator fun MyDate.plus(timeInterval: TimeInterval): MyDate = TODO()

fun task1(today: MyDate): MyDate {
    return today + YEAR + WEEK
}

fun task2(today: MyDate): MyDate {
    TODO("乗算に対応したらコメント解除してください") //return today + YEAR * 2 + WEEK * 3 + DAY * 5
}

DateUtil.kt:

import java.util.Calendar
​
/*
 * 与えられたインターバルを追加した日付を返すメソッド
 * インターバルは与えられた数の日数、週数、年数で指定されます
 * 使い方:
 * 'date.addTimeIntervals(TimeInterval.DAY, 4)'
 * 'date.addTimeIntervals(TimeInterval.WEEK, 3)'
 */
fun MyDate.addTimeIntervals(timeInterval: TimeInterval, amount: Int): MyDate {
    val c = Calendar.getInstance()
    c.set(year + if (timeInterval == TimeInterval.YEAR) amount else 0, month, dayOfMonth)
    var timeInMillis = c.timeInMillis
    val millisecondsInADay = 24 * 60 * 60 * 1000L
    timeInMillis += amount * when (timeInterval) {
        TimeInterval.DAY -> millisecondsInADay
        TimeInterval.WEEK -> 7 * millisecondsInADay
        TimeInterval.YEAR -> 0L
    }
    val result = Calendar.getInstance()
    result.timeInMillis = timeInMillis
    return MyDate(result.get(Calendar.YEAR), result.get(Calendar.MONTH), result.get(Calendar.DATE))
}
[expander_maker id=”1″ more=”ヒント” less=”非表示”]

ヒント

問題①で変更すべきコードは

operator fun MyDate.plus(timeInterval: TimeInterval): MyDate = TODO()

ですが、これは単純にtimeIntervalとして与えられた値(日、週、年のいずれか)を一つ分足す処理です。DAYであれば一日、WEEKであれば一週、YEARであれば一年足されます。日付計算の複雑な処理はDateUtil.ktのaddTimeIntervalsに既に実装されているので、まずは、このメソッドの使い方をよく確認してください。

②の問題は以下の三つの手順で実装します。

  1. TimeIntervalの乗算に対応するために、まずはそのTimeIntervalが何個分なのかという値を保持する新クラスを作成する。
  2. TimeInterval.timesをオーバーライドし、乗算する値を含む先ほど作成したクラスのインスタンスを返すようにする。
  3. 問題①のMyDate.plusはTimeIntervalクラスしか引数にとらないので、自分で作成したクラスに対応したMyDate.plusを新しくオーバーライドする。
[/expander_maker] [expander_maker id=”1″ more=”答え” less=”非表示”]

答え

import TimeInterval.*

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)

// 日付に追加される期間を表すenumクラス
enum class TimeInterval { DAY, WEEK, YEAR }

operator fun MyDate.plus(timeInterval: TimeInterval) =
        addTimeIntervals(timeInterval, 1)

class RepeatedTimeInterval(val timeInterval: TimeInterval, val number: Int)

operator fun TimeInterval.times(number: Int) =
        RepeatedTimeInterval(this, number)

operator fun MyDate.plus(timeIntervals: RepeatedTimeInterval) =
        addTimeIntervals(timeIntervals.timeInterval, timeIntervals.number)

fun task1(today: MyDate): MyDate {
    return today + YEAR + WEEK
}

fun task2(today: MyDate): MyDate {
    return today + YEAR * 2 + WEEK * 3 + DAY * 5
}

[/expander_maker]

その他の問題はこちらからどうぞ。

完全無料で通えるプログラミングスクール

プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。

効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。

中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!

https://codelabsjp.net/best-programming-school/

おすすめ書籍

Kotlinの文法をまず学びたい!という方には以下の書籍がおすすめです。Kotlinは日本語書籍がまだ豊富とは言えない状況ですが、細かく解説されており、Kotlin入門者のかたでもつまずくことなく学習できると思います。

[itemlink post_id=”1743″]

実際にアプリを作りながら覚えていきたい!という方には以下もお勧めです。はじめに上の書籍で文法をさらっと学んでから取り組むのがお勧めです。

[itemlink post_id=”1745″]