[Kotlin] Calculator without Overloading
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도 가져올 수 없는 것 같았다.
이후 시도에 성공하게 되면 업로드할 예정이다.