Object-oriented programming

Hello, I can not understand thing with override. I have such interface:

interface ArithmeticOperations{
    fun addition(num: Number): Double
    fun subtraction(num: Number): Double
    fun multiplication(num: Number): Double
    fun division(num: Number): Double
}

I try to create class:

class Fractions(var integerPart: Long, var fractionalPart: Short) : ArithmeticOperations{

override fun addition(otherNum: Fractions): Double {
    var result: Double = 0.0
    result = abs(fractionalPart.toDouble()) + abs(otherNum.fractionalPart.toDouble())
    return (abs(fractionalPart.toDouble()) + abs(otherNum.fractionalPart.toDouble()) +
            integerPart.toDouble() + fractionalPart.toDouble())

}

}

And when i try override addition and add argument he says that function overrides nothing. Is there any sense in sush interface or it is more simple to just make those functions in class without interface? The purpoce of interface was that there is many types of numbers and operations for each is the same but type and some details are different and because of that we need to override functions.

Thanks in advance for every answer!)

This is the double-dispatch problem: The function to be invoked depends on the types of 2 variables instead of 1. But first an explanation of why your code does not compile.

addition(...) in Factions overrides nothing because its signature differs from the signature in the interface:

  • ArithmeticOperations: (Number) ->Double
  • Fractions: (Fractions) -> Double

Fractions will have to implement addition(Number). You can still add the overload addition(Fractions) if you want. The overload might be able to do things more efficiently because it handles 2 Fractions.

So how do you handle a situation like this? There are multiple possibilities:

  • You check the type of Number in each implementation of an arithmetic method. For example:
    when (otherNum) {
        is Fractions -> addFraction(otherNum)
        is OtherType -> ...
        is YetAnotherType -> ...
        else -> // Some default implementation or an exception
    }
    
  • Use the visitor pattern.
  • Simply convert all otherNums to a Double, and use that to do the calculation.

I am sure there are others.

2 Likes

As @jstuyts said, you can’t override a function with a different parameter type. As for algebra operations, you can check a working implementation of the idea here: https://github.com/mipt-npm/kmath/blob/dev/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt. You can use kmath as a base design for algebra implementation. It works pretty good so far. Of course some advanced features require KEEP-176, but we can live without it.

1 Like

All discussions are welcome in the slack #mathematics channel.

A better solution might be to subclass the Number type. Then you can pass your Fraction class to any function that expects a Number.

Playground link

data class Fraction(val divisor: Long, val dividend: Short): Number() {
    override fun toByte() = (divisor / dividend).toByte()
    override fun toChar() = (divisor / dividend).toChar()
    override fun toShort() = (divisor / dividend).toShort()
    override fun toInt() = (divisor / dividend).toInt()
    override fun toLong() = divisor / dividend

    override fun toFloat() = divisor.toFloat() / dividend
    override fun toDouble() = divisor.toDouble() / dividend
}

I like the idea, but to be honest I can’t think of any function that expects Number as a parameter. The fact is that Number is simply not usefull except for casting it to any of the other “base numbers” and in that case you can just take Int or Double as an argument.

You would have to implement them yourself, like I implemented * on the playground:

operator fun times(rhs: Number): Number = when {
    rhs is Frac -> Frac(this.divisor * rhs.divisor, (this.dividend * rhs.dividend).toShort())
    rhs is Long -> Frac(this.divisor * rhs, this.dividend)
    else        -> TODO()
}

It would give you conversion to a Number but not specific operations on fractions. When you will multiply fraction by fraction, which operations will you use?

It will work if fraction is on the left side, but if the number is on the left side? You also have a perfect chance of violating a*b = b*a condition if you start adding custom operations to anything. This is exactly why no operations are defined on numbers.