1

What are the benefits of the following model, when non-generic sublasses have parent generic superclass/interface?

interface Generic<Q: Number, R: Number> {

    fun calculate(input: Q): R
}

class ClassA: Generic<Int, Int> {

    override fun calculate(input: Int): Int {
        return input
    }
}

class ClassB: Generic<Float, Float> {

    override fun calculate(input: Float): Float {
        return input
    }
}

Polymorphism is not available anyway (need explicit casting with pattern matching). Same result is possible to get without inheritance/implementation:

fun main() {
    val classA: Generic<Int, Int> = ClassA()
    val classB: Generic<Float, Float> = ClassB()
    val inputs: List<Number> = listOf(1, 1.1, 2, 2.2)

    for (input in inputs) {
        when (input) {
            is Int -> classA.calculate(input)
            is Float -> classB.calculate(input)
        }
    }
}
Eldar Agalarov
  • 4,849
  • 4
  • 30
  • 38
  • 2
    The main benefit of this model is that you only have to create one `Generic` interface instead of `IntCalculator`, `FloatCalculator` and `Calculator` interfaces. _P.S. I assume that you [program to an interface](https://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface), and therefore you have to create either `Generic` or `IntCalculator`, `FloatCalculator` and `Calculator`_. – IlyaMuravjov Nov 05 '19 at 17:59

1 Answers1

1

With your current set up, I would argue that having the Generic interface isn't completely necessary. However, as you extend your code, I think that it can be helpful.

One advantage of using that right now is that you now have two similar classes with consistent method names and similar method signatures. By inheriting Generic, it might save a typo from typing caluclate instead of calculate. Some may not consider this a valid reason, but I would argue that using that interface makes your code more consistent in this case.

The advantage of using Generic comes when you actually need to reference it.

interface Calculator<T : Number, K : Generic<T>> {
    fun calculate(generic: K): T
}
class DefaultCalculator...

The above code is just an example and is probably not practical in your use case, but if you think you might need it in the future, using the Generic interface now might save you some refactoring.

retodaredevil
  • 1,261
  • 1
  • 13
  • 20