Соответствие строковому образцу наилучшей практике
Ниже приведен код, который не работает, но он описывает, что я хочу сделать.
Не могли бы вы порекомендовать лучший подход к этой проблеме?
def resolveDriver(url: String) = {
url match {
case url.startsWith("jdbc:mysql:") => "com.mysql.jdbc.Driver"
case url.startsWith("jdbc:postgresql:") => "org.postgresql.Driver"
case url.startsWith("jdbc:h2:") => "org.h2.Driver"
case url.startsWith("jdbc:hsqldb:") => "org.hsqldb.jdbcDriver"
case _ => throw new IllegalArgumentException
}
}
Ответы
Ответ 1
В терминах синтаксиса вы можете модифицировать только крошечный бит в вашем случае:
case url if url.startsWith("jdbc:mysql:") => "com.mysql.jdbc.Driver"
Это просто привязывает значение url
к выражению шаблона (которое также равно url
) и добавляет защитное выражение с тестом. Это должно сделать компиляцию кода.
Чтобы сделать это немного больше scala -like, вы можете вернуть Option [String] (я удалил предложение пара, так как это просто для иллюстрации):
def resolveDriver(url: String) = url match {
case u if u.startsWith("jdbc:mysql:") => Some("com.mysql.jdbc.Driver")
case u if u.startsWith("jdbc:postgresql:") => Some("org.postgresql.Driver")
case _ => None
}
То есть, если вы не хотите управлять исключениями.
Ответ 2
Вот альтернативный способ. Сохраните все сопоставления на карте, а затем используйте метод collectFirst
, чтобы найти совпадение. Типичная подпись collectFirst
:
def TraversableOnce[A].collectFirst[B](pf: PartialFunction[A, B]): Option[B]
Использование:
scala> val urlMappings = Map("jdbc:mysql:" -> "com.mysql.jdbc.Driver", "jdbc:postgresql:" -> "org.postgresql.Driver")
urlMappings: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(jdbc:mysql: -> com.mysql.jdbc.Drive
r, jdbc:postgresql: -> org.postgresql.Driver)
scala> val url = "jdbc:mysql:somestuff"
url: java.lang.String = jdbc:mysql:somestuff
scala> urlMappings collectFirst { case(k, v) if url startsWith k => v }
res1: Option[java.lang.String] = Some(com.mysql.jdbc.Driver)
Ответ 3
Начиная с Scala 2.13
, можно сопоставить String
с шаблоном, отменив интерполяцию строки:
val s"jdbc:$dialect:$rest" = "jdbc:mysql:whatever"
// dialect: String = "mysql"
// rest: String = "whatever"
Тогда в нашем случае это просто вопрос сопоставления извлеченного значения (диалект sql) соответствующему драйверу с использованием Map
:
val drivers = Map(
"postgresql" -> "org.postgresql.Driver",
"mysql" -> "com.mysql.jdbc.Driver",
"h2" -> "org.h2.Driver"
)
val driver = drivers(dialect)
// driver: String = "com.mysql.jdbc.Driver"
Если вы ожидаете некорректные данные, вы также можете использовать оператор сравнения:
"jdbc:postgresql:something" match {
case s"jdbc:$dialect:$rest" => Some(dialect)
case _ => None
}
// Option[String] = Some("postgresql")
Ответ 4
import PartialFunction._ // condOpt
val drivers = Map(
"mysql" -> "mysql driver",
"h2" -> "h2 driver"
// ...
)
val pattern = "^jdbc:(\\w+):.*".r
def resolveDriver(url: String) = condOpt(url) {
case pattern(dbms) => drivers.get(dbms)
}.flatten.getOrElse(throw new IllegalArgumentException)