Generic method overloading and precedence in kotlin - kotlin

internal fun <T> T.printMe(): Unit { // generic function for all types
println(this)
}
internal fun CustomType.printMe(): Unit { // function for only customType class
println("**** ${this} ******")
}
internal fun <T> printCustom(content: T) {
content.printMe()
}
data class CustomType(val print: String)
fun main() {
"Hello".printMe()
CustomType("Testing").printMe()
printCustom(CustomType("Testing"))
}
Result
Hello
**** CustomType(print=Testing) ******
CustomType(print=Testing)
I wonder why calling printMe() via another generic function printCustom() will prefer T.printMe() instead of CustomType.printMe()

Related

Kotlin: use generic on interface level as argument type for function

Is it impossible to use generic on interface level as argument type for function?
I read about out and in keywords but as I understand they don't work for this case.
interface BaseB
open class ChildB1: BaseB
open class ChildB2: BaseB
abstract class BaseMapper<V: BaseB> {
open fun test(v: V) {
return
}
}
class TestMapper1: BaseMapper<ChildB1>() {
override fun test(v: ChildB1) {
return
}
}
class TestMapper2: BaseMapper<ChildB2>() {
override fun test(v: ChildB2) {
return
}
}
#Test
fun t() {
//ERROR
val mappers: List<BaseMapper<BaseB>> = listOf(TestMapper1(), TestMapper2())
mappers[0].test(ChildB1())
}
A BaseMapper<ChildB1> is not logically a BaseMapper<BaseB>. It consumes ChildB’s, so if you passed some other implementation of Base it would cause a ClassCastException if the compiler let you do that. There is no common subtype of your two subclasses besides Nothing, so the only way to put both of these types in the same list is to make it a List<BaseMapper<in Nothing>>.
Example of why it is not logically a BaseMapper<BaseB>:
open class ChildB1: BaseB {
fun sayHello() = println("Hello world")
}
class TestMapper1: BaseMapper<ChildB1>() {
override fun test(v: ChildB1) {
v.sayHello() // if v is not a ChildB1, this would be impossible
}
}
//...
val impossibleCast: BaseMapper<BaseB> = TestMapper1()
// TestMapper1 cannot call sayHello() because it's undefined for ChildB2.
// This is impossible:
impossibleCast.test(ChildB2())
// ...so the compiler prevents you from doing the impossible cast in the first place.

A question about Kotlin Function Type cast

Code fragment in Kotlin
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(receiver, probeCompletion)
else {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
}
}
What I want to know is How (suspend R.() -> T) AKA Function Type can be recognized
as BaseContinuationImpl which is a Classify Type even to cast as `Function2'.
Any help will be appreciated.
There are no separate function types and class types. Function types are just types that can be executed with specific arguments and specific return type. They are interchangeable with Function0, Function1, etc. interfaces and they contain a single invoke() function.
We can implement a function type by our class:
class MyClass : (suspend () -> Unit) {
override suspend fun invoke() {}
}
Now, let's get this code:
val lambda: (suspend () -> Unit) = {}
After disassembling we see that our lambda is compiled to:
final class FooKt$foo$lambda$1 extends kotlin/coroutines/jvm/internal/SuspendLambda implements kotlin/jvm/functions/Function1 {
...
public final invoke(Ljava/lang/Object;)Ljava/lang/Object;
...
}
It extends SuspendLambda which is a subtype of BaseContinuationImpl. It also implements Function1 and contains invoke function which makes it a function type.

Pass a static method as a parameter to another method in Kotlin

As per this question, a function can be passed as a parameter to another function as shown below
fun foo(m: String, bar: (m: String) -> Unit) {
bar(m)
}
fun buz(m: String) {
println("another message: $m")
}
fun something() {
foo("hi", ::buz)
}
Similarly, we can also pass a method from a class
class OtherClass {
fun buz(m: String) {
println("another message: $m")
}
}
foo("hi", OtherClass()::buz)
But what if the method we want to pass is static (within a companion object)?
class OtherClass {
companion object {
fun buz(m: String) {
println("another message: $m")
}
}
}
I am aware that since it is static we can simply call the method directly without having to resort to passing it as a parameter, however, there are still some situations (such as when taking advantage of pre-existing code) where this would be useful.
To access companion object of class use ${className}.Companion. So...
foo("hit", OtherClass.Companion::buz).

Type inference only works for extension function

The following code works fine and the call to the foo.get() extension function returns the correct type BarImpl.
open class Bar
class BarImpl: Bar()
class Foo<T : Bar>
inline fun <reified T : Bar> Foo<T>.get(): T {
return SomeMap(this).get(T::class)
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
But when I try to move Foo<T>.get() into the class the type inference fails
class Foo<T : Bar> {
inline fun <reified T : Bar> get(): T {
return SomeMap(this).get(T::class)
}
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
error: type inference failed: Not enough information to infer parameter T in inline fun get(): T
Please specify it explicitly.
val vm = foo.get()
^
How can I move the function into the class?
The extension function returns the result of the Foo type parameter. So the result type can be inferred from the receiver type.
And the member function result type has nothing in common with Foo type parameter except the name, which means nothing for a compiler. You can see that T in method and T in class are different types by writing and compiling the following code:
Foo<BarImpl>().get<BarImpl2>()
If you want to make get to be a member function which returns the result of Foo type parameter, you should remove type parameter from function and inject class instance via the constructor:
class Foo<T : Bar>(private val clazz: KClass<T>) {
fun get(): T {
return SomeMap(this).get(clazz)
}
companion object {
inline operator fun <reified T : Bar> invoke() = Foo(T::class)
}
}

why the translated kotlin code complains about a Array<BaseData>? to be a Array<out BaseData>

Having a java class, using androidStudio to translate to kotlin.
Got a error and not sure how to correctly translate it.
The java code:
public class BaseDataImpl extends BaseData {
private final BaseData[] translators;
public BaseDataImpl(final BaseData... translators) {
this.translators = cloneArray(translators);
}
public static <T> T[] cloneArray(final T[] array) {
if (array == null) {
return null;
}
return array.clone();
}
}
after the code translation, got error: required Array<BaseData>?, found Array<out BaseData>, but the translators in the cloneArray<BaseData>(translators) call is defined as val translators: Array<BaseData>?,
anyone could help to explain?
class BaseDataImpl(vararg translators: BaseData) : BaseData() {
private val translators: Array<BaseData>?
init {
this.translators = cloneArray<BaseData>(translators) //<=== error: required Array<BaseData>?, found Array<out BaseData>
}
companion object {
fun <T> cloneArray(array: Array<T>?): Array<T>? {
return array?.clone()
}
}
}
It is written in the Kotlin function reference regarding varargs:
Inside a function a vararg-parameter of type T is visible as an array of T, i.e. the ts variable in the example above has type Array<out T>.
where the referenced function was:
function <T> asList(vararg ts: T): List<T>
So in your case you actually pass an Array<out BaseData> but you only accept an array of type Array<T>? (in your case Array<BaseData>). Either you adapt all of the types to Array<out T> (which basically is similar as saying List<? extends BaseData> in Java) or you take care that you are only dealing with Ts instead, e.g. with:
inline fun <reified T> cloneArray(array: Array<out T>?): Array<T>? {
return array?.clone()?.map { it }?.toTypedArray()
}
But look up the documentation regarding this: Kotlin generics reference - type projections. You probably can accomplish this even easier.