Generics in Kotlin [duplicate] - android

I'm trying to mock a function in Kotlin
Mockito.mock(Function2<Int, Int, Unit>::class.java)
and it says "Only classes are allowed on the left hand side of a class literal". What's the proper way to obtain a reference to a statically known parameterized class? For now I live with an ugly cast
Mockito.mock(Function2::class.java) as (Int, Int) -> Unit

The error is correct and the solution you provided is the intended one. The rationale here is that since generic type arguments are not reified at runtime, you can only obtain an object representing a class, not a type.
There's a workaround though: if you use the class literal syntax through a reified type parameter, substituting it with the desired type at the call site, you'll get the same KClass object but with the actual arguments you've provided. In your case you can declare the following function:
inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java) as T
And use it like this:
val f = mock<(Int, Int) -> Unit>()

Related

why we can pass a contravariance in a covariance type example of the List in Kotlin

I'm wondering why I can pass a contravariance in a covariance type example of the List
I created this interface as you can see I is a contravariance but I was able to pass to the list
interface ListMapper<in I, out O> : Mapper<List<I>, List<O>>
if you check you will see that the list accept a Covariance type
public interface List<out E> : Collection<E> {...}
Why and how can this be possible ?
Presumably, the first type in the Mapper interface is contravariant at the declaration site.
There's no reason for the restriction you're imagining should be there. The covariance of the List is internal to the List functionality, and has nothing to do with the Mapper's functionality.
A List is covariant, meaning it cannot consume any T, so maybe it will simplify your understanding to replace it with a non-generic class that doesn't consume anything, such as Int, which is immutable. It looks obviously acceptable logically:
interface IntMapper : Mapper<Int, Int>
It's no different than with the List. You have a type that doesn't consume anything, yet it can be the input for a Mapper.
Answering your follow-up question from the comment:
The K type of HashMap is invariant at the declaration site. To simplify this dicussion, let's just use MutableList which also has an invariant type (so we aren't confused by Map's second V type).
Let's also simplify and use a more basic interface that just consumes, so we aren't confused by the second type in Mapper:
interface Consumer<in I>
So now we have:
// Allowed:
class ListConsumer<in I> : Consumer<List<I>>
// Not allowed:
class MutableListConsumer<in I> : Consumer<MutableList<I>> // Compile error
Imagine you have a ListConsumer. Since ListConsumer is contravariant, it allows you to cast a ListConsumer<Number> to the more restrictive type ListConsumer<Int>:
val numberListConsumer: ListConsumer<Number> = //...
val intListConsumer: ListConsumer<Int> = numberListConsumer
This makes sense. If you can consume List<Number>, then it is safe to consume any List<Int> because a List<Int> is also a List<Number> due to List's covariant type.
But it doesn't make sense with MutableList. A MutableList<Int> is not a MutableList<Number> because MutableList's type is invariant.

How to use a Kotlin extension function on a class?

I have a pretty short question about an extension function that would help clear some of my code. Basically I have some transformations on the hashCode of a class name and I want an extension function to do the transformations.
Example:
Getting the name hashCode: StateA::class.java.name.hashCode() where StateA is a simple class.
I want to the extension function like:
fun Class<*>.transformName(): String {
var hashString = this.javaClass.name.hashCode()
//Do my stuff on that hashString
return hashString
}
But this doesn't seem to work. When I apply the extension function with StateA.transformName(), the function gives me an error with Unresolved Reference.
I tried various things like applying the function to StateA::class or having the hashString equal to this::class.java.name.hashCode() but nothing works. Any tips?
You can't really achieve the StateA.transformName() syntax, as StateA just on its own refers to the companion object inside that class. So to get that syntax, you'd need to have a companion object inside every class that you want to use this extension on.
What you can do in a very general way is get the KClass that describes your class first. This gives you an object (the KClass instance) that you can then call an extension on:
fun KClass<*>.transformName() {
val clazz: Class<*> = this.java
clazz.name.hashCode()
}
StateA::class.transformName()
Another approach, which is less verbose on the call site could be a generic function like this, where the reified keyword allows you to access the concrete class that was used as the generic type parameter inside the function:
inline fun <reified T> transformName() {
val clazz: Class<*> = T::class.java
clazz.name.hashCode()
}
transformName<StateA>()

How to make an extension functions about getInteger for Context in Kotlin?

I'm a beginner of Kotlin, the Code A get a int value from resource file.
I hope to use an extension functions to do it, and invoke it just like this.getInteger(R.integer.ActivityEditBackup)
But Code B I made is incorrect, how can I fix it?
Code A
mContext.resources.getInteger(R.integer.ActivityEditBackup))
Code B
inline fun <reified T : Activity>Context.getInteger(int id): int {
return T.resources.getInteger(id)
}
You're overcomplicating it a bit.
You won't use the specific type of Context in any way, you don't need to make your extension generic.
In the parameter list, the name of the parameter comes first, and the type after.
The integer type's name in Kotlin is Int, with a capital I.
You can refer to your Context inside the extension function with this.
You can use support annotations to specify that your parameter is always an integer resource ID.
Overall, with these changes:
inline fun Context.getInteger(#IntegerRes id: Int): Int {
return this.resources.getInteger(id)
}
There was also some general confusion about syntax, you should look into the documentation for functions and then extensions.
Additionally, you can convert the function to an expression body and omit the explicit this:
inline fun Context.getInteger(#IntegerRes id: Int) = resources.getInteger(id)

Inferring parameter of class type with default

I have a class with constructor declared like this
class Facade<T : SuperClass>(
val kClass: KClass<in T> = SuperClass::class
)
It is done like this so that developer doesn't have to specify SuperClass if they want to use it instead of a subclass.
Reason to even send the class type is, so that developer doesn't have to specify type in angle brackets .
But now comes the problem. Creating instance like below, says that there is not enough information to infer parameter T. Which is resulting in having to put class into angle brackets.
Facade()
But since default value is SuperClass, then kotlin should be able to infer parameter T as SuperClass. What am I thinking wrong?
Thank you
TL;DR:
Facade(SubClass:class) // working
Facade(SuperClass:class) // working, but don't want (superclass is default)
Facade<SuperClass>() // working, but don't want angle brackets <>
Facade() // not working, cannot infer T type from default, why?
For the Facade() to be equivalent to Facade(DerivedClass::class) the default constructor parameter would have to be declared as val kClass: KClass<in T> = T::class. However to use T::class the T type parameter would need to be reified. A type parameter can only be reified in inline functions and not constructors.
To workaround this issue you can declare a factory function that delegates to constructor like so:
inline fun <reified T : SuperClass> Facade(): Facade<T> = Facade(T::class)
This allows one to use it as e.g.:
val derivedFacade:Facade<DerivedClass> = Facade()
Note that if you would like to use the SuperClass as the default parameter for T you would need to declare another factory method using different name e.g.:
fun SuperFacade(): Facade<SuperClass> = Facade()
This is required since if we had declared #JvmName("SuperFacade") fun Facade() = Facade(SuperClass::class) the compile would match it whenever we do not provide type parameters. This in turn would defy the type inference from the derivedFacade example.
You can resolve your problem by removing the angle brackets and changing the constructor type.
Just use this constructor:
class Facade(val kClass: KClass<*> = SuperClass::class)
All these call are working
Facade(SubClass:class)
Facade(SuperClass:class)
Facade()

what does "::" mean in kotlin?

I'm new to Kotlin
I used this code for opening another activity:
startActivity(Intent(this,IntroAndLang::class.java))
current activity and target activity are written in Kotlin
I can't understand why there is not single : instead of :: at IntroAndLang::class.java
:: is used for Reflection in kotlin
Class Reference val myClass = MyClass::class
Function Reference this::isEmpty
Property Reference ::someVal.isInitialized
Constructor Reference ::MyClass
For detailed reading Official Documentation
:: converts a Kotlin function into a lambda.
Let's say you have a function that looks like this:
fun printSquare(a: Int) = println(a * 2)
And you have a class that takes a lambda as a 2nd argument:
class MyClass(var someOtherVar: Any, var printSquare: (Int) -> Unit) {
fun doTheSquare(i: Int) {
printSquare(i)
}
}
How do you pass the printSquare function into MyClass? If you try the following, it wont work:
MyClass("someObject", printSquare) //printSquare is not a LAMBDA, it's a function so it gives compile error of wrong argument
So how do we CONVERT printSquare into a lambda so we can pass it around? Use the :: notation.
MyClass("someObject",::printSquare) //now compiler does not complain since it's expecting a lambda and we have indeed converted the `printSquare` FUNCTION into a LAMBDA.
Also, please note that this is implied... meaning this::printSquare is the same as ::printSquare. So if the printSquare function was in another class, like a Presenter, then you could convert it to lambda like this:
Presenter::printSquare
UPDATE:
Also this works with constructors. If you want to create the constructor of a class and then convert it to a lambda, it is done like this:
(x, y) -> MyClass::new
this translates to MyClass(x, y) in Kotlin.
As stated in the docs this is a class reference:
Class References:
The most basic reflection feature is getting the runtime reference to a Kotlin class. To obtain the reference to a statically known Kotlin class, you can use the class literal syntax:
val c = MyClass::class
//The reference is a value of type KClass.
Note that a Kotlin class reference is not the same as a Java class reference. To obtain a Java class reference, use the .java property on a KClass instance.
It’s also the syntax for method references as in this simple example:
list.forEach(::println)
It refers to println defined in Kotlin Standard library.
Since kotlin 1.1, in addition to class, function, property and constructor references as stated above, '::' can also be used to obtain the bound references to all of the above.
For instance, using '::class' could be used to get the exact class of a particular object despite the type of the receiver as below...
val widget: Widget = ...
assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }
widget::class returns the exact class of the object 'widget' as either 'GoodWidget' or 'BadWidget' despite the type of the receiver expression (i.e 'Widget' as declared initially)
More info at https://kotlinlang.org/docs/reference/reflection.html

Categories

Resources