I've started getting addicted to this format inste...
# announcements
h
I've started getting addicted to this format instead of abstract or open classes, and I imagine that's a bad thing:
Copy code
interface Foo {
    val foo: Int
    val bar: String?

    companion object {
        operator fun invoke(foo: Int, bar: String? = null) = object : Foo {
            override val foo = foo
            override val bar = bar ?: otherLogic...
            // etc
        }
    }
}
In a lot of ways it's like having the ability to inherit from multiple classes. That's probably not a good freedom to have, though.
invoke
operator has a few advantages over
constructor
, too, such as error checking the arguments prior to instantiating the object. This also opens up delegation to cut down on code You'd think it'd be unfortunate you couldn't do this:
Copy code
class Foozle(foo: Int, bar: String?) : Foo(foo, bar)
But you can instead just do this:
Copy code
class Foozle(foo: Int, bar: String?) : Foo by Foo(foo, bar)
I'd love to hear the drawbacks to this overall approach
😱 1
a
You can skip the companion object and use an "extension constructor" from anywhere:
Copy code
fun Foo(...): Foo = object : Foo { ...}
h
yeah, but for some reason I really like the companion object version, because: A: It obeys general syntax rules (don't capitalize the name of a function) B: It appears from the outside as an actual constructor. In Idea, the basic code style gives it the same appearance, whereas it'll have a different one for a function. I like how sneaky that is, then
r
I do that too, but not using any kind of inheritance. When I have a service interface and a single implementation of that interface, I define an invoke function on the interface which is basically my factory creating, configuring and returning the implementation. Easier to use and still easily mockable for tests. I’d be interested to hear about drawbacks of this approach too.
m
@Hullaballoonatic if you don't want multiple inheritance, but still want constructor control via invoke, you can use a class with a private constructor. Effectively the same without creating a bunch of anonymous classes. @ribesg Controversial Opinion. If there's only 1 implementation, then you don't need an interface. The interface is implicitly the public functions on the implementation. If this is a library, then an interface makes sense for consumers, but if it isn't, then I apply the above rule. Mocking tools allow for mocking a class, so doesn't restrict testing.
r
You need an interface to create mocks for testing.
m
No you don't. MockK and Mockito support mocking classes. I write services this way without problem. But I did say it's controversial.
r
None of those libraries work outside of the JVM
m
My apologies, then, as my experience is JVM only. If your platform doesn't support mocking of classes, then feel free to ignore my comments. But on JVM, interfaces are not required to mock. And I think MockK is at least investigating MPP, so it may be an option in the future.