Добавьте новое значение ключа, представленное `Pair`, в` MutableMap`

В настоящее время у меня есть этот класс с dsl как способность к построению

 class GRLMessage { var headerMap : MutableMap<String, String> = mutableMapOf() lateinit var methodType : GRLMethod lateinit var multipartObject : IGRLMultipart fun message(closure: GRLMessage.() -> Unit) : GRLMessage { closure() return this } fun method(closure: GRLMessage.() -> GRLMethod) : GRLMessage { methodType = closure() return this } fun headers(closure: GRLMessage.() -> Unit) : GRLMessage { closure() return this } fun header(closure: GRLMessage.() -> Pair<String, String>) : GRLMessage { var pair = closure() headerMap.put(pair.first, pair.second) return this } fun multipart(closure: GRLMessage.() -> IGRLMultipart) : GRLMessage { multipartObject = closure() return this } } 

И я проверяю это так

 class GRLMessageTest { data class DummyMultipart(val field: String) : IGRLMultipart { override fun getContent() { this } } @Test fun grlMessageBuilderTest() { val grlMessage = GRLMessage().message { method { GRLMethod.POST } headers { header { Pair("contentType", "object") } header { Pair("objectType", "DummyMultipart") } } multipart { DummyMultipart("dummy") } } val multipart = DummyMultipart("dummy") val headers = mapOf( Pair("contentType", "object"), Pair("objectType", "DummyMultipart") ) val method = GRLMethod.POST assertEquals(multipart, grlMessage.multipartObject) assertEquals(method, grlMessage.methodType) assertEquals(headers, grlMessage.headerMap) } } 

Но, несмотря на предоставление

 header { Pair("contentType", "object") } 

Мне все же приходится оценивать closure внутри метода header и напрямую put ключ и значение в мою MutableMap

 fun header(closure: GRLMessage.() -> Pair<String, String>) : GRLMessage { var pair = closure() headerMap.put(pair.first, pair.second) return this } 

Есть ли лучший способ добавления записей в Map ?

headerMap ли ваша headerMap быть var? Если нет, вы можете изменить его на val и использовать headerMap += closure() .

Добавление функции расширения делает ваши беглые методы более очевидными:

 fun <T: Any> T.fluently(func: ()->Unit): T { return this.apply { func() } } 

При этом ваша беглая функция всегда ясно о ее возврате:

 fun header(closure: GRLMessage.() -> Pair<String, String>) : GRLMessage { return fluently { headerMap += closure() } } 

Это действительно то же самое, что:

 fun header(closure: GRLMessage.() -> Pair<String, String>) : GRLMessage { return this.apply { headerMap += closure() } } 

Но функция расширения добавляет удобство чтения.

Выше я использую ответ, данный @Ruckus для решения вашего конкретного вопроса о добавлении Pair в headerMap . Но у вас есть другие варианты, которые вы, возможно, захотите узнать о других случаях использования вашего DSL …

Вы можете использовать let , apply или with которым разрешаете любой тип разложения результатов вызова closure() (возможно, в будущем это будет сложнее, чем Pair ). Все они в основном одинаковы, минус их итоговое значение :

 with(closure()) { headerMap.put(this.first, this.second) } closure().apply { headerMap.put(this.first, this.second) } closure().let { headerMap.put(it.first, it.second) } 

Использование let или apply приятно, если вы хотите обрабатывать случай, когда closure() допускает возврат с null значением, и в этом случае вы можете принять действие только в том случае, если не null :

 closure()?.apply { headerMap.put(this.first, this.second) } closure()?.let { headerMap.put(it.first, it.second) } 

Другие примечания о вашем коде:

  • используйте val вместо var если у вас нет другого выбора
  • lateinit (или аналогичные Delegates.notNull() ) кажутся опасными для использования в неконтролируемом жизненном цикле, где нет гарантии, что он будет завершен, потому что сообщение об ошибке будет запутанным и произойдет в какое-то неожиданное время в будущем. Существуют, вероятно, другие способы решения этой проблемы с помощью DSL, которая объединяет вызовы для создания более многоступенчатой ​​грамматики
  • Вы можете сократить код, только имея типы на одной стороне задания, например:

     val myMap = mutableMapOf<String, String>() 

    вместо

     var myMap : MutableMap<String, String> = mutableMapOf() 

На данный момент в качестве решения я создал расширение для MutableMap

 fun MutableMap<String, String>.put(pair : Pair<String, String>) { this.put(pair.first, pair.second) } 

Что позволило мне писать так

 fun header(closure: GRLMessage.() -> Pair<String, String>) : GRLMessage { headerMap.put(closure()) return this }