Kotlin allows defining data class in a function, why? - kotlin

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

Related

Kotlin: Hashmap of interface methods by the implementing class name

I have a list of clases that implement a specific interface. The ability to construct those clases or not is not static (so it's not possible to use when(className)), and can be configured so I want to be able to create some clases or call some methods based on a hashMap of allowed "constructors". Then if the key identifying a class is in present in the hashmap I can call the corresponding method, otherwise I can safely ignore. Let me illustrate:
Let's say I have an interface like
interface Instanceable {
data class Config(
val bar: Whatever
)
fun getIntance(config: Config): Instanceable
}
Then I have several (let's say 10) classes that implement this interface
class Implementation1() : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private lateinit var foo: Whatever
override fun getIntance(config: Config) = Implementation1().also{ this#Implementation1.foo = config.bar }
}
I want to create a hashmap of the methods by the identifiers, so later down the lane I can grab the method from the hashMap by the key ID and just invoke() the value if it's there. Something like:
allowedInstances("INSTANCE_1")?.let{ it.invoke(someConfig) }
In order to do this I tried to create a hashMap of methods like this:
private val allowedInstances = mutableHashMapOf<String, Instanceable.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
But it fails with:
Type mismatch.
Required: Instanceable.(Instanceable.Config) → Instanceable
Found: KFunction2<Implementation1, Instanceable.Config, Instanceable>
If I create the hashmap directly and let the compiler infer the types like this:
private val allowedInstances = mutableHashMapOf(
Implementation1.ID to Implementation1::getIntance,
Implementation2.ID to Implementation2::getIntance,
Implementation4.ID to Implementation4::getIntance,
)
Checking the type of the hashmap shows:
HashMap<String, out KFunction2<Nothing, Instanceable.Config, Instanceable>>
In fact I can do:
private val allowedInstances = mutableHashMapOf<String, Nothing.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
So the actual question is:
Why the function of the second hashMap parameter has Nothing as the receptor? Why I cannot have the interface Instanceable instead?
Edit: Still not good to have the Nothing there:
allowedInstances["INSTANCE_1"]?.let{ it.invoke(Nothing, someConfig) }
//Fails with: Classifier 'Nothing' does not have a companion object, and thus must be initialized here
Edit 2: All of the errors are in compile time
Your function type
Instanceable.(Instanceable.Config) -> Instanceable
is describing an extension function on an instance of Instanceable. You need to omit the receiver from the function type to be able to match your constructors' signature:
(Instanceable.Config) -> Instanceable
Edit: The other half of the problem is that you define getInstance() as a member function of the class. So you have to create an invalid instance of your class to use to create a valid instance, which doesn't make sense.
I would delete the getInstance() function from your interface, and put the equivalent code in the constructor of your class. Then you can define a function type in your Map that constructs your items.
interface Instanceable {
data class Config(
val bar: Whatever
)
// REMOVE this: fun getIntance(config: Config): Instanceable
}
class Implementation1(config: Config) : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private val foo: Whatever = config.bar
}
private val allowedInstances = mutableHashMapOf<String, (Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = ::Implementation1
// and so on...
// If there's an implementation that has no config, you can use a lambda:
class NoConfigImplementation : Instanceable {
companion object {
const val ID = "INSTANCE_2"
}
}
allowedInstances[NoConfigImplementation.ID] = { _ -> NoConfigImplementation() }

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

When should I use delegation in Kotlin?

I have been going through multiple links (One, Two) and documentation regarding the delegate pattern and somewhat understand the advantage it brings in the form of "composition over inheritance". I can see how the inbuilt delegate properties (lazy, vetoable, map, observable) are useful; but having a hard time understanding 2 areas:
1. Why/When should I write a custom delegate for property? How is it better than overriding getter/setter of that property?
Comparing the 2 approaches:
private var withoutDelegate: String = ""
get() = DataHelper.getLatestData(::withoutDelegate.name)
set(value) {
DataHelper.setLatestData(value)
field = value
}
val withDelegate by StringDelegateProvider()
class StringDelegateProvider {
operator fun getValue(thisRef: String?, property: KProperty<*>): String {
return DataHelper.getLatestData(property.name)
}
}
2. At the class level, how is delegation better than traditional composition patterns?
Comparing the 2 approaches - composition without delegation seems much more concise:
interface Base {
fun print()
}
class BaseImpl1(val x: Int) : Base {
override fun print() { print(x) }
}
class BaseImpl2(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun clientFunctionWithDelegation() {
val i1 = BaseImpl1(10)
val i2 = BaseImpl2(10)
val b1 = Derived(i1)
val b2 = Derived(i2)
b1.print()
b2.print()
}
fun clientFunctionWithoutDelegation(){
//wihtout extending Base, we can still create multiple types of Base and use them conditionally.
val i1: Base = BaseImpl1(10)
val i2: Base = BaseImpl2(10)
i1.print()
i2.print()
}
Would appreciate if the community can share some use-cases where delegation can help.
1: You can reuse the delegate without having to override get and/or set each time. Example: the lazy delegate
2: let's say you want to create a MutableList that prints the list every time you mutate the list. You don't want to reimplement MutableList, you just want to override the functions mutating the list. So instead of manually delegating every call, you just say class PrintList<T>(original: MutableList<T>) by original and you just override the functions you care about

Kotlin - Delegation chain

In Kotlin, is it possible to have a Delegation chain ?
To demonstrate what i'm trying to achieve here's the example (https://kotlinlang.org/docs/reference/delegation.html) in the kotlin doc modified :
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { println(x) }
}
class Derived(var b: Base, val someData: Float = 10f) : Base by b
class SecondDerived(var b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
val derived = Derived(b)
val secondDerived: Base = SecondDerived(derived)
secondDerived.print()// prints 10
if (secondDerived is Derived) println(secondDerived.someData) //here secondDerived is Derived == false
}
I would expect "secondDerived" to be of type "Derived" but the cast say it is not.
I suspect that in memory the secondDerived base is indeed of type Derived, but the compiler cannot see that. Is there any way to make the cast work ?
On the JVM, a class can have only a single superclass, and Kotlin's class delegation does not change it in any way. All it does is generate implementations of Base interface methods that delegate to the Derived instance. It does not affect is checks.