Delegation and mixins


#1

I am trying to find some elegant solution how to add functionality to existing class hierarchies.

I found this old discussion about very same problem
http://blog.jetbrains.com/kotlin/2011/08/multiple-inheritance-part-2-possible-directions/#comment-125
There’s suggested solution to use delegation but it looks to me that’s not possible because methods in Activity superclass overriden by delegation are never called and that’s showstopper for android.

open class Activity {   open fun onCreate() = println("Activity create") }

trait Callbacks {
  fun onCreate()
}

class CallbacksImpl: Callbacks  {
  override fun onCreate() = println(“Callbacks create”)
}


When I do this:

class MyActivity: Activity(), Callbacks by CallbacksImpl()

val activity = MyActivity()
activity.onCreate() // prints ONLY "Callbacks create"


I can see no way how to change this code (without significantly touching MyActivity) to make it print:

“Activity create”
"Callbacks create"


First I was thinking that “requiring” Activity in Callbacks would help but even this looks like dead end.

trait Callbacks: Activity {
  override fun onCreate() {
  // ?
  }
}

In Java is this problem “solved” by making many subclasess like CallbacksActivity, CallbacksPreferenceActivity, … which are exactly the same.
I was hoping Kotlin can provide better solution but it seems that traits and delegation are usefull only for presenting new functionality but not extending existing one.


#2

The problem is that when you require a class in a trait, you can't make a "super.onCreate()" call, because inheritance may turn out to be indirect. But you can do something like this:

open class Activity {   open fun onCreate() = println("Activity create") }

abstract class ExtensibleActivity: Activity() {
  override fun onCreate() {
  super.onCreate()
  doOnCreate()
  }
  
  abstract fun doOnCreate()
}

trait Callbacks : ExtensibleActivity {
  override fun doOnCreate() {
  println(“Callbacks create”)
  }
}

class MyActivity: ExtensibleActivity(), Callbacks

fun main(args : Array<String>) {
  val activity = MyActivity()
  activity.onCreate() // prints both “Activity create” and “Callbacks create”
}

Live version: http://kotlin-demo.jetbrains.com/?publicLink=104074971561017308771-1411938433


#3

My description probably wasn't clear enough. The problem is there are already multiple subclasses of Activity and I want Callbacks to be usable with all of them.

Something like this exists in framework and I can’t change it:

open class Activity {
  open fun onCreate() = println(“Activity create”)
}

open class ActivityA: Activity()
open class ActivityB: Activity()


But I would like to do something like this:

class MyActivityA: ActivityA(), Callbacks by CallbacksImpl()
class MyActivityB: ActivityB(), Callbacks by CallbacksImpl()


It would be nice to have only one implementation of Callbacks which can be mixed in to any activity. It works fine until I have to call super method, which is this case.
I was trying to avoid creating helper subclass for every Activity type. That’s what I am doing in Java.

Taking your code, I have to create this:

abstract class ExtensibleActivityA: ActivityA() { … }
abstract class ExtensibleActivityB: ActivityB() { … }


and implementation of both will be identical, right?


#4

Yes, if you have many activity classes that you can't change, it's a problem.


#5

Why exacly we can't call "super.someMethod()" in a trait? Is it because concrete class defined as supertype of a trait isn't actualy it's supertype but it's more like a constraint saying which classes can implement this trait? So "super" keyword in a class would behave different than in a trait?

I expect there are some more serious reasons why you prohibited this. I think it would be “super” useful if we could do this.


#6

First, we don't know if there is any implementation of a particular method in the superclass: the actual superclass may easily override it to be abstract.

Also, there’s no sane way to do it technically: the corresponding bytecode instruction (invokespecial) is only allowed in the actual subclass, and the code for trait method bodies is emitted to an auxiliary static class.