Pair should implement Map.Entry

// 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.

1 Like

It looks like a Credential

data class Credential(val username:String, val password:String)

Should Credential implements UrlAttrValue and Map.Entry<String, String>?

Yes, that was essentially my AttrValue implementation. I love the way it’s so easy and brief to create these one-off classes with meaningful names in Kotlin! Still, it seems to me this could be handled with a built-in type (Pair if it implemented Map.Entry) instead of creating my own.

I know this is a a pretty old thread, but having Pair implement Map.Entry would be extremely useful. If you look at e.g. org.apache.commons.lang3.tuple.Pair<A,B>, it implements Map.Entry<A,B> as well.

At the moment, I’m forced to use Apache Pair in many locations in my kotlin code for this very reason. I’d much rather stick to Kotlin’s own classes.