Recently i have had chance to work on converting legacy Java classes to Kotlin classes.
Some of the Problems to Solve
- Making the class testable
- Ability to Mock dependencies
- Removing Dead Methods
- Verify whether objects need to nullable
- Use more advanced features of Kotlin to make the code more readable
- Testing private inner classes only reachable through callbacks at runTime
Some of the ways to Solve the above Issues
First Step is to use the android studio feature and convert the existing class to Kotlin.

This will have the conversion done.
From here on i will talk about things that will help in testing the file and patterns that can be used for legacy modules that don’t have any Dependency Injection tools. To help with the mocking of the dependencies.
Move Instantiation to Constructor
Try to move all new instantiations to the constructor with default value initialization or passed by the calling class.
internal class A {
private b:B = B()
fun doSomething():B {
return b;
}
}
and refactor it to –
internal class A @JvmOverloads constructor(private val b:B = B()) {
fun doSomething():B {
return b;
}
}
@JvmOverloads annotation is used for interoperability with Java and using default arguments.
This way mock objects can be used for testing different flows.
Using Factory Pattern
Another option to pass control the creation of objects is to use factory pattern. But keep in mind if there are a lot of factory classes that are present than it might be a case to move to Dependency Injection tool to manage this.
internal class A {
private b:B = B()
fun doSomething():B {
return b;
}
}
Create a Factory that will control the creation of B. Constructor is made private to support Singleton pattern.
class B private constructor() {
internal fun createInstance(): B {
return B()
}
companion object {
val instance = BFactory()
}
}
and use the factory to get an instance, use it in the class.
internal class A @JvmOverloads constructor(private val bFactory:BFactory = BFactory.instance)) {
private var b:B
init {
b = bFactory.createInstance()
}
fun doSomething():B {
return b;
}
}
Pull out Inner class into its own Individual class
internal class A {
fun doingSomething() {
doSomethingLater()
}
private class C {
fun doSomethingElse():Int {
return 1
}
}
private class D {
fun doSomethingLater():Int {
return 2;
}
}
}
Create a new class C and pass it as a constructor parameter
internal class C {
fun doSomethingElse():Int {
return 1
}
}
Create anew class D and pass it as a constructor parameter
internal class D {
fun doSomethingLater():Int {
return 1
}
}
and pass D as parameter through the constructor
internal class A @JvmOverloads(private val c:C, private val d:D) {
// c is available at runTime for callBacks
fun doingSomething() {
d.doSomethingLater()
}
}
Verify Nullable Objects
To cleanup the code verify whether any nullable objects defined during conversion are indeed nullable.
Convert if blocks to use let and elvis operator to make the code more readable.
Conclusion
After following these steps the code should be more readable , testable and concise.