Is there a way to place override function in a separate file in Kotlin?

Viewed 531

I have a Kotlin class that is becoming very large (a couple of hundreds of lines). It's mainly because this class is the listener of several interfaces. Usually, I split my class functions with extensions (and place them in separate files). However, when I try that with override functions, Android Studio gives me this error:

"Modifier 'override' is not applicable to 'top level function'"

So, is there a workaround? How would you split a large file with many override functions? (In Swift, this is done using extensions or Partials in C#). Here is an image for reference in Android Studio and in Xcode. In Swift, we simply add "extension" and that allows us to write code as if we were writing right within the class:

enter image description here

2 Answers

Instead of having your Activity implement listener interfaces, make them into anonymous object members. I think this is usually cleaner anyway.

class MyActivity: AppCompatActivity() {
    private lateinit var binding: MyActivityBinding
    private val someButtonListener = OnClickListener {
        binding.text = "Button clicked!"
    }

    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        binding = MyActivityBinding.inflate(layoutInflater)
        binding.button.onClickListener = someButtonListener
    }
}

Then, you could break these out into another file by making functions that create them.

class MyActivity: AppCompatActivity() {
    private lateinit var binding: MyActivityBinding

    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        binding = MyActivityBinding.inflate(layoutInflater)
        binding.button.onClickListener = createSomeButtonListener(binding)
    }
}

fun createSomeButtonListener(binding: MyActivityBinding) = OnClickListener {
    binding.text = "Button clicked!"
}

If you need to call functions in the Activity from these listeners, you'll have to make your Activity a parameter of the function and expose those functions as public, unfortunately. It's not really proper encapsulation, but you typically don't reference Activities from other classes ever, so it's probably not a big deal for it to have some public functions.

This is my code inspired by the answer:

interface MyInterface {
    fun itHappened()
}

class MyClass {
    lateinit var listener: MyInterface
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val myObj = MyClass()
        myObj.listener = anonymousListener()
        myObj.listener.itHappened()
    }
}

fun MainActivity.anonymousListener() = object : MyInterface {
    override fun itHappened() {
        Log.d("MyTag", "Clicked")
    }
}
Related