Scala 用抽象类型扩展case类

Scala 用抽象类型扩展case类,scala,traits,case-class,Scala,Traits,Case Class,我有两个这样的案例课程: case class Size(id: Long, size: Int) case class Title(id: Long, title: String) . . . 我有10个功能差不多的。我决定将它们结合在一个共同的特征下,这导致了如下结果: trait Property[T]{ val value: T } trait PropertyPair[T]{ val id: Long val prop: Property[T] } case

我有两个这样的案例课程:

case class Size(id: Long, size: Int)

case class Title(id: Long, title: String)
.
.
.
我有10个功能差不多的。我决定将它们结合在一个共同的特征下,这导致了如下结果:

trait Property[T]{
    val value: T
}

trait PropertyPair[T]{
   val id: Long
   val prop: Property[T]
}

case class Size(id: Long, prop: Property[Int]) extends PropertyPair[Int] {
    def size: Int = prop.value
}

case class Title(id: Long, prop: Property[String]) extends PropertyPair[String] {
    def title: String = prop.value
} 
sealed trait AnyProperty {
  val id: Long

  type Prop
  val prop: Prop
}

sealed abstract class Property[T](
  val prop: T
) extends AnyProperty {
  type Prop = T
}
虽然上面的代码块似乎是一个很好的解决方案,现在我可以在
PropertyPair
trait下定义函数,但代码仍然有味道

  • 我需要为我添加的每个属性包含三次
    [T]
  • 我需要显式地添加属性名作为一个额外函数来访问它,就像它只是case类中的一个字段一样
  • 现在要初始化
    标题
    我需要写

    Title(101L, Property("Title")) 
    
    而不是

    Title(101L, "Title")
    

    出于某种原因,我确信有一种比我提供的更优雅、更不容易出错的解决方案。

    您不需要两个级别,可以用
    抽象类
    替换
    trait
    ,以利用其构造函数:

    sealed abstract class Property[T](val prop: T) {
      val id: Long
    }
    
    case class Size(id: Long, size: Int) extends Property[Int](size)
    case class Title(id: Long, title: String) extends Property[String](title)
    
    每种情况都有一个
    id
    值,这是
    属性
    类所需要的,但是由于您希望它们对
    prop
    有不同的名称,您可以将它们作为
    prop
    值传递给
    属性
    构造函数

    然后它可以用作

    val size = Size(101L, 42)
    val title = Title(202L, "Foo")
    
    这是一个简单的解决方案。对于更一般的情况,我建议您这样做:

    trait Property[T]{
        val value: T
    }
    
    trait PropertyPair[T]{
       val id: Long
       val prop: Property[T]
    }
    
    case class Size(id: Long, prop: Property[Int]) extends PropertyPair[Int] {
        def size: Int = prop.value
    }
    
    case class Title(id: Long, prop: Property[String]) extends PropertyPair[String] {
        def title: String = prop.value
    } 
    
    sealed trait AnyProperty {
      val id: Long
    
      type Prop
      val prop: Prop
    }
    
    sealed abstract class Property[T](
      val prop: T
    ) extends AnyProperty {
      type Prop = T
    }
    
    (其余都一样)

    这种方法的优点是,您可以使用top trait来引用任何属性,例如

    def foo[P <: AnyProperty](p: P): Long = p.id
    foo(size) // 101L
    

    你想从继承层次结构中得到什么?你这么做是因为你认识到一个相似性,还是因为它实际上给了你一些你目前没有/预计会出现的新用例的功能?它实际上是最后一个代码块中的一个标签符号吗?还有一个问题:如果buh方法放在属性类中,我们需要类型保护吗?非常有趣。我假设你也可以通过传递buh方法将AnyProperty数组映射到它们的prop值,对吗?@Martee可能只是输入错误,但为了避免混淆:它是投影(投影/选择,而不是保护)。答案是否定的,当在
    属性
    中时,您可以直接引用
    属性
    类型成员。@JordanCutler返回类型会有问题。如果您执行
    List[AnyProperty](大小、标题).map(buh)
    ,您将获得
    List(42,“Foo”)
    。但是
    列表
    是一个同质集合,即所有元素都具有相同的类型,在这种情况下,它将被派生为
    任何
    (Int和
    字符串
    的最低公共超类型)。这对您来说可能很好,但是您不需要任何类型的投影,只需进行
    buh
    return
    any