Sometimes I like to just fiddle around and think about what I can do, rather than what I should do. 😉
Here’s the motivating example. Let’s say I’m implementing some class. Inside that class I need to perform some operations on String
s. These are pretty specialized operations that are really only relevant to this class’s functionality. Here’s a skeleton of an example of what I would sometimes do:
class Foo() { fun doSomething() { // some logic val x = parseBookFile(); if (x.countPages() > 1000) { throw Exception("tl;dr") } // more logic } } private fun String.countPages() = TODO() // domain specific term doesn't belong public
But, now you can’t test that countPages
function by itself. You just have to test Foo.doSomething
more thoroughly. If you do this too much you get combinatorial explosion in your test cases.
What if we did this instead?
class Foo() { fun doSomething() { // some logic val x = parseBookFile(); if (x.countPages() > 1000) { throw Exception("tl;dr") } // more logic } fun String.countPages() = TODO() // domain specific term doesn't belong public }
Now the function String.countPages
isn’t technically private, but outsiders can’t really use it unless they work kind of hard to get at it. The only way to access String.countPages
is while inside class Foo
‘s context. In other words, you’re either editing Foo
‘s definition directly or you’re writing an extension function for Foo
:
fun doAThing() { "abc".countPages() // compile error } fun Foo.doSomethingElse() { "abc".countPages() // here you can access it! }
I haven’t actually used this anywhere yet, but I feel like it’s a nice middle ground between other options. Those other options being:
- Keep the function private. Now you can’t test it.
- Make the function public/internal. Now it “pollutes” the namespace when I’m almost positive that others should not use this function.
- Make the function internal but make this whole class/functionality into a module, so you can test it, but still keep it private from users of your new “library”. This is clearly overkill for many cases.
- Kotlin implements package-private. :p
The functionality itself doesn’t have to be an extension function, either, necessarily:
class Foo() { fun doSomething() { [snip[ } fun Foo.add(i: Int, j: Int): Int = TODO() }
This will have the same effect of only allowing add
to be accessed from an extension function on Foo
. This form does give a compiler warning that the receiver isn’t used, though.
So, I don’t know. I just thought it was pretty cute and that I might share it. 😀
EDIT: Taken to its logical conclusion, I guess we can end up with something like:
class Foo() { fun doSomething() = with(FooContext) { // some logic val x = parseBookFile(); if (x.countPages() > 1000) { throw Exception("tl;dr") } // more logic } } object FooContext { fun String.countPages() = TODO() // domain specific term doesn't belong public }
submitted by /u/ragnese
[link] [comments]