How to use generic function inside interface and implementing in a class

Im playing with Generics in order to understand how they work.

Lets say ive a class

class ClassA(private val txt: List<Char>, private val pat: List<Char>) : Search, Floor

where Floor ( as in floor operation in tree) is

internal interface Floor {
    abstract fun <T> maxOf(oldVal: T, newVal: T): T
}

I want to override this method in ClassA, ClassB, C …Z.

how should i go about implementing it in ClassA ? like

class ClassA(private val txt: List<Char>, private val pat: List<Char>) : Search, Floor {
  override fun <MutableList<Int>> maxOf(oldVal: MutableList<Int>, newVal: MutableList<Int>: Int     {
        return maxOf(oldVal.max(), newVal.max())
    } 
}

Or maybe it is like:

internal interface Floor {
        abstract fun maxOf(oldVal: T, newVal: T): T
    }

Or maybe like:

class ClassA(private val txt: List<Char>, private val pat: List<Char>) : Search, Floor {
  override fun <out: MutableList> maxOf(oldVal: MutableList<Int>, newVal: MutableList<Int>: Int     {
        return maxOf(oldVal.max(), newVal.max())
    } 
}

And when do you use
fun maxOf(oldVal: T, newVal: T): T or fun <T> maxOf(oldVal: T, newVal: T): T

I’m not sure what you intended to do, but to make it work you should write:

interface Floor {
	fun <T : Comparable<T>> maxOf(a: T, b: T): T
}

class FloorImpl : Floor {
	override fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a >= b) a else b
}

In the note, maxOf() functions are already a part of standard library :wink:

1 Like

ok. makes sense.

So lets say

internal interface Floor {
    fun <T: Any> maxValOf (oldVal: T, newVal: T): Any
}

by that i imply that for some ClassA maxValOf should be of type where T could be say MutableList

then

class FloorImpl : Floor {
	override fun <T : MutableList<Int>> maxValOf(
        oldVal: MutableList<Int>,
        newVal: MutableList<Int>): Int {
//        return maxOf(oldVal.max(), newVal.max() )
        return 20
    }
}

Which means an abstract interface method that can take in two args of type and spew out Anything, Any here is Int. in some other ClassB It could be String.

But compiler here says: ‘maxValOf’ overrides nothing

Then how about this one:

interface Floor2 {
	fun <T : Comparable<T>> maxOf(a: Collection<T>, b: Collection<T>): T?
}

// lazy version
class Floor2Impl : Floor2 {
	override fun <T : Comparable<T>> maxOf(a: Collection<T>, b: Collection<T>): T? {
		return (a.asSequence() + b.asSequence()).max()
	}
}

// eager version
class Floor2Impl : Floor2 {
	override fun <T : Comparable<T>> maxOf(a: Collection<T>, b: Collection<T>): T? {
		return (a + b).max()
	}
}
1 Like

No the problem is ClassZ may implement it NOT as a collectible. rather like:

class Floor2Impl : Floor2 {
	override fun <T : Int> maxOf(a: Int b: Int): String? {
		return "I am a String here"
	}
}

I mean whats the point of defining a Generic Interface function if I know firsthand what the other developer knows he is going to implement in some ClassFuture.

So I want to take in Anything, only known fact is that the Args are of same type

interface Floor3<T : Any> {
	fun maxOf(a: T, b: T): Any
}

class Floor3Impl: Floor3<Collection<Int>> {
	override fun maxOf(a: Collection<Int>, b: Collection<Int>): String {
		(a + b).max()
		return "Done"
	}
}
1 Like

Cool! So, how do we know when to do
interface ABC<T: SomeType>
vs

interface ABC {
  fun<T: SomeType/Any?> ()
}

Function-level generics have a very limited extensibility. You can’t specialize their types in subclasses. For that you need to use type-level generics.

understood. this clears up things. However you may want Interfaces with multiple functions, in some rare cases perhaps, where each fun <T: Type> could be different so we couldn’t define that Interface by <T: Type> commonly. I know we can break into multiple interfaces. but what if an interface had several fun boundaries? and classA to Z used that interfaces’ functions for their own implementations?

Trying that out here in your example:

interface ABC {
  fun<T: SomeType/Any?> ()
}

gives Declaration member missing if you just swap the type to fun instead. and remove it from the Class: ABC block

The only other thing I can think about is this:

interface ABC<T> {
	fun <S : T> someFunction(a: S, b: S)
}

You can specialize function-level generics with those from the type itself.

hmm. you mean generic type T per function of a generic type S of Interface? correct?

Yeap. Spring Data also uses this approach in repository interfaces.

ok. ive never done spring and i never touch java.

You can add any number of generic parameters to an interface:

interface Foo<A, B, C> {
    fun a(): A
    fun b(): B
    fun c(): C
}

Type-level generics are useful because they can be specialized in subclasses. However, type-level generics are fixed for each instance of the type. Function-level generics don’t have this restriction, so neither of them is clearly superior. For example:

class Foo() {
    fun<T> generic(param: T) = param!!
}

val foo = Foo()
foo.generic(5)
foo.generic("you can use any type")


class Bar<T>() {
    fun generic(param: T) = param!!
}

val bar = Bar<String>()
bar.generic("you can only use strings here")

Which one is better, depends on the use case.

@codecakes if you’re not aware of them, you should check out these functions from the standard library:


It’s not clear what’s your use case, but generics are generally (ha!) used in one of two ways:

  1. to handle any possible type that meets specific constraints:

    fun <T: Comparable<T>> Iterable<T>.max(): T? {
        var max: T? = null
        for (item in this) {
            if (max == null || item > max) {
                max = item
            }
        }
        return max
    }
    //NOTE: this is already in the stdlib!
    

    Notice how we only require that T, whatever actual class it ends up being, must implement Comparable<T> otherwise item > max won’t compile!

  2. to bind a set of functionality to a “placeholder” type:

    interface Collection<T> : Iterable<T> {
        val size: Int
        fun isEmpty(): Boolean
        fun contains(element: T): Boolean
        fun containsAll(elements: Collection<T>): Boolean
    }
    //NOTE: this is already in the stdlib!
    

    Notice how in this case we don’t need to set any limitation on what T can be, but we can still be certain that each instance implementing this interface will have to specify one type, so all functionality will be consistent.