// Here's a pair:
val cToC: Pair<String, Char> = "C" to 'c'
// Maps are constructed with pairs:
val m: Map<String, Char> =
mapOf("A" to 'a',
"B" to 'b',
cToC)
// Inputs are pairs, but outputs are Map.Entry. Why the difference?
val entrySet: Set<Map.Entry<String, Char>> = m.entries
// This won't even compile because Pair is not a Map.Entry!
// entrySet.contains(cToC)
// Passes, just as if Pair was meant to implement Entry
assertTrue(entrySet.map { it.toPair() }.contains(cToC))
- Since Pairs are used to create maps, it makes sense that the input items and output items for Map.entries() should match. Making Pair implement Map.Entry is the simplest way to do this.
- Map.Entry is the closest thing Java has to a Pair in the standard library. Javaâs most popular interface that takes two generic, covariant
out
parameters. - Kotlin provides
Map.Entry.toPair()
acknowledging that Map.Entry and Pair are at least somewhat interchangeable. - If you canât/wont make Pair implement Map.Entry, maybe add Pair.toMap.Entry()?
As a workaround, Iâm using this extension function in several of my projects:
fun <K,V> Pair<K,V>.toEntry() = object: Map.Entry<K,V> {
override val key: K = first
override val value: V = second
}
User story:
I had a method for adding attribute-value pairs to a URL:
addAttrs(items: List<UrlAttrValue?>)
List
because youâre technically allowed to have multiple parameters with the same name, and if you do, their order becomes important. I made a UrlAttrValue interface and a default AttrValue data class. I think this is textbook Kotlin/Java OOP.
But the Java/Kotlin client code usually starts with a Map of key-value pairs and doesnât care about order. I found myself forgetting and writing new implementations of AttrValue (despite the existing UrlAttrValue). Eventually, I realized, itâs just a pair (Pair). An immutable key-value pair. If I were using this code primarily from Kotlin, I would make it take a Pair, but itâs used a little more from Java, so what to do?
The best solution I could come up with was addAttrs(items: Iterable<Map.Entry<String, TaintedS?>?>)
. I can now pass myMap.entries
in Kotlin, myMap.entrySet()
in Java. But what about when there are duplicates and order is important?
I have been using a Tuple2 implements Map.Entry
in Java for a few years now:
.addAttrs(vec(tup("duplicateAttr", taint("value1")),
tup("duplicateAttr", taint("value2"))));
It made me expect that in Kotlin, I could:
.addAttrs(listOf("duplicateAttr" to taint("value1"),
"duplicateAttr" to taint("value2")))
I mean, I can use Tuple2 in Kotlin and it works fine. I just found that having Tuple2<A,B>
(an immutable pair) implement Map.Entry<K,V>
has been incredibly convenient and not led to any confusion. Iâve even gone so far as to implement SortedMap to store and return Tuple2âs (well, to alter Rich Hickeyâs implementation to do that).
There could be a corner case where someone uses Pair in a way that should never be treated as a Map.Entry, but I canât think of it. If such a case ever exists, they could make their own class for it. Or maybe even use a NOT Type.