Не переменный аргумент типа String в типе Map [String, Any] - PullRequest
0 голосов
/ 30 апреля 2020

У меня есть простой способ получить вложенный ключ из хэш-карты. Мне нужно сопоставить шаблон на Map[String,Any], чтобы я мог продолжать итерацию во вложенных данных до тех пор, пока не получу плоское значение:

def get(map: Map[String, Any], key: String): Any = {
   var fields: mutable.Seq[String] = key.split('.')
   var currentKey: String = fields.head
   var currentValue: Any = map

   while (fields.nonEmpty && currentValue.isInstanceOf[Map[String, Any]]) {
     currentKey = fields.head
     fields = fields.drop(1)
     currentValue match {
       case m: Map[String, Any] => currentValue = m.getOrElse(currentKey, None)
       case _                   =>
     }
   }
   if (fields.nonEmpty) None else currentValue
 }

Это работает, когда я использую его только в пределах scala, но если он вызывается с java, я получаю ошибку non-variable type argument String in type scala.collection.immutable.Map[String,Any].

Я видел некоторые другие решения, которые требуют от вас рефакторинга кода и переноса карты в класс case, но это очень разрушительно для всего кода, который использует этот метод. Есть ли более простое решение?

1 Ответ

1 голос
/ 01 мая 2020

Вы не можете сопоставить шаблон с Map[String,Any] из-за стирания типа. Компилятор предупредит об этом. Этот код просто совпадает с Map[_,_], поэтому он будет успешным с любым типом ключа, а не только с String.

. Таким образом, метод по своей сути глючит, и кажется, что вызов из Java выявляет ошибки, которые сделали не появляется при использовании Scala.

Так как вы еще не используете это с Java, я бы переключился на безопасную реализацию кода для Java и затем быстро перенес бы устаревший код в эту версию насколько это возможно. Хотя это может быть разрушительным, это будет исправление ошибки проектирования, которая привела к ошибочному коду, поэтому это должно быть сделано раньше, а не позже. (Всякий раз, когда вы видите Any, используемый в качестве типа значения, вполне вероятно, что дизайн в какой-то момент вышел из строя)

Типобезопасная версия не так сложна, вот примерная реализация:

class MyMap[T] {
  trait MapType
  case class Value(value: T) extends MapType
  case class NestedMap(map: Map[String, MapType]) extends MapType

  def get(map: Map[String, MapType], key: String): Option[T] = {
    def loop(fields: List[String], map: Map[String, MapType]): Option[T] =
      fields match {
        case Nil =>
          None
        case field :: rest =>
          map.get(field).flatMap{
            case Value(res) => Some(res)
            case NestedMap(m) => loop(rest, m)
          }
      }

    loop(key.split('.').toList, map)
  }
}

В действительности MyMap должен на самом деле хранить данные Map, а не передавать их в get, и существуют методы для безопасного построения вложенных карт.

...