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"]
Related
I am new to Kotlin and tried to google it, but I don't get it.
Example here:
https://try.kotlinlang.org/#/UserProjects/q4c23aofcl7lb155oc307cnc5i/sgjm2olo277atiubhu2nn0ikb8
Code:
fun main(args: Array<String>) {
val foo = mutableMapOf('A' to 0, 'C' to 0, 'G' to 0, 'T' to 0)
foo['A'] = foo['A'] + 1
println("$foo['A']")
}
I don't get it; why does the indexing operator return the nullable type? The map in the example is defined as Map<Char, Int>, not Map<Char, Int?>.
I can override it via non-null assertion, so this works:
foo['A'] = foo['A']!!.plus(1)
Is there a cleaner way?
You can use the index operator with arbitrary chars, even those that are not part of the map, as in not an existing key. There are two obvious solutions to this, either throw an exception or return null. As you can see in the documentation, the standard library returns null in the operator fun get, which the index operator translates to:
/**
* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
*/
public operator fun get(key: K): V?
The alternative is getValue which is described like this:
Returns the value for the given [key] or throws an exception if there is no such key in the map.
Used like this: val v: Int = foo.getValue('A')
I have following 2 forms of calling a collect operation, both return same result, but I still cannot depend fully on method references and need a lambda.
<R> R collect(Supplier<R> supplier,
BiConsumer<R,? super T> accumulator,
BiConsumer<R,R> combiner)
For this consider the following stream consisting on 100 random numbers
List<Double> dataList = new Random().doubles().limit(100).boxed()
.collect(Collectors.toList());
1) Following example uses pure lambdas
Map<Boolean, Integer> partition = dataList.stream()
.collect(() -> new ConcurrentHashMap<Boolean, Integer>(),
(map, x) ->
{
map.merge(x < 0.5 ? Boolean.TRUE : Boolean.FALSE, 1, Integer::sum);
}, (map, map2) ->
{
map2.putAll(map);
});
2) Following tries to use method references but 2nd argument still requires a lambda
Map<Boolean, Integer> partition2 = dataList.stream()
.collect(ConcurrentHashMap<Boolean, Integer>::new,
(map, x) ->
{
map.merge(x < 0.5 ? Boolean.TRUE : Boolean.FALSE, 1, Integer::sum);
}, Map::putAll);
How can I rewrite 2nd argument of collect method in java 8 to use method reference instead of a lambda for this example?
System.out.println(partition.toString());
System.out.println(partition2.toString());
{false=55, true=45}
{false=55, true=45}
A method reference is a handy tool if you have an existing method doing exactly the intended thing. If you need adaptations or additional operations, there is no special syntax for method references to support that, except, when you consider lambda expressions to be that syntax.
Of course, you can create a new method in your class doing the desired thing and create a method reference to it and that’s the right way to go when the complexity of the code raises, as then, it will get a meaningful name and become testable. But for simple code snippets, you can use lambda expressions, which are just a simpler syntax for the same result. Technically, there is no difference, except that the compiler generated method holding the lambda expression body will be marked as “synthetic”.
In your example, you can’t even use Map::putAll as merge function, as that would overwrite all existing mappings of the first map instead of merging the values.
A correct implementation would look like
Map<Boolean, Integer> partition2 = dataList.stream()
.collect(HashMap::new,
(map, x) -> map.merge(x < 0.5, 1, Integer::sum),
(m1, m2) -> m2.forEach((k, v) -> m1.merge(k, v, Integer::sum)));
but you don’t need to implement it by yourself. There are appropriate built-in collectors already offered in the Collectors class:
Map<Boolean, Long> partition2 = dataList.stream()
.collect(Collectors.partitioningBy(x -> x < 0.5, Collectors.counting()));
I am new to Kotlin and am still trying to learn it. I have been researching this problem for several hours now and still have not figured it out. I want to get an element from inside of a list by it's index. I figured out how to do this with a plain list, like so
val my_list = listOf(1,2,3)
println(my_list.get(0))
The above works, but when I try to do this with a list that is stored inside of a map
val my_list = mutableMapOf<String, Any>()
my_list["set1"] = listOf(1,2,3)
my_list["set2"] = listOf("A","B","C")
my_list["set3"] = listOf("d","e","f")
val sub_list = my_list["set1"]
println(sub_list.get(0))
I get the following error
Unresolved reference. None of the following candidates is applicable
because of receiver type mismatch: #InlineOnly public inline operator
fun <#OnlyInputTypes K, V> Map.get(key: Int): ???
defined in kotlin.collections #SinceKotlin public operator fun
MatchGroupCollection.get(name: String): MatchGroup? defined in
kotlin.text
Note: I primarily use Python, so that is what I am used to. The functionality from Python that I am trying to reproduce in Kotlin is having a dictionary of lists.
The problem is the type declaration of your map, it should be:
val my_list = mutableMapOf<String, List<Any>>()
Any doesn't have a get() method, so there's no way to invoke it.
Even when that problem is solved, you'll probably have to deal with nullability, though, as:
val sub_list = my_list["set1"]
Will return List<Any>?, which means that my_list might not have a value for the specified key. If that's the case, you'll have to do something like:
sub_list?.get(0)?.run { println(it) }
Which in turn, could also cause an exception if the sub_list is empty. That could be solved with something more like:
vsub_list?.firstOrNull()?.run { println(it) }
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.
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