【问题标题】:Case Class constructor parameter give as tuple案例类构造函数参数作为元组给出
【发布时间】:2017-02-23 10:07:09
【问题描述】:

我的模型看起来像这样:

abstract class A(id:String, val time:Int) extends Ordered[A]{
    override def compare(that:TrackingEvent) = this.time.compare(that.time)
}

case class B(id:String, override val time: Int, x:Int, y:int) extends A(id,time) {
    //some methods
}
// more Case Classes who inherit from A

我从 mongodb 获取一些数据并将它们存储在一些案例类中(每个类都继承自 A)

我现在做的是:

val header = getHeader(doc) //doc => actual row, return: Tuple2(String,Int)
val xy = getXYDatapoint(doc) // return: Tuple2(Int,Int)
val b = B(header._1,header._2,xy._1,xy._2)

每个继承的案例类都使用相同的 getHeader(doc) 函数来获取标题。 (这就是为什么抽象类 A 有两个参数)

我希望它看起来更好,以便我可以删除一些行。

类似:

val b = B(header+xy) 

或者别的什么。我可以更改整个 Scala 代码,并愿意寻求任何帮助,让它看起来更好(我是 Scala 新手)

我尝试过使用 shapeless,但没有成功:

import shapeless._
import syntax.std.tuple._

val header = getHeader(doc)
val xy = getXY(doc)
val param = header++xy

val b = (B.apply _).tupled(param) // didn't work because param is of type prepend.Out
val b = (B.apply _).tupled(("a",2,3,4)) // would work 

如果有人知道一些不错的提示或技巧,我很乐意听到。

【问题讨论】:

    标签: scala tuples shapeless case-class


    【解决方案1】:

    你真的很亲近;你只需要明确地说你想要的类型是一个元组。以下是所有有需要的人的一般示例:

    import shapeless.syntax.std.tuple._
    
    case class Test(a: Int, b: Int, c: String, d: Int)
    
    val tuple: (Int, Int, String, Int) = (1, 2) ++ ("a", 3)
    
    Test.tupled(tuple) // works
    

    但是,如果我可以补充的话,您的用例可以通过一些额外的建模来简化。那么你根本不需要无形的。最近我看到很多人滥用案例课程。它们旨在作为抽象数据类型 (ADT),而不是一般意义上的类。如果您的案例类需要包含一些逻辑,那么最好将其设为一个类。

    不过,您确实有案例类的用例。为什么不在两个独立的 ADT 中捕获有关标头和数据点的信息?

    这是完整的代码:

    case class Header(id: String, time: Int)
    case class DataPoint(x: Int, y: Int)
    
    abstract class A(id: String, val time: Int) {
      // whatever
    }
    
    class B(header: Header, dataPoint: DataPoint) extends A(header.id, header.time) {
      // whatever
    }
    
    val dataPoint = DataPoint(1, 2)
    val header = Header("header", 42)
    
    val b = new B(header, dataPoint)
    

    当然,如果 A 类的参数 idtime 的语义是来自标头的那些,你甚至可以让 A 只接受一个参数:abstract class A(header: Header)

    就我个人而言,我认为这是干净、清晰且建模良好的。当然,你的看法可能不同。 :)

    【讨论】:

    • 我觉得你的第二点很有意思。但是当你有一个 C 类需要一个数组,D 类需要 2 个字符串时,你还会这样做吗?
    • 它始终取决于用例。您是否有 C、D 等,每个都有不同的数据类型但都代表标题,或者它们是来自不同的东西(不仅仅是标题)的一堆 ids 和 times?顺便说一句,我们不要在这里过度扩展讨论 :) 我们解决了主要问题(案例类 元组)。我给了你一个关于设计的提示,但如果有更多关于它的问题,我们可以随时在 gitter 或其他地方见面,或者你可以发布后续问题。
    【解决方案2】:

    您可以为案例类定义另一个应用方法:

    case class B(id: String, override val time: Int, x: Int, y: Int) extends A(id, time)
    object B {
      def apply(header: (String, Int), coordinates: (Int, Int)): B =
        B(header._1, header._2, coordinates._1, coordinates._2)
    }
    

    现在您可以将其用作:

    val header = getHeader(doc)
    val coords = getXY(doc)
    val b = B(header, coords)
    

    这很好,因为您不再需要调用 tupled,您只需使用新的 apply 方法构建您的实例并直接传递 getHeadergetXY 的结果。

    【讨论】:

      猜你喜欢
      • 2019-09-29
      • 1970-01-01
      • 1970-01-01
      • 2011-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-01
      • 1970-01-01
      相关资源
      最近更新 更多