【问题标题】:Scala - calling a method with generic type parameter given a string value that determines the correct typeScala-给定一个确定正确类型的字符串值,调用具有泛型类型参数的方法
【发布时间】:2014-08-31 03:45:03
【问题描述】:

我正在设计一个 2 层架构的 API 接口。它从 URL 中获取一个字符串参数 fieldName 并返回一个 JSON 字符串。 fieldName 指的是我的数据库表中的一个字段。您可以将其签名视为:

def controller(fieldName: String): String

在控制器中,我想在我的数据访问层调用一个方法来执行以下查询:

SELECT fieldName, SUM(salary) FROM Employee GROUP BY fieldName

由于字段的类型不同,查询结果的类型也会不同。该方法由泛型类型参数 T 参数化,该类型参数对应于名为 fieldName 的字段的类型。

def getTotalSalaryByField[T](fieldName: String): Map[T, Long]
  • 如果 fieldName 是“age”,T 应该是 Int。
  • 如果 fieldName 是“name”,T 应该是 String。 等等。

在运行时给定一个特定的 fieldName,我如何调用这个方法来给它正确的类型?

我不想写很多 if-else 或模式匹配语句来选择类型。它看起来像这样:

fieldName match {
    case "age" => serializeToJson(getTotalSalaryByField[Int]("age"))
    case "name" => serializeToJson(getTotalSalaryByField[String]("name"))
    ...
    // 100 more for 100 more fields
}

这很难看。如果我用 Python 写这个,只需要一行:

json.dumps(getTotalSalaryByField(fieldName))

Scala 是不是不适合 Rest 后端编程?因为这似乎是人们会遇到的常见模式,而静态类型会阻碍。我希望看到一些关于我应该如何以 scala-ish 方式解决整个问题的建议,即使这意味着重塑、重写 DAL 和控制器。

编辑: @drexin,DAL 方法的实际签名是

def myDAOMethod[T](tf: Option[TimeFilter], cf: CrossFilter)
    (implicit attr: XFAttribute[T]): Map[T, Long]

T 是 fieldName 的类型。 Long 是 y 的类型。如您所见,我需要根据 url 参数中的 fieldName 选择一个类型为 T。

编辑: 添加了一些代码并使用例清晰

【问题讨论】:

  • 您希望 Scala 为您做什么?从不同的文本类型推断?除了模式匹配还有其他方法吗?
  • 能否提供您要调用的方法的签名?
  • 可能是 sqltyped project 是你真正追求的,这使用 scala 宏来为列提供正确的类型。
  • @goral, “除了模式匹配还有其他方法吗?”这是我的问题。
  • @drexin,问题已编辑。

标签: scala generics


【解决方案1】:

在编译时和运行时知道变量的类型是有区别的。

如果fieldName 在编译时未知(即它是一个参数),并且列的类型因fieldName 而不同,那么您将无法指定方法的返回类型在编译时。

对于编译时指定的类型T,您需要使用返回AnyRef 的DAO 方法,而不是返回T 的方法。

旧答案:

数据库访问库可以返回值而不需要知道它们的类型,您的代码也需要这样做。

您正在寻找使用类型参数 T 的 DAO 方法:

def myDAOMethod[T](tf: Option[TimeFilter], cf: CrossFilter)
    (implicit attr: XFAttribute[T]): Map[T, Long]

...但是正如您所说,您事先并不知道T的类型,因此此方法不适用。 (T用于将数据库列数据转换为String或Int)。

您的 DAO 应该提供此方法的无类型版本,类似于:

def doSelect(tf: Option[TimeFilter], cf: CrossFilter): Map[AnyRef, Long]

你用的是什么数据库访问库?

【讨论】:

  • 网格增益。返回结果后,我在 DAO 方法中从 Any 转换为特定类型。
【解决方案2】:

如果您使用的是 Play 框架,那么我建议您使用 Play JSON API。

https://www.playframework.com/documentation/2.1.1/ScalaJson

代替

def getTotalSalaryByField[T](fieldName: String): Map[T, Long] = ???

你可以写

def getTotalSalaryByField(fieldName: String): Map[JsValue, Long] = ???

因为JsNumberJsStringJsNull 等所有类型都继承自通用 JSON 特征 JsValue

Json.toJson()包装你的queryResult

def getTotalSalaryByField(fieldName: String): JsObject = ???

【讨论】:

    【解决方案3】:

    我认为您在这里有三个选择:

    第三个选项可能是您正在寻找的,但它基于实验性 scala 功能(即宏)。我想它会在编译阶段连接到您的数据库并检查架构。如果您使用一些单独的 sql 文件生成模式,那么它应该更容易。然后宏将生成所有具有正确类型硬编码的样板。

    您可能找不到完全符合您需要的工作示例,但有一个用于 RFC 文件的示例,您可以将其用作灵感:https://github.com/travisbrown/type-provider-examples

    【讨论】:

      【解决方案4】:

      我不能 100% 确定我理解你的问题,所以如果我完全回答其他问题,请道歉。

      您需要 Scala 能够根据预期的返回类型猜测字段的类型。也就是在下面的代码中:

      val result : Map[String, Long] = myDAOMethod(tf, cf)
      

      您希望 Scala 能够正确推断,因为您需要 Map[String, Long],所以您的 x 变量的类型为 T

      在我看来,您已经拥有了所需的一切。我不知道XFAttribute[T] 是什么,但我怀疑它允许您将结果集中的条目转换为T 类型的实例。

      更重要的是,您已经将其声明为隐式参数。

      如果你在作用域中有一个隐式的XFAttribute[String],那么前面的代码应该可以编译、运行并且是类型安全的。

      作为一个小改进,我会更改您的方法的签名以使用上下文边界,但这主要是个人喜好问题:

      // Declare the implicit "parsers"
      implicit val IntAttribute: XFAttribute[Int] = ???
      implicit val StringAttribute: XFAttribute[String] = ???
      
      // Empty implementation, fill it with your actual code.
      def myDAOMethod[T: XFAttribute](tf: Option[TimeFilter], cf: CrossFilter): Map[T, Long] = ???
      
      // Scala will look for an implicit XFAttribute[Int] in scope, and find IntAttribute.
      val ages: Map[Int, Long] = myDAOMethod(tf, cf)
      
      // Scala will look for an implicit XFAttribute[String] in scope, and find StringAttribute.
      val names: Map[String, Long] = myDAOMethod(tf, cf)
      

      我不确定您的问题是否暗示您还想将字符串"age"Int 类型紧密绑定。当然,这也是可能的,但这完全是另一个答案,我不想用不必要的漫谈来污染这个问题。

      【讨论】:

      • 感谢您花时间写这篇文章。不,问题不是从返回类型中猜测 T,而是从 x 的值中获取 T,用 T 调用方法,这样做不需要编写一长串 if-else 语句。
      • 您的意思是说您在调用myDAOMethod 时既不知道该字段的名称也不知道其类型?您是否介意用用例更新您的问题,可能是一些代码显示您希望如何使用您的方法,......以便您使用的约束更清晰?可能只有我一个人,但我发现很难准确理解你想要做什么。
      • 问题已编辑。我知道字段的名称和类型。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-01
      • 2019-04-24
      • 1970-01-01
      • 1970-01-01
      • 2012-09-08
      • 2015-12-28
      • 1970-01-01
      相关资源
      最近更新 更多