https://kotlinlang.org logo
#announcements
Title
# announcements
r

Robert

10/08/2019, 4:27 PM
What is the easiest way to extend an
Map
? I tried extending
HashMap
but it is final. When I extend
Map
I have to implement all types of body functions like
containsKey
,
containsValue
,
get
,
isEmpty
and properties
entries
,
keys
,
size
and
values
a

Alowaniak

10/08/2019, 4:42 PM
Perhaps
delegate
by?
i

ilya.gorbunov

10/08/2019, 4:48 PM
Try `AbstractMap`/`AbstractMutableMap`. Then you need to implement only a set or mutable set of entries.
r

Robert

10/08/2019, 4:49 PM
@Alowaniak not sure how
@ilya.gorbunov Ty! I can work with that
s

Shawn

10/08/2019, 5:09 PM
r

Robert

10/08/2019, 5:24 PM
When using the
AbstractMap
, how do I set the initial values? As I can't use
mapOf
to bootstrap it..
r

Ruckus

10/08/2019, 5:33 PM
You would need to define the appropriate constructor, and then a helper function like
robertMapOf(vararg Pair<K, V>)
or
Map<K, V>.toRobertMap()
m

Matteo Mirk

10/09/2019, 7:44 AM
From a design perspective, it’s never a good idea to extend a collection --unless you have some particular data structure need. I suggest you use composition, initialize your type with a map, and implement just the methods you need. Maybe with good names, meaningful to your domain.
s

Stephan Schroeder

10/09/2019, 12:21 PM
I can’t believe nobody mentioned extension functions yet.
r

Robert

10/09/2019, 6:57 PM
@Matteo Mirk I don't understand. If I have a map of items consisting of key-value, it would be wasteful to composite a list and re-implement all the methods like
contains
etc. Instead I just extend the existing class with methods and add some new ones. I don't see how that is "wrong"
@Stephan Schroeder I guess that could work, but I want to implement a specific list in this case, not add a function to all lists. I think that is what you mean?
@Ruckus Do you know of any example? I'm stuck on the last part of the code, for example
.apply
. I don't have and
putAll
method or something, as it's a normal map
Copy code
class RobertMap: AbstractMap<Int, String>() {
    override val entries: Set<Map.Entry<Int, String>>
        get() = throw UnsupportedOperationException()
}
public fun <Int, String> robertMapOf(vararg pairs: Pair<Int, String>): RobertMap = RobertMap(pairs.size).apply {}
mapOf
does this, which seems to create a LinkedHashMap by default?
if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()
r

Ruckus

10/09/2019, 7:14 PM
Well, depending on how simple you want to go, you could do
Copy code
class RobertMap(vararg entries: Pair<Int, String>) : AbstractMap<Int, String>() {
    override val entries: Set<Map.Entry<Int, String>> =
        Collections.unmodifiableSet(entries.mapTo(mutableSetOf()) { Entry(it.first, it.second) })

    private class Entry(override val key: Int, override val value: String) : Map.Entry<Int, String>
}
which would let you use
Copy code
val map = RobertMap(1 to "one", 2 to "two", ...)
r

Robert

10/09/2019, 7:18 PM
Aweomse! It doesn't seem to recognize
Colletions
but maybe that's because I'm in a MP project. So I thought an unmodifiable Map would be cheaper than a mutable one. But from the code I guess this is not true; as it is change to mutableSet before it becomes the unmodifiable set?
r

Ruckus

10/09/2019, 8:03 PM
Yeah, sorry,
Collections.unmodifiableSet
is a Java stdlib function. I was optimizing for readability / LOC. You could save it more efficiently and use your own implementation of an abstract set to access it.
👍 1
m

Matteo Mirk

10/10/2019, 2:57 PM
@Robert Composition doesn’t mean reproducing the whole interface of an associated object. Inheriting from a collection, and in general from a class you have no control over, is a bad idea because you’re forced to conform to its interface and internal representation, which maybe you don’t want (although there are use cases for it). The important question you want to ask yourself is: why do you need to extend a Map, what are you trying to achieve? If you need to build an enhanced version of a Map data structure (which means ALL Map interface + your methods or customized behavior) then okay go and inherit from
AbstractMap
, but it’s seldom the case. If instead you need a domain object that can be backed by a map, then I strongly advise to use composition and create your domain-specific interface, which of course won’t replicate any of the Map’s interface. If you need more explanation I can provide some code example, just ask.
258 Views