https://kotlinlang.org logo
#getting-started
Title
# getting-started
r

Ray Rahke

03/04/2024, 1:23 AM
I have a hierarchy of classes (one abstract base class, 3 sub classes). I need all sublcasses to have a method foo(). but it needs to be static. The implementation for each subclass's method will be different, so it also needs to be abstract. How can I create foo() as an abstract static method on the base class? I have looked and it does not seem possible in kotlin to have a method as both abstract and static.
j

Joffrey

03/04/2024, 1:45 AM
How would you use it? I mean what's the point of enforcing the presence of a static method via subtyping ? Could you please give an example on what you're trying to do? In Kotlin, you can make the companion object of each of these classes implement an interface, so you can achieve this static access on every class, but I'm still not sure what you're trying to do with it.
r

Ray Rahke

03/04/2024, 1:54 AM
I will give an example soon. It is for implementing a command handler, via the chain of responsibility design pattern
I want each command to statically have a match(message: String): Boolean function
then I will do something like handlers: KClassCommandHandler for command in handlers { if command.match(message) { executor = command(some_lifetime_data) // instantiate executor.execute()
we only instantiate a command executor if the match returns true.
j

Joffrey

03/04/2024, 2:11 AM
Then I really think you should use companion objects instead
r

Ray Rahke

03/04/2024, 7:10 AM
@Joffrey I'm not sure how to get that working
Copy code
abstract class CommandHandler (
    val parts: String
) {
    companion object {
        abstract fun match(str: String): Boolean
    }
    private fun format(str: String): List<String> {
        return str.split(" ")
    }
    abstract fun execute()
}

class HelpCommand : CommandHandler() {
    companion object {
        override fun match(str: String): Boolean {
            return str.startsWith("help")
        }
    }

    override fun execute() {
        // print help stuff ...
    }
}

class Main {
    val handlers: List<KFunction<CommandHandler>> = listOf(::HelpCommand)
    val input = readln()
    for (command. in handlers) {
        if (command.match(input)) {
            command(input).execute()
        }
    }
}
further research, looks like this is just not possible
Kotlin's type system is not powerful enough to express this sort of thing
j

Joffrey

03/04/2024, 7:33 AM
If your command classes don't have any constructor arguments nor state, they could even be `object`s themselves (singletons). If they do need arguments, then using companion objects would do, but you have to make the companions themselves implement an interface (if you need this)
r

Ray Rahke

03/04/2024, 7:33 AM
the command classes take in the original message as the constructo rargument
command("goto kingdom").execute()
j

Joffrey

03/04/2024, 7:34 AM
(By the way, for this use case I suggest you use Clikt, which is a very nice CLI library for Kotlin)
r

Ray Rahke

03/04/2024, 7:35 AM
ill check it out. though this is for a student's project where i want him to get to experience all the problems that crop up in creating a CLI from scratch
j

Joffrey

03/04/2024, 7:35 AM
Ah ok, then that rules out the lib
r

Ray Rahke

03/04/2024, 7:35 AM
he's quite good at OOP so he was liking the idea of a static abstract method approach when i told him about it
guess we will just have to not do static
j

Joffrey

03/04/2024, 7:36 AM
The Kotlin equivalent of static is using the companion, but indeed it's quite decoupled from the class. So there will be no real link between the companion and the class itself (besides the convenience of using the command class name to refer to the companion)
r

Ray Rahke

03/04/2024, 7:37 AM
i see
what about
I can do stuff like command::class, could I extend that?
add my own stuff
command::match()?
j

Joffrey

03/04/2024, 7:39 AM
Copy code
interface CommandType<T : CommandHandler> {
    fun matches(str: String): Boolean
    fun create(command: String): T
}

abstract class CommandHandler (
    val parts: String
) {
    private fun format(str: String): List<String> {
        return str.split(" ")
    }
    abstract fun execute()
}

class HelpCommand(parts: String) : CommandHandler(parts) {
    companion object : CommandType<HelpCommand> {
        override fun matches(str: String): Boolean = str.startsWith("help")
        override fun create(parts: String) = HelpCommand(parts)
    }

    override fun execute() {
        // print help stuff ...
    }
}

fun main() {
    val handlers: List<CommandType<*>> = listOf(HelpCommand)
    val input = readln()
    
    for (commandType in handlers) {
        if (commandType.matches(input)) {
            commandType.create(input).execute()
        }
    }
}
r

Ray Rahke

03/04/2024, 7:39 AM
yeah that is pretty close to just doing a Factory pattern
which is what i was considering
j

Joffrey

03/04/2024, 7:41 AM
Yeah it's basically that, but using companions as factories so you can get this "static" feeling by referencing command types using the command class names
w

Wout Werkman

03/04/2024, 7:41 AM
No, abstract static members are not possible in Kotlin sadly unlike most other modern statically typed languages. In Kotlin one either uses OOP (factories) or functional (type classes) approach for this behavior.
r

Ray Rahke

03/04/2024, 7:41 AM
@Joffrey yeah that does avoid having to create n*2 classes, where every Command class needs a corresponding CommandFactory class. I like that
@Wout Werkman :👍 sounds good, thanks
but what are functional (type classes)?
w

Wout Werkman

03/04/2024, 8:37 AM
theSwiftSnippetIfYouAreInterestedByTheWay.cpp