代码之家  ›  专栏  ›  技术社区  ›  Martee

用抽象类型扩展trait的case类

  •  1
  • Martee  · 技术社区  · 7 年前

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

    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
    } 
    

    虽然上面的代码块似乎是一个很好的解决方案,现在我可以在 PropertyPair 特性,但代码仍然有味道。

    1. 我需要包括 [T] 我添加的每个属性三次
    2. 我需要显式添加属性名作为额外函数来访问它,就像它只是case类中的一个字段一样。

    现在初始化 Title 我需要写信

    Title(101L, Property("Title")) 
    

    而不是

    Title(101L, "Title")
    

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

    1 回复  |  直到 7 年前
        1
  •  3
  •   laughedelic    7 年前

    您不需要2个级别,可以替换 trait 具有 abstract class 要使用其构造函数:

    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 所需的值 Property 类,但您希望它们具有不同的名称 prop ,您可以将它们传递给 所有物 构造函数作为 道具 价值

    然后它可以用作

    val size = Size(101L, 42)
    val title = Title(202L, "Foo")
    

    这是一个简单的解决方案。对于更一般的情况,我建议您这样做:

    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
    

    您可以参考 Prop 例如,键入成员

    def buh[P <: AnyProperty](p: P): P#Prop = p.prop
    buh(title) // "Foo"