Kotlin: How can I create a “static” inheritable fu

2020-01-31 23:54发布

问题:

For example, I want to have a function example() on a type Child that extends Parent so that I can use the function on both.

Child.example()
Parent.example()

The first "obvious" way to do this is through the companion object of Parent, but this doesn't allow example() for Child.

The second way I tried was defining an extension function on Parent.Companion, which is inconvenient because you are forced to define a companion object. It also doesn't allow example() for Child.

Does anybody know how I can do this?

回答1:

What you are asking for does not exist, you appear to be asking:

Can I reference a companion object method of a superclass from a class reference of its descendants

or maybe you are asking:

Can I reference a static member of a superclass from a reference of its descendants.

The answer for both is no. It is by design of Kotlin that this is disallowed, was a conscious decision, was intentional. If you wish to change this decision you would need to file an issue in YouTrack. Programmers in Java were heavily confused by the inheritance and overriding of static methods and the behavior when called from one reference versus another and how it is statically resolved rather than dynamically. Java 8 team when adding static methods to interfaces realize the confusion this could cause so they took a more Kotlin approach of only allowing it to be called by the interface reference. To avoid these types of nightmares, the Kotlin team disallowed it. Just as they disallowed many other confusing aspects of Java.

The other answers (@voddan's for example) are giving you work-arounds to have the same feel of calling syntax by using companion objects but you reject those in your comments by saying you want to avoid a companion object, even though your question states you are trying to use them. So assuming you do not want to use them, the answer is simply no, can't be done.

To get rid of the companion object, you would want to talk about Can extension functions be called in a “static” way? ... which will be disappointing since it is not yet allowed.

Going back to companion objects (sorry, but they are the one path to glory here), you can also just manually delegate the method of the child to the parent:

open class Parent {
    companion object { // required, sorry, no way around it!
        fun foo() = /* some cool logic here */
    }
}

class Child: Parent() {
    companion object {  // required, sorry, no way around it!
        fun foo() = Parent.foo() 
    }
}

Or as extensions:

open class Parent {
    companion object {} // required, sorry, no way around it!
}

class Child: Parent() {
    companion object {} // required, sorry, no way around it!
}

fun Parent.Companion.foo() = /* some cool logic here */
fun Child.Companion.foo() = Parent.foo()


回答2:

Since companion objects play by the same rules as any others, it is easy to use normal means of code reuse on them:

open class Example {
    fun example() {}
}

class Parent {
    companion object : Example()
}

class Child {
    companion object : Example()
}

fun main(args: Array<String>) {
    Child.example()
    Parent.example()
}

Alternatively with class delegation you can reuse the implementation from Parent directly:

interface Example {
    fun example()
}

class Parent {
    companion object : Example {
        override fun example() {}
    }
}

class Child {
    companion object : Example by Parent.Companion
}


回答3:

The purpose is to create an "alternative operator" to construction

In Java you should use a Factory instead of a static method when used as a "constructor".

On the Kotlin side you could use a top-level-function instead of a Factory.

Or if you really want to have a "static method" in your class, I'd create a companion object and add static extension methods.