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
Related
First I'll post the code
#OptIn(ExperimentalSerializationApi::class)
#Serializer(forClass = UUID::class)
object UUIDserializer : KSerializer<UUID> {
override fun deserialize(decoder: Decoder): UUID = UUID.fromString(decoder.decodeString())
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())
}
}
typealias SID = #Serializable(with = UUIDserializer::class) UUID
fun randomSid() = UUID.randomUUID() as SID
#Serializable
data class Example(val id:SID = randomSid())
class SerializeId {
#Test
fun nestedTypeUsage() {
val example = Example()
val string = Json.encodeToString(example)
println(string)
}
#Test
fun directTypeUsage () {
val hi = randomSid()
val string = Json.encodeToString(hi)
println(string)
}
}
nestedTypeUsage run and passes, but directTypeUsage fails.
Serializer for class 'UUID' is not found.
Mark the class as #Serializable or provide the serializer explicitly.
kotlinx.serialization.SerializationException: Serializer for class 'UUID' is not found
I can't apply the #Serializable annotation directly to a val or a function parameter.
almost immediately after posting this. I realized I can
#Test
fun directTypeUsage () {
val hi = randomSid()
val string = hi.toString()
println(string)
}
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()
}
}
I have two classes A and B.
Class A has several properties. One of them are an instance of class B.
At some point in the main function I will define an instance a of A. I will need to do some computation on its property of type B.
This computation, however, needs another property of a.
The result is a.property3.computation(a.property1,someValue). I think it's ugly.
Here is some "pseudo-code" (in Kotlin but I am facing the same problem using other languages as well):
class B {
val property : Map<String,Int>
fun computation(val parameter1: Int, val parametre2: Double) : Int {
//doing some computation
return result
}
}
class A {
var property1 : Int
var property2 : Stirng
var property3 : B
}
fun main (){
val someValue = 0.4 //It's here to show the idea that the function `computation` needs also some other arguments that does not come from `A`'s propery
val a = A()
val result = a.property3.computation(a.property1,someValue) // here is the line that bothers me !
}
I think you should use Builder design pattern to remove computation function from B class like this:
class B {
val properties : MutableMap<String,Int> = HashMap()
fun setProperty(name: String, value: Int) {
this.properties[name] = value
}
}
class A {
var property1 : Int = 0
var property2 : String = ""
var property3 : B = B()
}
class Calculator(builder: Builder) {
private val property1 = builder.property1
private val someValue = builder.someValue
private val properties = builder.properties
companion object {
fun builder() = Builder()
}
fun computation() : Int {
//doing some computation
val result = property1 + someValue.toInt() + properties.getOrDefault("value", 0)
return result
}
class Builder {
var property1: Int = 0
var someValue: Double = 0.0;
var properties : MutableMap<String,Int> = HashMap()
fun property1(property1: Int): Builder {
this.property1 = property1
return this
}
fun someValue(someValue: Double): Builder {
this.someValue = someValue
return this
}
fun properties(properties : Map<String,Int>): Builder {
this.properties.putAll(properties);
return this
}
fun build(): Calculator {
return Calculator(this)
}
}
}
fun main (){
val someValue = 0.4 //It's here to show the idea that the function `computation` needs also some other arguments that does not come from `A`'s propery
val a = A()
a.property1 = 10
a.property3.setProperty("value", 20)
val result = Calculator.builder()
.properties(a.property3.properties)
.property1(a.property1)
.someValue(someValue)
.build()
.computation()
println(result)
}
May be you want this?
fun main (){
val someValue = 0.4
val a = A()
val result = with(a) {
property3.computation(property1,someValue)
}
}
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?
I think somethings wrong about my code in TeamImplsTest, and i need advice :D
This is my code
API interface
interface API {
#GET("lookupteam.php")
fun getTeam(#Query("id") id: String): Call<TeamModel>
}
TeamPresenter
interface MatchPresenter {
fun loadTeamDetail(team_id: String)
}
TeamImpls
class TeamImpls(val teamView: TeamView) : TeamPresenter {
override fun loadTeamDetail(team_id: String) {
val call = RetrofitConfig().getApi().getTeam(team_id)
call.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful()) {
val res = response.body()
res?.let { teamView.onSuccess(it) }
}
}
override fun onFailure(call: Call, t: Throwable) {
Log.e("PrevMatchFragment", t.toString())
}
})
}
}
TeamModel
data class TeamModel(
val teams: ArrayList
)
data class TeamModeLResult(
val idTeam: String,
val strTeam: String,
val strAlternate: String,
val strSport: String,
val strStadium: String,
val strTeamBadge: String
)
And
This my TeamImplsTest
class TeamImplsTest {
#Mock
private lateinit var teamView: TeamView
#Mock
private lateinit var teamPresenter: TeamPresenter
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
teamPresenter = TeamImpls(teamView)
}
#Test
fun loadTeamDetail() {
val teams = TeamModel(arrayListOf())
val teamId = "133613"
teamPresenter.loadTeamDetail(teamId)
Mockito.verify(teamView).onSuccess(teams)
}
}
i got error
Wanted but not invoked:
teamView.onSuccess(TeamModel(teams=[]));
-> at com.fathurradhy.matchschedule.domain.presenter.TeamImplsTest.loadTeamDetail(TeamImplsTest.kt:34)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
teamView.onSuccess(TeamModel(teams=[]));
-> at com.fathurradhy.matchschedule.domain.presenter.TeamImplsTest.loadTeamDetail(TeamImplsTest.kt:34)
Actually, there were zero interactions with this mock.
You're not mocking the API call as loadTeamDetail creates its own API instance.
To enable you to test the API call behaviour you could provide the API instance through your constructor, e.g.
class TeamImpls(private val api: API, private val teamView: TeamView) : TeamPresenter {
override fun loadTeamDetail(team_id: String) {
val call = api.getTeam(team_id)
This would then allow you to mock the api behaviour and verify the presenter calls the correct method when the call fails/succeeds, e.g.
class TeamImplsTest {
#Mock
private lateinit var teamView: TeamView
#Mock
private lateinit var api: API
#Mock
private lateinit var teamPresenter: TeamPresenter
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
teamPresenter = TeamImpls(api, teamView)
}
#Test
fun loadTeamDetail() {
val teams = TeamModel(arrayListOf())
val teamId = "133613"
// Use retrofit-mock to create your mockResponse.
// See: https://github.com/square/retrofit/tree/master/retrofit-mock
Mockito.`when`(api.getTeam(teamId)).thenReturn(Calls.response(teams)
teamPresenter.loadTeamDetail(teamId)
Mockito.verify(teamView).onSuccess(teams)
}
}