Lazy way of overriding functions

This time I am just doing pure experimentation. I am sure this methodology of messing up your own code has some fancy scientific or mathematical name. The origin of this is when I was thinking about how limited we are in Android that every time we want to do something with Activity, we have to extend it somehow. And each of us has always experienced that new project starts with creating something called “BaseActivity”. And then later discovering that we need “EvenMoreBaseActivity”, and so on. Until we start begging to be able to actually modify and extend Activity instance itself.

I can’t promise solution to this problem right away (without creating NewFancyBasceActivity), but the technique I am explaining here, can be used for creating your next new shiny kotlin project. This kind of fancy coding is available because we have functions as first class citizens in Kotlin.

Imagine our simple class. It differs from normal classes, that we define our methods as named lambdas.

open class Experimental {
    open var simplePrint = {
        print("One is called")
    }

    open var squaring = { param: Int ->
        param * param
    }
}

And now imagine, that we want to do something additional to our simplePrint or squaring functions. Of course, because our class is open with open vars, we can override the heck out of it, and be done with it. But what if we want something ELSE to be done with the methods. Of course, we create additional extensions, and so on, until we have BasicExperimentalForNetworkRelease version. And… can you parametrize it? Would you be able to create instance of this class (if it would be normal class with functions) with parametrized behaviours?

But because our class has variables which are functions, we can do parametrization. And implement function overriding by introducing two utility functions.

fun modify(old: () -> Unit, new: (original: () -> Unit) -> Unit) = {
    new(old)
}

fun <T, R> modify(old: (T) -> R, new: (T, original: (T) -> R) -> R) =
        { p: T ->
            new(p, old)
        }

First one takes original old function, and lambda, which is being passed original function of type equal to the old function. Also it returns lambda which is the same shape as first function passed to the method.

This gives us fancy way of overriding lambda methods

val instance = Experimental()
instance.simplePrint()
println()
instance.simplePrint = modify(instance.simplePrint) { _super ->
    _super()
    print("****")
}
instance.simplePrint()

Output would be:

One is called
One is called****

Notice that we have modified simplePrint method by creating lambda, which receives original function as it’s parameter, which then we can use in our invocation as we please.

Second modification function is for methods with parameter(s). Actually we need to care only for methods with one parameter, as you can turn any n-parameter function into one parameter function using currying which I will not explain here (ask Google).

fun <T, R> modify(old: (T) -> R, new: (T, original: (T) -> R) -> R) =
        { p: T ->
            new(p, old)
        }

Idea is the same, only we pass the original parameter along with original old function. And the modification code would look something like this:

println(instance.squaring(5))
instance.squaring = modify(instance.squaring) { p, _super ->
    _super(p) + 2
}
println(instance.squaring(5))

Output:

25
27

So here it is. Fancy parametrization and modification of classes if they are written with lambdas not actual functions.

Of course now some thoughts about promised modification of Activity. What you can invent is your own REAL BasicActivity, which has extension points for each lifecycle or other interesting method using this pattern

oepn class MyActivity : Activity() {
  var _stop = {}
  var _start = {}

  override fun onStart() {
    super.onStart() // can't do much about this
    _start() // <- This is where magic happens
  }
  ...
}

Now you can modify your lifecycle behavior on the fly without extending the Activity. You can create instance of the same activity with one behavior, and another instance of the Activity with different behavior. You can have different behaviors for landscape and portrait. Because you can add Activity lifecycle observer on your Application class, you can actually decide those behaviours there and actually INJECT different behaviours to the activities. The same you can do authomatically to your Fragments, if you override onAttach method to your activity, which then can decide (or delegate) what kind of behaviors to inject into your Fragments.

At the end of the day you will have your app consisting not of zillion different classes which actually limit you, but you will have smart classes with zillion of nice small functions doing whatever you need whenever you need without polluting classes.

But… with greate power comes greate responsibility and long hours of debugging.

 

Have fun.

 

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s