카테고리 없음

[Kotlin] Calculator without Overloading

Jun.LEE 2024. 3. 5. 19:08

 

Primitive type의 변수 2개를 변수로 연산하는 binary operation을 만들어 보려고 한다.

코틀린 Int의 super class인 Number class를 찾아가 보면 다음과 같다.

public abstract class Number {
    public abstract fun toDouble(): Double
    public abstract fun toFloat(): Float
    public abstract fun toLong(): Long
    public abstract fun toInt(): Int
    public abstract fun toShort(): Short
    public abstract fun toByte(): Byte
    // Char는 고려하지 않음
}

 

연산의 변수를 명시적으로 작성해주려면 각 연산마다 15개의 Overloading한 함수가 있어야 하는 것이다.

변수를 모두 Double로 캐스팅하여 연산하는 방법이 있긴 하지만 다른 방법을 사용하고 싶었다.

 

그래서 다음과 같이 진행했다.

fun <A: Number, B: Number> binaryOp(a: A, b: B): Number { }

 

Primitives class의 super class인 Number로 제한한 Generic한 방식으로 변수와 리턴 타입을 지정해 주었다.

 

함수를 마무리하기 전 확인해 볼 것이 있다.

@kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Byte): Long

    /** Adds the other value to this value. */
    @kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Short): Long

    /** Adds the other value to this value. */
    @kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Int): Long

    /** Adds the other value to this value. */
    @kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Long): Long

    /** Adds the other value to this value. */
    @kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Float): Float

    /** Adds the other value to this value. */
    @kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Double): Double

 

위 코드는 kotlin.Long의 plus들을 그대로 복사해 온 자료이다.

확인해 보면 정수들은 서로 다른 클래스일 경우에는 사이즈가 더 큰 자료형에 담아 리턴하게 된다.

그리고 실수와 정수를 연산한 경우에는 실수 타입에 담아 리턴하게 된다.

 

따라서 일종의 Hierarchy를 생각해 볼 수 있었다. " Double ≥ Float ≥ Long ≥ Int ≥ Short ≥ Byte "

companion object {
    val numberClass = listOf(
        Double::class, Float::class, Long::class,
        Int::class, Short::class, Byte::class
    )
}

 

 

따라서 순서에 맞게 넣어준 리스트를 하나 생성하고

fun <A: Number, B: Number> addition(a: A, b: B): Number {
    return when(numberClass.indexOfFirst { it == a::class || it == b::class }) {
        0 -> a.toDouble() + b.toDouble()
        1 -> a.toFloat() + b.toFloat()
        2 -> a.toLong() + b.toLong()
        3 -> a.toInt() + b.toInt()
        4 -> a.toShort() + b.toShort()
        5 -> a.toByte() + b.toByte()
        else -> throw IllegalArgumentException("Parameter class: Wrong")
    }
}

 

위와 같이 작성해 주었다. 더 높은 hierarchy에 맞추어서 각 변수를 캐스팅하고 연산 후에 리턴하게 된다.

 

 

 

잘 작동하는 듯하다.

 

이후에 저 조건문도 줄여주고 싶어 다음시도를 했다.

fun <A: Number, B: Number> addition(a: A, b: B): Number {
    var targetClass = numberClass.first { it == a::class || it == b::class }
    return a::class.functions.find {
        it.name == "plus" && it.returnType == targetClass.createType()
    }!!.call() as Number
}

 

위처럼 reflection을 통해 해당 타입에 맞는 overloading 된 plus 함수를 가져와서 연산해주고 했으나 실패했다.

 

일단 Primitive의 경우에는 constructor가 private 했고 해당 값이라고 예상했던 Field value도 가져올 수 없는 것 같았다.

 

이후 시도에 성공하게 되면 업로드할 예정이다.