How to find the KClass of the owner of a companion object? - kotlin

I'm trying to get the class that owns a companion object so I can create loggers with inline technique inside a companion object but referencing the main class that is being logged and not the companion object.
The problem is that I can't find a way to get the owner of the companion object, how can I do it?
fun Logger(c: Class<*>): Logger {
var c2 = c
val k = c.kotlin
if (k.isCompanion) {
c2 = k.<opposite of what companionObject does>.java
}
// Calls a factory, reuse the same instance if it already exists
return RootLogger.getChild(c2)
}
#Suppress("NOTHING_TO_INLINE")
inline fun Logger(): Logger {
return Logger(MethodHandles.lookup().lookupClass())
}
The intended use-cases:
A:
class SomeClass {
companion object {
// Logger is inside a companion object
// But it must be the same as if it were created directly inside `SomeClass`
private val log = Logger()
}
fun someFun() = log.info("Hello")
}
B:
// can be object or anything else
class SomeClass {
// The same object is returned to all instances and would be the same object
// as if it were inside a companion object
private val log = Logger()
fun someFun() = log.info("Hello")
}

In JVM environment that can be achieved with:
k.java.enclosingClass.kotlin
So the function would be:
fun Logger(c: Class<*>): Logger {
var c2 = c
val k = c.kotlin
if (k.isCompanion) {
c2 = k.java.enclosingClass
}
// Calls a factory, reuse the same instance if it already exists
return RootLogger.getChild(c2)
}
That can be tested with:
internal class LoggerTest {
#Test
internal fun testUseCaseA() {
val fqn = UseCaseA::class.qualifiedName
assertEquals(fqn, UseCaseA.log.name)
val log = Logger(UseCaseA::class.java)
assertEquals(fqn, log.name)
assertSame(UseCaseA.log, log)
val log2 = Logger(UseCaseA.Companion::class.java)
assertEquals(fqn, log2.name)
assertSame(log, log2)
}
#Test
internal fun testUseCaseB() {
val fqn = UseCaseB::class.qualifiedName
val b1 = UseCaseB()
assertEquals(fqn, b1.log.name)
val log = Logger(UseCaseB::class.java)
assertEquals(fqn, log.name)
assertSame(b1.log, log)
val b2 = UseCaseB()
assertEquals(fqn, b2.log.name)
assertSame(b2.log, log)
}
private class UseCaseA {
companion object {
val log = Logger()
}
}
private class UseCaseB {
val log = Logger()
}
}

Related

When will the init{...} be launched in a Class of Kotlin?

I run Code A, and get the error Result A. It seems that isRecordingState has't been initialized.
So I modify Code A as Code B, and Code B can run correctly.
In my mind, I can place different functions in any order in a class of Kotlin.
I think init{ } of a Class will be launched after the object has been initialized, so I think I can place init{ } in any place of a class.
What's wrong with my ideas?
Code A
#HiltViewModel
class SoundViewModel #Inject constructor(
#ApplicationContext private val appContext: Context,
...
): ViewModel() {
init { beginSoundDensity() }
private val _timeXState = MutableStateFlow(0)
var isRecordingState by mutableStateOf(false)
private set
private var myJob: Job?=null
fun beginSoundDensity() {
if (isRecordingState == false) {
isRecordingState = true
myJob?.cancel()
...
}
}
}
Result A
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object androidx.compose.runtime.State.getValue()' on a null object reference
at info.dodata.soundmeter.presentation.viewmodel.SoundViewModel.isRecordingState(SoundViewModel.kt:245)
at info.dodata.soundmeter.presentation.viewmodel.SoundViewModel.beginSoundDensity(SoundViewModel.kt:81)
at info.dodata.soundmeter.presentation.viewmodel.SoundViewModel.<init>(SoundViewModel.kt:39)
Code B
#HiltViewModel
class SoundViewModel #Inject constructor(
#ApplicationContext private val appContext: Context,
...
): ViewModel() {
private val _timeXState = MutableStateFlow(0)
var isRecordingState by mutableStateOf(false)
private set
private var myJob: Job?=null
init { beginSoundDensity() }
fun beginSoundDensity() {
if (isRecordingState == false) {
isRecordingState = true
myJob?.cancel()
...
}
}
}
The code just runs from top to bottom, so this code for example prints "12345"
fun main() {
A()
}
class A {
init {
print("1")
}
val s2 = printAndReturn("2")
init {
print("3")
}
val s4 = printAndReturn("4")
init {
print("5")
}
private fun printAndReturn(s: String): String {
print(s)
return s
}
}

Mockk anonymous class

I'm trying to mock anonymous class created in testInstance Sample:
class SomeClass(val someValue:SomeType) :SomeAnotherClass(){
val anonymousClass = object : AnotherClass{
override anotherMethod() { }
}
override fun someMethod(someValue) = anonymousClass.someMethod(someValue)
}
And test class:
class SomeClassTest {
private val someValue: SomeType = mockk()
private val testInstance = spyk(SomeClass(someValue), recordPrivateCalls = true)
#Test
fun `test method`(){
mockkConstructor(CustomTlsSocketFactory::class)
every { anyConstructed<AnotherClass>().someMethod(someValue) } returns mockk()
testInstance.someMethod(someValue)
verify { anyConstructed<AnotherClass>().someMethod(someValue) }
}
}
And for some reason anonymousClass.someMethod(someValue) is trying to call original method not mockk.
Calling testInstance.anonymousClass.isMock is false

How to mock the ApplicationContext in companion object in kotlin?

Help test a function that calls a class method.
There is a class whose field is initialized with a chain of methods appContext().getBean().
class ClassA {
private val employeeApi: EmployeeApi= appContext().getBean()
fun execute(): List<Employee> {
return employeeApi.method()
}
}
and for example there is a function for calls to class methods.
fun methodForTesting(params: ClassA .() -> Unit): List<Employee> {
return ClassA().apply(params).execute()
}
This function needs to be tested. How to do it?
Open access to the method in the file using
mockkStatic("com.company.MyFile")
Create a list of workers who need to be returned
Create a method stub
Calling the method
Checking data
#Test
fun `methodForTesting mockk`() {
mockkStatic("com.company.MyFile")
val classA : ClassA= Mockito.mock(ClassA::class.java)
val listEmployee: List<Employee> = listOf(Employee("Ivan"))
Mockito.`when`(classA.execute()).thenReturn(listEmployee)
val list : List<Employee> = methodForTesting(allAny())
Assertions.assertThat(list).isEqualTo(listEmployee)
}
If I write like this, then an error occurs. How can the solution be finalized?
Error:
kotlin.UninitializedPropertyAccessException: lateinit property FIELD has not been initialized
at Mockito.when(classA.execute()).thenReturn(listEmployee) <-- HERE
Configuration
EnableConfigurationProperties(HurmaProperties::class)
class AppConfig : ApplicationContextAware {
private var applicationContext: ApplicationContext? = null
override fun setApplicationContext(applicationContext: ApplicationContext) {
this.applicationContext = applicationContext
appCtx = applicationContext
}
#Bean
fun restTemplateBuilder(): RestTemplateBuilder = RestTemplateBuilder()
#Bean
fun restTemplate(builder: RestTemplateBuilder): RestTemplate =
builder.build()
companion object {
#JvmStatic
lateinit var appCtx: ApplicationContext
private set
}
}
fun appContext() = AppConfig.appCtx
I figured it out
If you call getBean() from the ApplicationContext, and you need to test some method, then do this:
var employeeApi = mock<EmployeeApi>()
every { appContext().getBean<EmployeeApi>() } returns employeeApi
every { employeeApi.method() } returns listEmployee
val resultList: List<Employee> = methodForTesting();

Inherit Companion Obejct in Children - Kotlin

I've read that static methods cannot overridden in Kotlin, so I'm not sure if this is possible, but not being able to do so would result in a lot of repetitious code. Is there any way to achieve the same behavior while moving the companion object into the Parent? Here is what I have so far
Parent.kt
abstract class Parent {
protected val TAG = this::class.java.simpleName
}
Brother.kt
class Brother: Parent() {
companion object {
#Volatile private var instance: Brother? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: Brother().also { instance = it }
}
}
}
Sister.kt
class Sister: Parent() {
companion object {
#Volatile private var instance: Sister? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: Sister().also { instance = it }
}
}
}
main()
fun main() {
println("Hello, ${Brother.getInstance().TAG}")
println("Hello, ${Sister.getInstance().TAG}")
}
Console Output:
Hello, Brother Hello, Sister
Maybe this will work for what you're trying to do.
You can create a superclass for objects that do this pattern:
open class SingletonAccessor<T: Any> (private val constructor: () -> T){
#Volatile private var instance: T? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: constructor().also { instance = it }
}
}
And then inherit it from your implementation class companion objects:
class Brother private constructor(): Parent() {
companion object: SingletonAccessor<Brother>(::Brother)
}
class Sister private constructor(): Parent() {
companion object: SingletonAccessor<Sister>(::Sister)
}
This pattern isn't much different from simply making Brother and Sister objects, since they have no constructor parameters, but maybe this is just a simplified example.
Based on #Tenfour04's answer, I've come up with an alternate approach, which incorporates the SingletonAccessor into the Parent
abstract class Parent<T>(private val constructor: () -> T) {
#Volatile private var instance: T? = null
protected val TAG = this::class.java.simpleName
fun getInstance() = instance ?: synchronized(this) {
instance ?: constructor().also { instance = it }
}
}
The implementation in the children is the same as before.
Let me know if this answer can be improved further. In particular, I would like to do in the class declaration class Parent<T: Parent>, but that doesn't compile. Is there a way to limit the type parameter to itself and its children?

Kotlin - Factory Function for Class with Private Constructor

In Kotlin, is it possible to have a factory function that creates an instance of a class with a private constructor?
My goal is to enforce the factory function to be used and to prevent instantiation via the class's constructor.
Example:
// factory function, valid
val myInstance = myClassOf()
// class instantiation, invalid
val myInstance = MyClass()
I'm trying to mimic the behavior of some of the built-in factory functions like intArrayOf(), e.g.
// works
val myIntArray = intArrayOf()
// not possible as IntArray has a private constructor
val myIntArray = IntArray()
You can use companion object in this way:
class MyClass private constructor() {
companion object {
operator fun invoke() = MyClass()
}
}
val myInstance = MyClass() // Calls the factory function invoke()
Name the factory function if it has a special meaning. For example:
class MyClass private constructor(values: List<String>) {
companion object {
fun of(vararg values: String) = MyClass(values.toList())
}
}
val myInstance = MyClass.of("first", "second")
You can do something like this:
import MyClass.Companion.myClassOf
class MyClass private constructor() {
companion object {
fun myClassOf() = MyClass()
}
}
//val myInstance1 = MyClass() // not allowed
val myInstance2 = myClassOf()
Try Builder instead.
class FoodOrder private constructor(
val bread: String?,
val condiments: String?,
val meat: String?,
val fish: String?) {
data class Builder(
var bread: String? = null,
var condiments: String? = null,
var meat: String? = null,
var fish: String? = null) {
fun bread(bread: String) = apply { this.bread = bread }
fun condiments(condiments: String) = apply { this.condiments = condiments }
fun meat(meat: String) = apply { this.meat = meat }
fun fish(fish: String) = apply { this.fish = fish }
fun build() = FoodOrder(bread, condiments, meat, fish)
}
}
Reference: https://www.baeldung.com/kotlin-builder-pattern