Kotlin - "Don't care" in desugaring of data class - wildcard

Is it possible to mark "don't cares" in Kotlin? I have not been able to find the appropriate syntax, Haskell and Python use the _ symbol but this does not work in Kotlin.
Example,
data class DesugarExample(val foo: Int, val bar: Int, val baz: Int)
fun desugarIt() {
val (useFoo, dontCare, useBaz) = DesugarExample(1, 2, 3)
}

Unfortunately this isn't currently possible, but according to Dmitry Jemerov, the use of the underscore in this use case is planned for Kotlin 1.1.
Update: The example above does in fact work in Kotlin 1.1 when replacing one of the vals with underscore to indicate "don't care". Example:
fun desugarIt() {
val (useFoo, dontCare, _) = DesugarExample(1, 2, 3)
}

Related

Kotlin functional strategy pattern doesn't compile

I'm trying to put several functions in a map. The idea is to have: Map<String, [function]>.
The code is as follows:
class UserIdQueriesHandler {
val strategy: Map<String, KFunction2<#ParameterName(name = "id") String, #ParameterName(name = "options") Array<Options>, Answer>> =
mapOf( // the compiler complains here
"d" to ::debt,
"p" to ::currentProduct
)
fun debt(id: String, options: Array<DebtOptions>): UserDebt = UserDebt(isPresent = true, amount = 0.0)
fun currentProduct(id: String, options: Array<CurrentProductOptions>): UserProducts = UserProducts(products = emptyList())
}
enum class DebtOptions : Options { BOOL, AMOUNT }
enum class CurrentProductOptions : Options { ALL, PRINT, DIGITAL, ENG, HEB, TM }
data class UserDebt(val isPresent: Boolean, val amount: Double) : Answer
data class UserProducts(val products: List<Int>): Answer
Answer and Options are simple kotlin interfaces:
interface Answer
interface Options
Compiler output:
Type inference failed. Expected type mismatch:
required:
Map<String, KFunction2<#ParameterName String, #ParameterName Array<Options>, Answer>>
found:
Map<String, KFunction2<#ParameterName String, {[#kotlin.ParameterName] Array & [#kotlin.ParameterName] Array }, Answer>>
The type of strategy says functions you put into it can accept any Array<Options> as the second argument, which debt and currentProduct can't.
The simplest workaround would be to change their argument type to Array<Options> (or List<Options>; they probably don't need to mutate it!) and fail at runtime if wrong options are passed or ignore them.
Variance section in the documentation is also relevant.
Since an Array can be both read and written, its type parameter is invariant. This makes it so that you can't assign an Array<DebtOptions> to a variable that has the type of Array<Options>. The former isn't a subtype of the latter, because it would allow you to put other elements in the array that are Options, but not DebtOptions, leading to problems to code that has a reference to this array as an Array<DebtOptions>.
A solution would be to make your functions accept Array<Options>, if you can.
val strategy: Map<String, KFunction2<String, Array<Options>, Answer>> =
mapOf(
"d" to ::debt,
"p" to ::currentProduct
)
fun debt(id: String, options: Array<Options>): UserDebt = ...
fun currentProduct(id: String, options: Array<Options>): UserProducts = ...
You could combine this with using the nicer functional type instead of a KFunction2:
val strategy: Map<String, (String, Array<Options>) -> Answer> = ...

instance::class.java vs. instance.javaClass

Given Kotlin 1.1. For an instance of some class, instance::class.java and instance.javaClass seem to be nearly equivalent:
val i = 0
println(i::class.java) // int
println(i.javaClass) // int
println(i::class.java === i.javaClass) // true
There is a subtle difference, however:
val c1: Class<out Int> = i::class.java
val c2: Class<Int> = i.javaClass
instance.javaClass is negligibly shorter, but instance::class.java is more consistent with the corresponding usage on a type. While you can use .javaClass on some types, the result may not be what you would expect:
println(i::class.java === Int::class.java) // true
println(i.javaClass === Int.javaClass) // false
println(Int::class.java === Int.javaClass) // false
println(Int.javaClass) // class kotlin.jvm.internal.IntCompanionObject
So, I would argue that it is better to never use .javaClass for more consistency. Are there any arguments against that?
The difference in these two constructs is that, for an expression foo of static (declared or inferred) type Foo:
foo.javaClass is typed as Class<Foo>
foo::class.java is typed as Class<out Foo>
In fact, the latter is more precise, because the actual value that foo evaluates to can be an instance of not Foo itself but one of its subtypes (and it's exactly what's denoted by the covariant out Foo).
As #marstran correctly noted in the comment on the question, .javaClass once was considered to be deprecated (see the Kotlin 1.1 RC announcement) because it can break type safety (see below), but it was afterwards left as-is because it was widely used and replacing it with the alternative of ::class.java would require adding explicit unchecked casts in the code.
Also, see the comments under this answer: (link)
Please note that Int.javaClass does not denote the type of Int but instead is the Java class of the Int's companion object. Whereas Int::class.java is an unbound class reference and denotes the type. To get it with .javaClass, you need to call it on an Int instance, e.g. 1.javaClass.
Here's how exactly .javaClass can break type safety. This code compiles but breaks at runtime:
open class Foo
class Bar : Foo() {
val baz: Int = 0
}
fun main(args: Array<String>) {
val someFoo: Foo = Bar()
val anotherFoo: Foo = Foo()
val someFooProperty: KProperty1<in Foo, *> = // 'in Foo' is bad
someFoo.javaClass.kotlin.memberProperties.first()
val someValue = someFooProperty.get(anotherFoo)
}
This example uses kotlin-reflect.
That's because someFooProperty represents a property of Bar, not Foo, but since it was obtained from someFoo.javaClass (Class<Foo> then converted to KClass<Foo>) the compiler allows us to use it with the in Foo projection.

Kotlin Map with non null values

Let say that I have a Map for translating a letter of a playing card to an integer
val rank = mapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14)
When working with the map it seems that I always have to make a null safety check even though the Map and Pair are immutable:
val difference = rank["Q"]!! - rank["K"]!!
I guess this comes from that generic types have Any? supertype. Why can't this be resolved at compile time when both Map and Pair are immutable?
There is another method for getting not null value from map:
fun <K, V> Map<K, V>.getValue(key: K): V
throws NoSuchElementException - when the map doesn't contain a value for the specified key and no implicit default value was provided for that map.
but operator for get == map[] returns nullable.
operator fun <K, V> Map<out K, V>.get(key: K): V?
It is not about the implementation of Map (being it Kotlin or Java based). You are using a Map and a map may not have a key hence [] operator returns nullable type.
mapOf() is providing a Map with no guarantees for the presence of a key-- something which is kind of expected especially considering the Java implementation of Map.
While I might personally prefer sticking with null-safe calls and elvis operators, it sounds like you'd prefer cleaner code at the call site (especially considering you know these keys exist and have associated non-null values). Consider this:
class NonNullMap<K, V>(private val map: Map<K, V>) : Map<K, V> by map {
override operator fun get(key: K): V {
return map[key]!! // Force an NPE if the key doesn't exist
}
}
By delegating to an implementation of map, but overriding the get method, we can guarantee that return values are non-null. This means you no longer have to worry about !!, ?., or ?: for your usecase.
Some simple test code shows this to be true:
fun main(args: Array<String>) {
val rank = nonNullMapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14)
val jackValue: Int = rank["J"] // Works as expected
println(jackValue)
val paladinValue: Int = rank["P"] // Throws an NPE if it's not found, but chained calls are considered "safe"
println(jackValue)
}
// Provides the same interface for creating a NonNullMap as mapOf() does for Map
fun <K, V> nonNullMapOf(vararg pairs: Pair<K, V>) = NonNullMap(mapOf<K, V>(*pairs))
The short answer is you can't achieve that until Kotlin changes. As others have pointed out, this doesn't have to do with mutability but the fact that Java's Maps accept null as valid values. At the moment, Kotlin's *Map classes has the exact implementation as Java's *Map classes.
If you still want to achieve non-null-value only map, you'll need to implement your own e.g. extending Map or wrap around it
More specifically, behind the scene, mapOf gives us a Kotlin's LinkedHashMap which is not a different class but a just a typealias of Java's LinkedHashMap
Maps.kt
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> =
if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()
TypeAliases.kt
#SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
You can try map.getValue(key) instead of map.get(key) but I personally think that's unclean and confusing.
Perhaps some others from Dan Lew here would be useful for you?
My Kotlin version is 1.3.72-release-IJ2020.1-3
I've found a decent solution:
val rank = object {
val rankMap = mapOf("J" to 11, "Q" to 12, "K" to 13, "A" to 14)
operator fun get(key: String): Int = rankMap[key]!!
}
val difference = rank["Q"] - rank["K"]

Kotlin invoke getter/setter reflectively

Beginner in Kotlin here.
I try to create and populate objects by reflection in a program. I cannot find the equivalent functionality in pure kotlin so my solution resembles the code below which works fine, but requires the use of dirty references like java.lang.String::class.java and intelliJ, understandably, doesn't seem to like this. Is there a simpler way that I am missing to do this?
val jclass = myObject::class.java
val setters = jclass.declaredMethods.filter { it.name.startsWith("set") }
for (s in setters) {
val paramType = s.parameterTypes.first()
val data = when(paramType) {
java.lang.Integer::class.java -> foo
java.lang.Double::class.java -> bar
java.lang.String::class.java -> baz
}
s.invoke(myObject, data)
}
You can use Kotlin reflection, which requires you to add kotlin-reflect as a dependency to your project.
Here you can find kotlin-reflect for Kotlin 1.0.5, or pick another version if you use different Kotlin version.
After that, you can rewrite your code as follows:
val properties = myObject.javaClass.kotlin.memberProperties
for (p in properties.filterIsInstance<KMutableProperty<*>>()) {
val data = when (p.returnType.javaType) {
Int::class.javaPrimitiveType,
Int::class.javaObjectType -> foo
Double::class.javaPrimitiveType,
Double::class.javaObjectType -> bar
String::class.java -> baz
else -> null
}
if (data != null)
p.setter.call(myObject, data)
}
Some details:
Despite using Kotlin reflection, this approach works with Java classes as well, their fields and accessors will be seen as properties, as described here.
Just like with Java reflection, memberProperties returns public properties of this type and all its supertypes. To get all the properties declared in the type (including the private ones, but not those from the supertypes), use declaredMemberProperties instead.
.filterIsInstance<KMutableProperty<*> returns only the mutable properties, so that you can use their p.setter later. If you need to iterate over the getters of all the properties, remove it.
In the when block, I compared p.returnType.javaType to Int::class.javaPrimitiveType and Int::class.javaObjectType, because what's Int in Kotlin can be mapped to either Java int or java.lang.Integer depending on its usage. In Kotlin 1.1, it will be enough to check p.returnType.classifier == Int::class.
If You need to get property getter/setter, there is a couple of built-in constructions for it YourClass::propertyName
have a look at example bellow
fun main(args: Array<String>) {
val myObject = Cat("Tom", 3, 35)
println(Cat::age.getter.call(myObject)) // will print 3
Cat::age.setter.call(myObject, 45)
print(myObject) // will print Cat(name=Tom, age=45, height=35)
}
data class Cat(var name : String, var age : Int, val height : Int)
but sometimes you don't know class exactly(working with generics) or need to get list of properties, then use val <T : Any> KClass<T>.declaredMemberProperties: Collection<KProperty1<T, *>> it will return all properties, some of them can be mutable(var) and some immutable(val), you can find out immutability by checking belonging to KMutableProperty<*> (by filtering with is operator or using convenience methods such as filterIsInstance<KMutableProperty<*>>)
about your code snippet
I absolutely agree with hotkey, but now it is better to use myObject::class.declaredMemberProperties instead of myObject.javaClass.kotlin.memberProperties
because the second one is deprecated
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html
data class Cat(var name : String, var age : Int, val height : Int)
#JvmStatic
fun main(args: Array<String>) {
val myObject = Cat("Tom", 3, 35)
val properties = myObject::class.declaredMemberProperties
for (p in properties.filterIsInstance<KMutableProperty<*>>()) {
val data = when (p.returnType.javaType) {
Int::class.javaPrimitiveType,
Int::class.javaObjectType -> 5
String::class.java -> "Rob"
else -> null
}
if (data != null)
p.setter.call(myObject, data)
}
println(myObject)
// it will print Cat(name=Rob, age=5, height=35),
// because height isn't var(immutable)
}
in general, I would approach similar problems with such construction in mind
val myObject = Cat("Tom", 3, 35)
Cat::class.declaredMemberProperties
//if we want only public ones
.filter{ it.visibility == KVisibility.PUBLIC }
// We only want strings
.filter{ it.returnType.isSubtypeOf(String::class.starProjectedType) }
.filterIsInstance<KMutableProperty<*>>()
.forEach { prop ->
prop.setter.call(myObject, "Rob")
}
println(myObject)
//it will print Cat(name=Rob, age=3, height=35),
//because name is only eligible in this case

Kotlin class equality fails

The following snippet shows the result of testing equality of Kotlin KClass references obtained from different sources. Their string representations are the same. But their java classes are different. Expected that c, c0 and c1 are equal. But for some reason they aren't.
Is there some nuance or it's a bug? If it's not a bug what is the reliable way to test equality of KClasses?
fun main(args: Array<String>) {
val c = Int::class
fun test(v0: Any, v1: Any) {
val c0 = v0.javaClass.kotlin
val c1 = v1.javaClass.kotlin
println("c= $c; c0= $c0; c1= $c1") // c= class kotlin.Int; c0= class kotlin.Int; c1= class kotlin.Int
println("c= ${c.java}; c0= ${c0.java}; c1= ${c1.java}") // c= int; c0= class java.lang.Integer; c1= class java.lang.Integer
println("c = c0? ${c == c0}; c0 = c1? ${c1 == c0}") // c = c0? false; c0 = c1? true
}
test(11, 22)
}
EDIT:
The workaround is to use KClass.javaObjectType method.
The docs says:
Returns a Java Class instance corresponding to the given KClass instance. In case of primitive types it returns corresponding wrapper classes.
I.e. c.javaObjectType == c1.javaObjectType is true
But it doesn't justifies why KClasses having same string representation are different. At least it's confusing. And it's good idea to note about that in docs.
In your case equality fails because KClasses are considered equal when they correspond to the same Java's type, not the same Kotlin type. This is false for int and java.lang.Integer.
The workaround is to use KClass's javaObjectType property, which will return Java class (not primitive type) even for Kotlin type compiled into Java's primitive:
fun sameClass(c1: KClass<*>, c2: KClass<*>) = c1.javaObjectType == c2.javaObjectType
sameClass(Int::class, (1 as Any?)!!.javaClass.kotlin) //true
I agree that this semantics is rather confusing, I filed an issue about it.
Also, KClass doesn't reflect nullability of Kotlin types, and in case if you need to work with declared Kotlin types precisely, you will need to use KType, which does.
UPD: the issue has been marked as fixed, and the equality is explained in KClass.equals KDoc since 1.0.2.

Resources