Kotlin - Companion Object with Receiver Function - kotlin

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

Related

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

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()

Is it possible to overload function with receiver operator in Kotlin?

I can define invoke inside a class
class A {
fun invoke(x: Double): Double {
...
}
}
and then use class instance as a functiion
val a: A()
val b = a(2.3)
right?
But can I define class instance to simulate function with receiver?
val o: MyClass()
val a: A()
val b = o.a(2.3)
Is it possible?
and then use class instance as a functiion
The invoke operator is just a way to define what happens when using the syntax () on some instance. Just like you can overload what + means, you can overload what () means. It's not exactly making an instance of A "usable as a function", but rather defining the operator () on instances of A. This is why I think it cannot really translate to "making it usable as a function with receiver".
The obvious easy way to declare an extension function would be the following:
fun MyClass.a(input: Double): Double = TODO(...)
But this doesn't seem to suit your needs. If what you really want is to add such functions as "capabilities" to some instances dynamically "on the spot" as in your example, I guess you could do so by defining such extension in a class that you provide as scope:
class A {
fun MyClass.a(x: Double): Double {
...
}
}
fun main() {
val o = MyClass()
val b = with(A()) { // brings this instance of A in scope to add the extension
o.a(2.3)
}
}

Kotlin allows defining data class in a function, why?

In kotlin, this is legal:
fun f1(): Int {
data class Data(val i: Int)
val d = Data(0)
return d.i
}
I wonder what are the consequenses of declaring a data class in a function. My best guess is that the data class is scoped to the function but I do not find anything in the doc mentionning that.
This is called Local Classes. They are mentioned in the documentation but only that they cannot have visibility modifiers.
You cannot access local class anywhere outside of the function it was declared in.
It can access any members, including private members, of the containing class.
It can access any local variables or method parameters that are in the scope of the declaring function
You can take a look at Java's local classes for more information. It should be basically the same.
A typical use case is to have a throw-away implementation of some interface.
fun main() {
val f1 = f1()
println(f1.x)
println(f1.y)
}
interface Data {
val x : Int
val y : Int
}
fun f1(): Data {
data class SpecificData(override val x: Int, override val y: Int) : Data
return SpecificData(5, 10)
}

Scope of methods of an anonymous object - Kotlin

In Kotlin if I define a method on an anonymous object, sometimes I am able to access it, while other times I am not. This seems to have something to do with scoping rules, but I am not sure what.
In the code example below, the access to example3.field.method() will cause a compilation error. Interestingly, example2.field.method() compiles just fine.
What could be the explanation for the below behaviour?
class Example3 {
val field = object {
fun method() {}
}
}
fun showcase() {
val example1 = object {
fun method() {}
}
example1.method()
println(example1::class.qualifiedName)
class Example2 {
val field = object {
fun method() {}
}
}
val example2 = Example2()
example2.field.method()
println(example2::class.qualifiedName)
val example3 = Example3()
// example3.field.method() // won't compile
println(example3::class.qualifiedName)
}
From docs Object Expressions and Declarations:
Note that anonymous objects can be used as types only in local and
private declarations. If you use an anonymous object as a return type
of a public function or the type of a public property, the actual type
of that function or property will be the declared supertype of the
anonymous object, or Any if you didn't declare any supertype. Members
added in the anonymous object will not be accessible.
Demonstrated in code sample below:
class Example4{
val publicObj = object{
val x = 1
}
private val privateObj = object{
val x = 2
}
fun showcase(){
val scopedObj = object{
val x = 3
}
println(publicObj.x) // ERROR : unresolved reference: x
println(privateObj.x) // OK
println(scopedObj.x) // OK
}
}
Pawel gave the correct answer to your question, pointing to the documentation:
the actual type of that function or property will be the declared supertype of the anonymous object, or Any if you didn't declare any supertype.
But just adding that if you really need to access example3.field.method() you could declare a supertype to field in Example3:
interface MyInterface {
fun method()
}
class Example3 {
val field = object: MyInterface {
override fun method() {}
}
}
fun main() {
val example3 = Example3()
example3.field.method()
}

Access methods outside companion object - Kotlin

I'm pretty new to kotlin and I was wondering if it's possible, and if it's against best practice to access methods and variables that are outside a companion object, from within the companion object.
For example
class A {
fun doStuff(): Boolean = return true
companion object{
public fun stuffDone(): Boolean = return doStuff()
}
}
or something like that
Thank you
doStuff() is an instance method of a class; calling it requires a class instance. Members of a companion object, just like static methods in Java, do not have a class instance in scope. Therefore, to call an instance method from a companion object method, you need to provide an instance explicitly:
class A {
fun doStuff() = true
companion object {
fun stuffDone(a: A) = a.doStuff()
}
}
You can also call functions that are outside of companion object block.
class A {
fun doStuff() = true
companion object {
val a = A()
fun stuffDone() = a.doStuff()
}
}
A correct way to do what you want is:
create a instance of your main class inside Companion Objects
initialize the instance when you instance the Main Class
call the instance of the class when you need to call methods, set or
get variables.
For a better control and better decouple level, you should access/set/get
stuff from the main class (A) from the Companion Object instead of calling getInstance() to access the Main Class stuff.
Example:
class A {
private val myPrivateVariable:String = "MY_STUFF"
init{
setupInstance(this#A)
}
companion object{
private val instance:A? = null
fun setupInstance(a:A){
this.instance = a
}
//IF YOU WANT TO ACCESS CLASS A METHODS/VARIABLES DIRECTLY YOU CAN CALL IT
fun getInstance():A{
return this.instance?.let{
it
}?:throw NullPointerException("INSTANCE NOT SET YET")
}
//ACCESSING CLASS A VARIABLES FROM COMPANION OBJECT (BETTER ERROR CONTROL AND DECOUPLED)
fun setMyVariable(string:String){
this.getInstance().myPrivateVariable = string
}
fun getMyVariable(string:String) = this.getInstance().myPrivateVariable
}
}