Can I add companion extension without first having companion object within a class? - kotlin

For the below code, I can add invoke extension to the Companion
operator fun MyValue.Companion.invoke(value: Int) =
MyValue(value.toString())
class MyValue(private val value: String) {
companion object
fun print() = println("value = $value")
}
This enable me to call something as below
MyValue(1).print()
But as you see originally MyValue don't need the companion object.
I wonder if MyValue is without the companion object, i.e.
class MyValue(private val value: String) {
fun print() = println("value = $value")
}
Is it possible for me to still create a Companion extension function? e.g.
operator fun MyValue.Companion.invoke(value: Int) =
MyValue(value.toString())

You can add a secondary constructor to your class that accept an Int,
class MyValue(private val value: String) {
constructor(value: Int) : this(value.toString())
fun print() = println("value = $value")
}
Now you can call both, MyValue("10").print() and MyValue(10).print()

Related

Can you make a list of value classes, with the list being bound to the list of values?

Since value classes (aka inline classes) are not classes at runtime but the value class, is it possible to make a list of them, and the list is bound to the list of values?
If i couldn't explain that very clearly, here is a code example:
class Foo(var i)
#JvmInline
value class FooWrapper(val foo: Foo)
fun main() {
val fooList = mutableListOf(Foo(1), Foo(2))
val wrappedFooList = fooList.???<FooWrapper>()
// fooList and wrappedFooList are the same list at runtime, so when you insert a value to fooList, it gets "added" to wrappedFooList as `FooWrapper(added)`
// This is what im currently using, but this is a seperate list, so when a value gets inserted into fooList, it doesn't get inserted here.
val wrappedFooListButNotTheSameList = fooList.map { FooWrapper(it) }
fooList.add(Foo(3)) // FooWrapper(Foo(3)) now exists in wrappedFooList
}
Since value classes (aka inline classes) are not classes at runtime but the value class
This is only true sometimes. Inlining does not always happen, and the class file for the inline class does exist.
As soon as you start using generics, inlining goes out of the window. That is, your list of FooWrapper would not be inlined at all.
Documentation:
However, sometimes it is necessary to keep wrappers around. As a rule of thumb, inline classes are boxed whenever they are used as another type.
See also the code sample that follows that. This is likely because when they are used as another type, code that doesn't know about the inline class is likely going to be interacting with the wrapper, and unexpected behaviours would occur if they are not boxed.
With all that in mind, if you still want two lists of unrelated types, that are "linked" together, you can first encode the conversion between the types with an interface:
interface ConvertibleTo<T> {
val converted: T
}
data class Foo(var i: Int): ConvertibleTo<FooWrapper> {
override val converted get() = FooWrapper(this)
}
#JvmInline
value class FooWrapper(val foo: Foo): ConvertibleTo<Foo> {
override val converted get() = foo
}
Then make a ConvertList<T, U> and a ConvertListIterator<T, U> by delegating everything (yes this is a lot of boilerplate). The built-in by can't help here because you are also adding an extra .converted on every U value. Instead of the interfaces, you can also add T.() -> U and U.() -> T in the constructor parameters.
class ConvertList<T: ConvertibleTo<U>, U: ConvertibleTo<T>>(private val list: MutableList<T>): MutableList<U> {
override val size: Int
get() = list.size
override fun contains(element: U) = list.contains(element.converted)
override fun containsAll(elements: Collection<U>) =
list.containsAll(elements.map(ConvertibleTo<T>::converted))
override fun get(index: Int) =
list[index].converted
override fun indexOf(element: U) =
list.indexOf(element.converted)
override fun isEmpty() = list.isEmpty()
override fun iterator() = ConvertListIterator(list.listIterator())
override fun lastIndexOf(element: U) = list.lastIndexOf(element.converted)
override fun add(element: U) = list.add(element.converted)
override fun add(index: Int, element: U) = list.add(index, element.converted)
override fun addAll(index: Int, elements: Collection<U>) =
list.addAll(index, elements.map(ConvertibleTo<T>::converted))
override fun addAll(elements: Collection<U>) =
list.addAll(elements.map(ConvertibleTo<T>::converted))
override fun clear() = list.clear()
override fun listIterator() = ConvertListIterator(list.listIterator())
override fun listIterator(index: Int) = ConvertListIterator(list.listIterator(index))
override fun remove(element: U) = list.remove(element.converted)
override fun removeAll(elements: Collection<U>) =
list.removeAll(elements.map(ConvertibleTo<T>::converted))
override fun removeAt(index: Int) = list.removeAt(index).converted
override fun retainAll(elements: Collection<U>) =
list.retainAll(elements.map(ConvertibleTo<T>::converted))
override fun set(index: Int, element: U) = list.set(index, element.converted).converted
override fun subList(fromIndex: Int, toIndex: Int) = ConvertList(list.subList(fromIndex, toIndex))
}
class ConvertListIterator<T: ConvertibleTo<U>, U: ConvertibleTo<T>>(private val iter: MutableListIterator<T>): MutableListIterator<U> {
override fun hasPrevious() = iter.hasPrevious()
override fun nextIndex() = iter.nextIndex()
override fun previous() = iter.previous().converted
override fun previousIndex() = iter.previousIndex()
override fun add(element: U) = iter.add(element.converted)
override fun hasNext() = iter.hasNext()
override fun next() = iter.next().converted
override fun remove() = iter.remove()
override fun set(element: U) = iter.set(element.converted)
}
Usage:
val list = mutableListOf(Foo(1), Foo(2))
val list2 = ConvertList(list)

Kotlin - Companion Object with Receiver Function

I guess it's an outright "NO", but here is my class
class KotlinReceiverFunction {
val multiplyBy = fun Int.(value: Int) = this*value
companion object {
fun printMultiplicationResult(a: Int, b: Int) = a.multiplyBy(b) // error
}
}
My question - is Receiver Function only allowed within a specific scope i.e. same as Lambdas? Or, can I get to work somehow in a companion object?
Regards
There are no restrictions on where a function with a receiver could be used. The problem in your case is different: multiplyBy is an instance member, so you need an instance of KotlinReceiverFunction to use it. It would be exactly the same if this function would not use a receiver:
val multiplyBy = fun (value1: Int, value2: Int) = value1*value2
companion object {
fun printMultiplicationResult(a: Int, b: Int) = multiplyBy(a, b) // error
}
To fix the problem you need to initialize an instance of KotlinReceiverFunction:
fun printMultiplicationResult(a: Int, b: Int) =
with(KotlinReceiverFunction()) { a.multiplyBy(b) } // works
Although, I think this is not exactly what you need.
This has nothing to do with receivers. You are using the correct receiver. It's just that things declared outside the companion object is out of scope inside the companion object:
class KotlinReceiverFunction {
val foo = 1
companion object {
fun bar() {
println(foo) // error
}
}
}
Think of KotlinReceiverFunction and its companion object as two disconnected things. Under the hood on the JVM, they are just two separate classes KotlinReceiverFunction and KotlinReceiverFunction$Companion, each with their own instance members.
In the companion object, You would need an instance of KotlinReceiverFunction to access its foo property. This instance acts as the receiver.
companion object {
fun bar() {
println(KotlinReceiverFunction().foo) // OK
}
}
Similarly, the multiplyBy function needs an instance of KotlinReceiverFunction as its receiver (dispatch receiver). But this function also needs an Int as a receiver (extension receiver)!
This makes it a little harder to access than foo when you are in the companion object. You would need to provide the instance of KotlinReceiverFunction with a scope function, as in broot's answer.
If you just declare the function inside the companion object, then it will work as you expect:
class KotlinReceiverFunction {
companion object {
val multiplyBy = fun Int.(value: Int) = this*value
fun printMultiplicationResult(a: Int, b: Int) = a.multiplyBy(b)
}
}
I don't see a reason why this needs to be a val initialised with an anonymous function. You could have just done:
private fun Int.multiplyBy(value: Int) = this * value

Is there a way to define an implicit ctor for a Kotlin class?

as the title says it, is there any way to define a constructor or convertion for a class/object that can be called implicitly like this?
class Person {
val name: String
val mood: Mood = Mood.Happy
fun toString(): String = "$name is $mood"
}
fun main() {
// Calls the magic implicit ctor
val steve: Person = "Steve"
// Prints "Steve is happy"
println("$steve")
}

Kotlin Generics, correct syntax for type parameters

I have the following class, which basically gets a JSON string from AWS, then converts it to an instance of a data class...
class SecretsManager(region: String) {
private val gson = Gson()
private val smClient = AWSSecretsManagerClientBuilder.standard().withRegion(region).build()
fun <T> getSecret(id: String): T {
val req = GetSecretValueRequest().withSecretId(id)
val json = smClient.getSecretValue(req).getSecretString()
return gson.fromJson(json, T::class.java)
}
}
To be used like this...
val myInstance = SecretsManager("eu-west-2").getSecret<MyDataClass>("myId")
Currently, I get an error - Cannot use 'T' as reified type parameter. I can get around this by marking the function as inline and T as reified , but then I can't access the private attributes from within the function.
What's the best way to do this in Kotlin?
You need to add another parameter to the getSecret method, and also need to add an inline reified method for that to work. See the code below
class SecretsManager(region: String) {
private val gson = Gson()
private val smClient = AWSSecretsManagerClientBuilder.standard().withRegion(region).build()
fun <T> getSecret(type: Class<T>, id: String): T {
val req = GetSecretValueRequest().withSecretId(id)
val json = smClient.getSecretValue(req).getSecretString()
return gson.fromJson(json, type)
}
inline fun <reified T> getSecret(id: String): T = getSecret(T::class.java, id)
}

Kotlin How to create dynamic Object

In javascript we can do something like this
function putritanjungsari(data){
console.log(data.name)
}
let data = {
name:"putri",
div:"m4th"
}
putritanjungsari(data)
In kotlin, i'am creating a function that accept an object as parameter then read it's properties later, how to do that in kotlin that targeting JVM?
If I understood your question correct, you are trying to have a variable that associates keys with some value or undefined(null in kt) if none are found. You are searching for a Map
If you don't know what types you want, you can make a map of type Any? So
Map<String, Any?>
Which is also nullable
Map<String, Any>
If you don't want nullables
Your code for example:
fun putritanjungsari(data: Map<String, Any?>){
print(data["name"])
}
val data: Map<String, Any?> =mapOf(
"name" to "putri",
"div" to "m4th"
)
putritanjungsari(data)
Note that you can't add new keys or edit any data here, the default map is immutable. There is MutableMap (which is implemented the same, only it has a method to put new data)
You can apply the property design pattern to solve your problem.
Here is its implementation in Kotlin:
interface DynamicProperty<T> {
fun cast(value: Any?): T
fun default(): T
companion object {
inline fun <reified T> fromDefaultSupplier(crossinline default: () -> T) =
object : DynamicProperty<T> {
override fun cast(value: Any?): T = value as T
override fun default(): T = default()
}
inline operator fun <reified T> invoke(default: T) = fromDefaultSupplier { default }
inline fun <reified T> required() = fromDefaultSupplier<T> {
throw IllegalStateException("DynamicProperty isn't initialized")
}
inline fun <reified T> nullable() = DynamicProperty<T?>(null)
}
}
operator fun <T> DynamicProperty<T>.invoke(value: T) = DynamicPropertyValue(this, value)
data class DynamicPropertyValue<T>(val property: DynamicProperty<T>, val value: T)
class DynamicObject(vararg properties: DynamicPropertyValue<*>) {
private val properties = HashMap<DynamicProperty<*>, Any?>().apply {
properties.forEach { put(it.property, it.value) }
}
operator fun <T> get(property: DynamicProperty<T>) =
if (properties.containsKey(property)) property.cast(properties[property])
else property.default()
operator fun <T> set(property: DynamicProperty<T>, value: T) = properties.put(property, value)
operator fun <T> DynamicProperty<T>.minus(value: T) = set(this, value)
}
fun dynamicObj(init: DynamicObject.() -> Unit) = DynamicObject().apply(init)
You can define your properties these ways:
val NAME = DynamicProperty.required<String>() // throws exceptions on usage before initialization
val DIV = DynamicProperty.nullable<String>() // has nullable type String?
val IS_ENABLED = DynamicProperty(true) // true by default
Now you can use them:
fun printObjName(obj: DynamicObject) {
println(obj[NAME])
}
val data = dynamicObj {
NAME - "putri"
DIV - "m4th"
}
printObjName(data)
// throws exception because name isn't initialized
printObjName(DynamicObject(DIV("m4th"), IS_ENABLED(false)))
Reasons to use DynamicObject instead of Map<String, Any?>:
Type-safety (NAME - 3 and NAME(true) will not compile)
No casting is required on properties usage
You can define what the program should do when a property isn't initialized
Kotlin is statically typed language, so it required a param type to be precisely defined or unambiguously inferred (Groovy, for instance, addresses the case by at least two ways). But for JS interoperability Kotlin offers dynamic type.
Meanwhile, in your particular case you can type data structure to kt's Map and do not argue with strict typing.
You have to use Any and after that, you have to cast your object, like this
private fun putritanjungsari(data : Any){
if(data is Mydata){
var data = data as? Mydata
data.name
}
}
Just for the sake of inspiration. In Kotlin, you can create ad hoc objects:
val adHoc = object {
var x = 1
var y = 2
}
println(adHoc.x + adHoc.y)