I've defined this class:
class NeverNullMap<K, V>(private val backing: MutableMap<K, V> = mutableMapOf(), val default: () -> V): MutableMap<K, V> by backing {
override operator fun get(key: K): V = backing.getOrPut(key, default)
}
And I can use it perfectly fine like this:
fun main(args: Array<String>) {
val myMap = NeverNullMap<String, Int> {0}
println(myMap["test"])
myMap["test"] = myMap["test"] + 10
println(myMap["test"])
}
as expected the output is:
0
10
But when I try to change it to:
fun main(args: Array<String>) {
val myMap = NeverNullMap<String, Int> {0}
println(myMap["test"])
myMap["test"] += 10
println(myMap["test"])
}
I get:
Exception in thread "main" java.lang.IllegalAccessError: tried to access method kotlin.collections.MapsKt__MapsKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class Day08Kt
at Day08Kt.main(Day08.kt:10)
Why is this happening?
Edit:
Digging a bit into decompiled code both get compiled to completly diffrent code.
In the working version without the += it gets compiled to:
Map var2 = (Map)myMap;
String var3 = "test";
Integer var4 = ((Number)myMap.get("test")).intValue() + 10;
var2.put(var3, var4);
The non working version gets compiled to:
MapsKt.set(myMap, "test", ((Number)myMap.get("test")).intValue() + 10);
So it calles this function: https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/Maps.kt#L175
I still have no idea why that produces the Error, just why the first version behaves diffrently.
Edit: YouTrack link to the report
Edit: yes, this is a bug, it has been merged with KT-14227:
Incorrect code is generated when using MutableMap.set with plusAssign operator
After compilation (or decompilation, in this case), MapsKt.set is turned into a private method:
#InlineOnly
private static final void set(#NotNull Map $receiver, Object key, Object value) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
$receiver.put(key, value);
}
This explains the IllegalAccessError.
Now, as to why it is private, I'm only speculating, but I feel like it may be due to this:
#usbpc102 pointed out that #InlineOnly is indeed the reason for the method being private.
#InlineOnly specifies that the method should never be called directly:
Specifies that this function should not be called directly without inlining
so I feel like this is a case where the call to set should have been inlined, but it was not.
Had the call been inlined, you would have ended up with compiled code that is practically identical to the working version, since the method only contains a call to put.
I suspect this is due to a compiler bug.
Related
Introduction
In Kotlin I have a generic conversion extension function that simplifies conversion of this object of type C to an object of another type T (declared as the receiver) with additional conversion action that treats receiver as this and also provides access to original object:
inline fun <C, T, R> C.convertTo(receiver: T, action: T.(C) -> R) = receiver.apply {
action(this#convertTo)
}
It is used like this:
val source: Source = Source()
val result = source.convertTo(Result()) {
resultValue = it.sourceValue
// and so on...
}
I noticed I often use this function on receivers that are created by parameterless constructors and thought it would be nice to simplify it even more by creating additional version of convertTo() that automates construction of the receiver based on its type, like this:
inline fun <reified T, C, R> C.convertTo(action: T.(C) -> R) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
Unfortunately, I cannot call it like this:
source.convertTo<Result>() {}
because Kotlin expects three type parameters provided.
Question
Given above context, is it possible in Kotlin to create a generic function with multiple type parameters that accepts providing just one type parameter while other types are determined from the call-site?
Additional examples (by #broot)
Imagine there is no filterIsInstance() in stdlib and we would like to implement it (or we are the developer of stdlib). Assume we have access to #Exact as this is important for our example. It would be probably the best to declare it as:
inline fun <T, reified V : T> Iterable<#Exact T>.filterTyped(): List<V>
Now, it would be most convenient to use it like this:
val dogs = animals.filterTyped<Dog>() // compile error
Unfortunately, we have to use one of workarounds:
val dogs = animals.filterTyped<Animal, Dog>()
val dogs: List<Dog> = animals.filterTyped()
The last one isn't that bad.
Now, we would like to create a function that looks for items of a specific type and maps them:
inline fun <T, reified V : T, R> Iterable<T>.filterTypedAndMap(transform: (V) -> R): List<R>
Again, it would be nice to use it just like this:
animals.filterTypedAndMap<Dog> { it.barkingVolume } // compile error
Instead, we have this:
animals.filterTypedAndMap<Animal, Dog, Int> { it.barkingVolume }
animals.filterTypedAndMap { dog: Dog -> dog.barkingVolume }
This is still not that bad, but the example is intentionally relatively simple to make it easy to understand. In reality the function would be more complicated, would have more typed params, lambda would receive more arguments, etc. and then it would become hard to use. After receiving the error about type inference, the user would have to read the definition of the function thoroughly to understand, what is missing and where to provide explicit types.
As a side note: isn't it strange that Kotlin disallows code like this: cat is Dog, but allows this: cats.filterIsInstance<Dog>()? Our own filterTyped() would not allow this. So maybe (but just maybe), filterIsInstance() was designed like this exactly because of the problem described in this question (it uses * instead of additional T).
Another example, utilizing already existing reduce() function. We have function like this:
operator fun Animal.plus(other: Animal): Animal
(Don't ask, it doesn't make sense)
Now, reducing a list of dogs seems pretty straightforward:
dogs.reduce { acc, item -> acc + item } // compile error
Unfortunately, this is not possible, because compiler does not know how to properly infer S to Animal. We can't easily provide S only and even providing the return type does not help here:
val animal: Animal = dogs.reduce { acc, item -> acc + item } // compile error
We need to use some awkward workarounds:
dogs.reduce<Animal, Dog> { acc, item -> acc + item }
(dogs as List<Animal>).reduce { acc, item -> acc + item }
dogs.reduce { acc: Animal, item: Animal -> acc + item }
The type parameter R is not necessary:
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
inline fun <reified T, C> C.convertTo(action: T.(C) -> Unit) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
If you use Unit, even if the function passed in has a non-Unit return type, the compiler still allows you to pass that function.
And there are other ways to help the compiler infer the type parameters, not only by directly specifying them in <>. You can also annotate the variable's result type:
val result: Result = source.convertTo { ... }
You can also change the name of convertTo to something like convert to make it more readable.
Another option is:
inline fun <T: Any, C> C.convertTo(resultType: KClass<T>, action: T.(C) -> Unit) = with(resultType.constructors.first().call()) {
convertTo(this, action)
}
val result = source.convertTo(Result::class) { ... }
However, this will conflict with the first overload. So you have to resolve it somehow. You can rename the first overload, but I can't think of any good names off the top of my head. I would suggest that you specify the parameter name like this
source.convertTo(resultType = Result::class) { ... }
Side note: I'm not sure if the parameterless constructor is always the first in the constructors list. I suggest that you actually find the parameterless constructor.
This answer does not solve the stated problem but incorporates input from #Sweeper to provide a workaround at least simplifying result object instantiation.
First of all, the main stated problem can be somewhat mitigated if we explicitly state variable's result type (i.e. val result: Result = source.convertTo {}) but it's not enough to solve the problem in cases described by #broot.
Secondly, using KClass<T> as result parameter type provides ability to use KClass<T>.createInstance() making sure we find a parameterless constructor (if there's any – if there is none, then result-instantiating convertTo() is not eligible for use). We can also benefit from Kotlin's default parameter values to make result parameter type omittable from calls, we just need to take into account that action might be provided as lambda (last parameter of call) or function reference – this will require two versions of result-instantiating convertTo().
So, taking all the above into account, I've come up with this implementation(s) of convertTo():
// version A: basic, expects explicitly provided instance of `receiver`
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
// version B: can instantiate result of type `T`, supports calls where `action` is a last lambda
inline fun <C, reified T : Any> C.convertTo(resultType: KClass<T> = T::class, action: T.(C) -> Unit) = with(resultType.createInstance()) {
(this#convertTo).convertTo(this#with, action)
}
// version C: can instantiate result of type `T`, supports calls where `action` is passed by reference
inline fun <C, reified T : Any> C.convertTo(action: T.(C) -> Unit, resultType: KClass<T> = T::class) = with(resultType.createInstance()) {
(this#convertTo).convertTo(T::class, action)
}
All three versions work together depending on a specific use case. Below is a set of examples explaining what version is used in what case.
class Source { var sourceId = "" }
class Result { var resultId = "" }
val source = Source()
fun convertX(result: Result, source: Source) {
result.resultId = source.sourceId
}
fun convertY(result: Result, source: Source) = true
fun Source.toResultX(): Result = convertTo { resultId = it.sourceId }
fun Source.toResultY(): Result = convertTo(::convertX)
val result0 = source.convertTo(Result()) { resultId = it.sourceId } // uses version A of convertTo()
val result1: Result = source.convertTo { resultId = it.sourceId } // uses version B of convertTo()
val result2: Result = source.convertTo(::convertX) // uses version C of convertTo()
val result3: Result = source.convertTo(::convertY) // uses version C of convertTo()
val result4: Result = source.toResultX() // uses version B of convertTo()
val result5: Result = source.toResultY() // uses version C of convertTo()
P.S.: As #Sweeper notices, convertTo might not be a good name for the result-instantiating versions (as it's not as readable as with basic version) but that's a secondary problem.
Let's say I have an object which helps me to deserialize other objects from storage:
val books: MutableList<Book> = deserializer.getBookList()
val persons: MutableList<Person> = deserializer.getPersonList()
The methods getBookList and getPersonList are extension functions I have written. Their logic is allmost the same so I thought I may can combine them into one method. My problem is the generic return type. The methods look like this:
fun DataInput.getBookList(): MutableList<Book> {
val list = mutableListOf<Book>()
val size = this.readInt()
for(i in 0 .. size) {
val item = Book()
item.readExternal(this)
list.add(item)
}
return list
}
Is there some Kotlin magic (maybe with inline functions) which I can use to detect the List type and generify this methods? I think the problem would be val item = T() which will not work for generic types, right? Or is this possible with inline functions?
You cannot call the constructor of a generic type, because the compiler can't guarantee that it has a constructor (the type could be from an interface). What you can do to get around this though, is to pass a "creator"-function as a parameter to your function. Like this:
fun <T> DataInput.getList(createT: () -> T): MutableList<T> {
val list = mutableListOf<T>()
val size = this.readInt()
for(i in 0 .. size) {
val item = createT()
/* Unless readExternal is an extension on Any, this function
* either needs to be passed as a parameter as well,
* or you need add an upper bound to your type parameter
* with <T : SomeInterfaceWithReadExternal>
*/
item.readExternal(this)
list.add(item)
}
return list
}
Now you can call the function like this:
val books: MutableList<Book> = deserializer.getList(::Book)
val persons: MutableList<Person> = deserializer.getList(::Person)
Note:
As marstran mentioned in a comment, this requires the class to have a zero-arg constructor to work, or it will throw an exception at runtime. The compiler will not warn you if the constructor doesn't exist, so if you pick this way, make sure you actually pass a class with a zero-arg constructor.
You can't initialize generic types, in Kotlin or Java. At least not in the "traditional" way. You can't do this:
val item = T()
In Java, you'd pass a Class<T> and get the constructor. Very basic example of that:
public <T> void x(Class<T> cls){
cls.getConstructor().newInstance(); // Obviously you'd do something with the return value, but this is just a dummy example
}
You could do the same in Kotlin, but Kotlin has a reified keyword that makes it slightly easier. This requires an inline function, which means you'd change your function to:
inline fun <reified T> DataInput.getBookList(): MutableList<T> { // Notice the `<reified T>`
val list = mutableListOf<T>() // Use T here
val size = this.readInt()
for(i in 0 .. size) {
// This is where the initialization happens; you get the constructor, and create a new instance.
// Also works with arguments, if you have any, but you used an empty one so I assume yours is empty
val item = T::class.java.getConstructor().newInstance()!!
item.readExternal(this) // However, this is tricky. See my notes below this code block
list.add(item)
}
return list
}
However, readExternal isn't present in Any, which will present problems. The only exception is if you have an extension function for either Any or a generic type with that name and input.
If it's specific to some classes, then you can't do it like this, unless you have a shared parent. For an instance:
class Book(){
fun readExternal(input: DataInput) { /*Foo bar */}
}
class Person(){
fun readExternal(input: DataInput) { /*Foo bar */}
}
Would not work. There's no shared parent except Any, and Any doesn't have readExternal. The method is manually defined in each of them.
You could create a shared parent, as an interface or abstract class (assuming there isn't one already), and use <reified T : TheSharedParent>, and you would have access to it.
You could of course use reflection, but it's slightly harder, and adds some exceptions you need to handle. I don't recommend doing this; I'd personally use a superclass.
inline fun <reified T> DataInput.getBookList(): MutableList<T> {
val list = mutableListOf<T>()
val size = this.readInt()
val method = try {
T::class.java.getMethod("readExternal", DataInput::class.java)
}catch(e: NoSuchMethodException){
throw RuntimeException()
}catch(e: SecurityException){
throw RuntimeException()// This could be done better; but error handling is up to you, so I'm just making a basic example
// The catch clauses are pretty self-explanatory; if something happens when trying to get the method itself,
// These two catch them
}
for(i in 0 .. size) {
val item: T = T::class.java.getConstructor().newInstance()!!
method.invoke(item, this)
list.add(item)
}
return list
}
I have a val built like this
val qs = hashMapOf<KProperty1<ProfileModel.PersonalInfo, *> ,Question>()
How can I obtain the class of ProfileModel.PersonalInfo from this variable?
In other words what expression(involving qs of course) should replace Any so that this test passes.
#Test
fun obtaionResultTypeFromQuestionList(){
val resultType = Any()
assertEquals(ProfileModel.PersonalInfo::class, resultType)
}
Thank you for your attention
There is no straight way to get such information due to Java type erasure.
To be short - all information about generics (in your case) is unavailable at runtime and HashMap<String, String> becomes HashMap.
But if you do some changes on JVM-level, like defining new class, information about actual type parameters is kept. It gives you ability to do some hacks like this:
val toResolve = object : HashMap<KProperty1<ProfileModel.PersonalInfo, *> ,Question>() {
init {
//fill your data here
}
}
val parameterized = toResolve::class.java.genericSuperclass as ParameterizedType
val property = parameterized.actualTypeArguments[0] as ParameterizedType
print(property.actualTypeArguments[0])
prints ProfileModel.PersonalInfo.
Explanation:
We define new anonymous class which impacts JVM-level, not only runtime, so info about generic is left
We get generic supperclass of our new anonymous class instance what results in HashMap< ... , ... >
We get first type which is passed to HashMap generic brackets. It gives us KProperty1< ... , ... >
Do previous step with KProperty1
Kotlin is tied to the JVM type erasure as well as Java does. You can do a code a bit nice by moving creation of hash map to separate function:
inline fun <reified K, reified V> genericHashMapOf(
vararg pairs: Pair<K, V>
): HashMap<K, V> = object : HashMap<K, V>() {
init {
putAll(pairs)
}
}
...
val hashMap = genericHashMapOf(something to something)
In an attempt to understand more about Kotlin and play around with it, I'm developing a sample Android app where I can try different things.
However, even after searching on the topic for a while, I haven't been able to find a proper answer for the following issue :
Let's declare a (dummy) extension function on View class :
fun View.isViewVisibility(v: Int): Boolean = visibility == v
Now how can I reference this function from somewhere else to later call invoke() on it?
val f: (Int) -> Boolean = View::isViewVisibility
Currently gives me :
Error:(57, 35) Type mismatch: inferred type is KFunction2 but (Int) -> Boolean was
expectedError:(57, 41) 'isViewVisibility' is a member and an extension
at the same time. References to such elements are not allowed
Is there any workaround?
Thanks !
Extensions are resolved statically, where the first parameter accepts an instance of the receiver type. isViewVisibility actually accept two parameters, View and Int. So, the correct type of it should be (View, Int) -> Boolean, like this:
val f: (View, Int) -> Boolean = View::isViewVisibility
The error message states:
'isViewVisibility' is a member and an extension at the same time. References to such elements are not allowed
It's saying that the method is both an extension function, which is what you're wanting it to be, and a member. You don't show the entire context of your definition, but it probably looks something like this:
// MyClass.kt
class MyClass {
fun String.coolStringExtension() = "Cool $this"
val bar = String::coolStringExtension
}
fun main() {
print(MyClass().bar("foo"))
}
Kotlin Playground
As you can see the coolStringExtension is defined as a member of MyClass. This is what the error is referring to. Kotlin doesn't allow you to refer to extension function that is also a member, hence the error.
You can resolve this by defining the extension function at the top level, rather than as a member. For example:
// MyClass.kt
class MyClass {
val bar = String::coolStringExtension
}
fun String.coolStringExtension() = "Cool $this"
fun main() {
print(MyClass().bar("foo"))
}
Kotlin Playground
A better fit is the extension function type View.(Int) -> Boolean:
val f: View.(Int) -> Boolean = View::isViewVisibility
But actually the extension types are mostly interchangeable (assignment-compatible) with normal function types with the receiver being the first parameter:
View.(Int) -> Boolean ↔ (View, Int) -> Boolean
I faced the same problem when I declared extension function inside another class and try to pass that extension function as parameter.
I found a workaround by passing function with same signature as extension which in turn delegates to actual extension function.
MyUtils.kt:
object MyUtils {
//extension to MyClass, signature: (Int)->Unit
fun MyClass.extend(val:Int) {
}
}
AnyClass.kt:
//importing extension from MyUtils
import MyUtils.extend
// Assume you want to pass your extension function as parameter
fun someMethodWithLambda(func: (Int)->Unit) {}
class AnyClass {
fun someMethod() {
//this line throws error
someMethodWithLambda(MyClass::extend) //member and extension at the same time
//workaround
val myClassInstance = MyClass()
// you pass a proxy lambda which will call your extension function
someMethodWithLambda { someIntegerValue ->
myClassInstance.extend(someIntegerValue)
}
}
}
As a workaround you can create a separate normal function and invoke it from an inline extension method:
inline fun View.isVisibility(v: Int): Boolean = isViewVisibility(this, v)
fun isViewVisibility(v: View, k: Int): Boolean = (v.visibility == k)
You can't call directly the extension method because you don't have the implicit this object available.
Using either a type with two parameters (the first for the implicit receiver, as #Bakawaii has already mentioned) or an extension type should both work without any warnings at all.
Let's take this function as an example:
fun String.foo(f: Int) = true
You can use assign this to a property that has a two parameter function type like this:
val prop: (String, Int) -> Boolean = String::foo
fun bar() {
prop("bar", 123)
}
Or, you can use an extension function type, that you can then call with either of these two syntaxes:
val prop2: String.(Int) -> Boolean = String::foo
fun bar2() {
prop2("bar2", 123)
"bar2".prop2(123)
}
Again, the above should all run without any errors or warnings.
In Kotlin, the following code compiles:
class Foo {
fun bar(foo: List<String>): String {
return ""
}
fun bar(foo: List<Int>): Int {
return 2;
}
}
This code, however, does not:
class Foo {
fun bar(foo: List<String>): String {
return ""
}
fun bar(foo: List<Int>): String {
return "2";
}
}
Compiling this will cause the following error:
Error:(8, 5) Kotlin: Platform declaration clash: The following declarations have the same JVM signature (foo(Ljava/util/List;)Ljava/lang/String;):
fun foo(layout: List<Int>): String
fun foo(layout: List<String>): String
In Java, neither example will compile:
class Foo {
String bar(List<Integer> foo) {
return "";
}
Integer bar(List<String> foo) {
return 2;
}
}
class Foo {
String bar(List<Integer> foo) {
return "";
}
String bar(List<String> foo) {
return "2";
}
}
Unsurprisingly, both of the prior snippets generate the familiar compiler error:
Error:(13, 12) java: name clash: bar(java.util.List<java.lang.String>) and bar(java.util.List<java.lang.Integer>) have the same erasure
What surprises me is that the first Kotlin example works at all, and second, if it works, why does the second Kotlin example fail? Does Kotlin consider a method's return type as part of its signature? Furthermore, why do method signatures in Kotlin respect the full parameter type, in contrast with Java?
Actually Kotlin knows the difference between the two methods in your example, but jvm will not. That's why it's a "platform" clash.
You can make your second example compile by using the #JvmName annotation:
class Foo {
#JvmName("barString") fun bar(foo: List<String>): String {
return ""
}
#JvmName("barInt") fun bar(foo: List<Int>): String {
return "2";
}
}
This annotation exists for this very reason. You can read more in the interop documentation.
While #Streloks answer is correct, I wanted to dig deeper regarding why it works.
The reason why the first variant works, is that it is not prohibited within the Java Byte code. While the Java compiler complains about it, i.e. the Java language specification does not allow it, the Byte code does, as was also documented in https://community.oracle.com/docs/DOC-983207 and in https://www.infoq.com/articles/Java-Bytecode-Bending-the-Rules. In the Byte code every method call refers the actual return type of the method, which isn't that way when you write the code.
Unfortunately I couldn't find the actual source, why it is that way.
The document regarding Kotlins name resolution contains some interesting points, but I did not see your actual case there.
What really helped me understand it, was the answer from #Yole to Kotlin type erasure - why are functions differing only in generic type compilable while those only differing in return type are not?, more precisely that the kotlin compiler will not take the type of the variable into account when deciding which method to call.
So, it was a deliberate design decision that specifying the type on a variable will not influence which method is the one to be called but rather the other way around, i.e. the called method (with or without generic information) influences the type to be used.
Applying the rule on the following samples then makes sense:
fun bar(foo: List<String>) = "" (1)
fun bar(foo: List<Int>) = 2 (2)
val x = bar(listOf("")) --> uses (1), type of x becomes String
val y = bar(listOf(2)) --> uses (2), type of y becomes Int
Or having a method supplying a generic type but not even using it:
fun bar(foo: List<*>) = "" (3)
fun <T> bar(foo: List<*>) = 2 (4)
val x = bar(listOf(null)) --> uses (3) as no generic type was specified when calling the method, type of x becomes String
val y = bar<String>(listOf(null)) --> uses (4) as the generic type was specified, type of y becomes Int
And that's also the reason why the following will not work:
fun bar(foo: List<*>) = ""
fun bar(foo: List<*>) = 2
This is not compilable as it leads to a conflicting overload as the type of the assigned variable itself is not taken into consideration when trying to identify the method to be called:
val x : String = bar(listOf(null)) // ambiguous, type of x is not relevant
Now regarding that name clash: as soon as you use the same name, the same return type and the same parameters (whose generic types are erased), you will actually get the very same method signature in the byte code. That's why #JvmName becomes necessary. With that you actually ensure that there are no name clashes in the byte code.