In kotlin Declaraing extensions as members, what does it mean "the dispatch of such functions is virtual with regard to the dispatch receiver type, but static with regard to the extension receiver type."
Is this mean that "the extension function doesn't follows its reciever's class type. It follows parameter type(?in this code call method parameter)."
please give your warmhearted and generous advice
open class Base { }
class Derived : Base() { }
open class BaseCaller {
open fun Base.printFunctionInfo() {
println("Base extension function in BaseCaller")
}
open fun Derived.printFunctionInfo() {
println("Derived extension function in BaseCaller")
}
fun call(b: Base) {
b.printFunctionInfo() // call the extension function
}
}
class DerivedCaller: BaseCaller() {
override fun Base.printFunctionInfo() {
println("Base extension function in DerivedCaller")
}
override fun Derived.printFunctionInfo() {
println("Derived extension function in DerivedCaller")
}
}
fun main() {
BaseCaller().call(Base()) // "Base extension function in BaseCaller"
DerivedCaller().call(Base()) // "Base extension function in DerivedCaller" - dispatch receiver is resolved virtually
DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" - extension receiver is resolved statically
}
Since you have linked the documentation, I take it that you have read the following part
The instance of the class in which the extension is declared is called
dispatch receiver, and the instance of the receiver type of the extension method is called extension receiver.
After you have understood the above terminology, you need to understand following points
If you don't know about virtual methods read this
Extensions are resolved statically. Consider the following code block
fun call(b: Base) {
// This will always call extension function defined on the Base class
// even if you pass an object of Derived class
b.printFunctionInfo() // call the extension function
}
// This calls the printFunctionInfo defined on the Base, even though we pass Derived
DerivedCaller().call(Derived())
Now to your question
the dispatch of such functions is virtual with regard to the dispatch
receiver type, but static with regard to the extension receiver type.
With the Extensions are resolved statically point we have established that no matter which object you pass (Base or Derived) the call function will always invoke an extension function defined on the Base type.
But which extension function will be invoked? one in the Base class or the one in Derived class ?
This depends on the type of object which invokes the call function, if you invoke the call with an Object of Base then the extension in the base class will be invoked and if you use the Derived object then the extension in Derived class will be invoked.
Related
I'm trying to create extension function like this:
fun UHFTAGInfo.toReadUhfTagInfo(): ReadUhfTagInfo {
return ReadUhfTagInfo(this.epc, this.count, this.rssi.toIntOrNull())
}
It is supposed to convert UHFTAGInfo (from java library) to ReadUhfTagInfo (my class in Kotlin).
I'm trying to use it like this:
UHFTAGInfo i = getUHFTAGInfo();
ReadUhfTagInfo ri = i.toReadUhfTagInfo();
At this moment my toReadUhfTagInfo function is at top level, but finally I want to put it in my ReadUhfTagInfo class, like this:
class ReadUhfTagInfo(var epc: String, var cnt: Int, var rssi: Int?)
{
fun UHFTAGInfo.toReadUhfTagInfo(): ReadUhfTagInfo {
return ReadUhfTagInfo(this.epc, this.count, this.rssi.toIntOrNull())
}
}
You can call Kotlin extension functions from Java, sure, but you can't call them with extension function syntax, you must call them like static methods. If you, for example, define
// file: Foo.kt
fun Bar.baz() { ... }
then in Java, you would call this as
FooKt.baz(bar);
I don't think you want to have toReadUhfTagInfo as a member function on the ReadUhfTagInfo class. That would imply that, in order to convert a UHFTAGInfo to a ReadUhfTagInfo, you already need a ReadUhfTagInfo object (which will presumably go unused except to serve as the receiver object.
Extension functions defined inside a class are member extensions and essentially have two receivers.
You can declare extensions for one class inside another class. Inside such an extension, there are multiple implicit receivers - objects whose members can be accessed without a qualifier. An instance of a class in which the extension is declared is called a dispatch receiver, and an instance of the receiver type of the extension method is called an extension receiver.
If you want the extension method to act like a static method in Java (i.e. not require an instance of the enclosing class to execute), then you do the same thing we do with all static methods in Kotlin: We put it in a companion object.
class ReadUhfTagInfo(var epc: String, var cnt: Int, var rssi: Int?)
{
companion object {
fun UHFTAGInfo.toReadUhfTagInfo(): ReadUhfTagInfo {
return ReadUhfTagInfo(this.epc, this.count, this.rssi.toIntOrNull())
}
}
}
As pointed out in the comments, this will still require the name to be imported into the current scope (as all extension methods do), but it won't require a receiver of type ReadUhfTagInfo to call anymore.
Say I have a Java class Metrics. I defined some extension functions on Metrics in Kotlin
fun Merics.expose(name: String, value: Number) {
// do something
}
Note that the Java class Metrics also has a method called expose but with different signature.
I created a test where I mocked a metrics objects and call a code path where the extension function expose should be called. But how can I verify that those extension functions are invoked?
I tried to use mockk and mockito-kotlin, none of them seem to know that metrics object has a new function called expose with different signatures.
You can not verify that the extension function is called on your mock, as it is not part of that class. The extension function is just a top-level function with a receiver (in your case an instance of Metrics).
But you can still verify that the extension function was called in your code
You can do this using mockkStatic.
You are passing the the path of the (generated) extension function. Let's assume you created your Metrics extension function in package org.com. The extension class should get generated in: com.org.MericsExtensionKt.
A test that wants to verify the call to your extension function could look like:
#Test
fun check_metrics_expose_extension_was_called() {
mockkStatic("com.org.MericsExtensionKt")
// call your function that is calling Metrics.expose()
// classUnderTest.someFunction()
// this verifies a call to the extension function and their parameters
verify { any<Metrics>().expose(name = any(), value = any()) }
}
While taking the "Kotlin for Java Developers" Coursera course, I came across this construction, where an extension function for a contained instance seems to have access to the containing class' private fields. Note how the Data class is implemented outside of Container and does not have access to Container's fields, but the Data.printMe() extension function can access Container's private containerVal member:
data class Data (val data: String)
class Container (private val containerVal: String, val data: Data){
fun Data.printMe() {
println("data: $data - in container: ${this#Container.containerVal}")
}
}
fun main() {
val c = Container("mycontainer", Data("mydata"))
// Can I call Data.printMe without using `with`?
// val d = c.data.printMe();
with (c) {
// Prints: data: mydata - in container: mycontainer
data.printMe();
}
}
Pretty cool that Data.printMe can access Container's private members. The with(c) is apparently what allows that. I can see lots of use cases for this, just like how in the course example extension functions for a game's Cells can access the containing Board without the Cell class itself being polluted by and tightly coupled with Board.
Does this "approach" have a name? And is there a syntax to call c.data.printMe "directly" without using with? The with documentation doesn't mention this use case.
The printMe() method has two receivers: the dispatch receiver and the extension receiver. The dispatch receiver is the instance of the class in which the extension is declared, and the extension receiver is the instance of the receiver type of the extension method. You are accessing contaiverVal in the context of a Container instance (dispatch receiver), not a Data instance (extension receiver).
If you try to do the opposite and access a private property of the Data class inside the printMe(), you will get a compiler error.
when you define an extension function as a member of a class, it is only usable in the context that class. You can access it inside the class or using a scope function where the context object is available as a lambda receiver (this). Those functions are run, with and apply.
I am having a problem getting IllegalAccessError for the following example:
I have a base class declared in a gradle module called arch
abstract class BaseClass {
protected abstract val value: Int
fun run() {
Log.d("Printme", "value $value")
}
protected inline fun getMyValue(): Lazy<Int> = lazy {
getAnEight()
}
protected fun getAnEight() = 8
}
and a child class declared in gradle module called app
class ChildClass: BaseClass() {
override val value by getMyValue()
}
It is worth saying I am creating a Kotlin project using Android Studio, but these classes are all simple Kotlin objects without any Android specific references. Of course these two modules also have different packages.
Now, from my main entry method I am doing the following (inside app module)
ChildClass().run()
I am calling my run() method declared in base class, which is accessing lazy initiated value property, which is in turn calling getAnEight() method. Since all methods are protected I would expect there is no reason a child class can't call all these. Even if one of the methods is marked as inline and this call gets replaced with method contents, it should still be able to call getAnEight() just fine.
Instead I am receiving IllegalAccessError saying BaseClass.getAnEight() is inaccessible to class ChildClass$$special$$inlined$getMeValue$1. This problem disappears when I remove inline modifier, or if I place BaseClass in the same package as ChildClass.
Is this a bug in Kotlin compiler? Or can someone explain to me this behavior if it's working as intended? Thanks in advance!
https://kotlinlang.org/docs/reference/inline-functions.html#public-inline-restrictions
When an inline function is public or protected and is not a part of a
private or internal declaration, it is considered a module's public
API. It can be called in other modules and is inlined at such call
sites as well.
This imposes certain risks of binary incompatibility caused by changes
in the module that declares an inline function in case the calling
module is not re-compiled after the change.
To eliminate the risk of such incompatibility being introduced by a
change in non-public API of a module, the public API inline functions
are not allowed to use non-public-API declarations, i.e. private and
internal declarations and their parts, in their bodies.
An internal declaration can be annotated with #PublishedApi, which
allows its use in public API inline functions. When an internal inline
function is marked as #PublishedApi, its body is checked too, as if it
were public.
EDIT: I made some bytecode research. The problem is that protected getMyValue() function is inlined into public constructor. In decompiled bytecode, ChildClass public constructor has a following line:
Lazy var4 = LazyKt.lazy((Function0)(new ChildClass$$special$$inlined$getMyValue$1(this)));
As you can see, it creates an instance of class ChildClass$$special$$inlined$getMyValue$1. Let's look at its declaration:
public final class ChildClass$$special$$inlined$getMyValue$1 extends Lambda implements Function0 {
final BaseClass this$0;
public ChildClass$$special$$inlined$getMyValue$1(BaseClass var1) {
super(0);
this.this$0 = var1;
}
public Object invoke() {
return this.invoke();
}
public final int invoke() {
return this.this$0.getAnEight(); // Here lies the problem
}
}
When you create a ChildClass instance, its constructor only creates a ChildClass$$special$$inlined$getMyValue$1 instance, that does not throw any errors. But when you call run(), invoke() method of class above is called. This method is public, its class is public, constructor was public, but getAnEight method is protected. That's how we get this error.
I was reading kotlin official tutorial, Under the data class topic, I came up with a following point.
If a supertype has the componentN() functions that are open and return compatible types, the corresponding functions are generated for the data class and override those of the supertype. If the functions of the supertype cannot be overridden due to incompatible signatures or being final, an error is reported;
My Questions are,
1) What is componentN() functions ?
2) Does the data class override the open function automatically ?
3) Is following code correct ?
open class SuperDataClass {
open fun componentN() {
println("from super class")
}
}
data class DataClassExample (var name: String): SuperDataClass() {
//
}
1) What is componentN() functions ?
They are operator functions corresponding to the properties in their order of declaration.
Example:
data class Person(name: String, age: Int)
the class above will have a component1 and a component2 function, allowing the access through destructuring declaration to name and age, in that order.
Take in consideration that componentN function is just to reference 1st, 2nd, 3rd, ..., Nth component. The componentN function itself is never generated.
2) Does the data class override the open function automatically ?
In data class, you are extending from Any class, you aren't overriding automatically any function. componentN functions are generated at compile time.
3) Is following code correct ?
open class SuperDataClass {
open fun componentN() {
println("from super class")
}
}
data class DataClassExample (var name: String): SuperDataClass() {
//
}
Yes, it will compile and will run correctly. But this is only because, as I said before, componentN isn't generated for data classes.
However, in this case a component1 is generated for the property name of DataClassExample. As the quote of the documentation you posted says: if you try this code, you will have an error.
open class SuperDataClass {
open fun component1() {//<-- note this
println("from super class")
}
}
data class DataClassExample (var name: String): SuperDataClass() {
//
}
The specific error is:
[DATA_CLASS_OVERRIDE_CONFLICT] Function 'component1' generated for the data class conflicts with member of supertype 'SuperDataClass'