【问题标题】:Scala Type Parameter and Case ClassesScala 类型参数和案例类
【发布时间】:2013-12-18 00:36:59
【问题描述】:

我有这样的功能:

private def fixBrand[T]( item:T ) = item.copy(brand = item.brand.toLowerCase)

这不是编译---抱怨 T 没有函数副本。 现在我知道我将传入的每个项目都是一个案例类。 我怎么能告诉编译器?这些案例类中的每一个也有一个“品牌”字段,但在对象层次结构方面是不相关的。

我以为我读到 Scala 2.10 有一个特性允许我将 T 用作具有函数 x、y、...的“事物”?不过忘记了这个功能叫什么!

【问题讨论】:

    标签: scala types


    【解决方案1】:

    您可以使用结构类型:

    def fixBrand[T <: { def copy(t: String): T; def brand: String }](item: T) = item.copy(item.brand.toLowerCase)
    

    【讨论】:

    • 这不适用于具有多个字段的案例类的复制方法。这是因为复制方法没有那个确切的签名。仅使用一个参数为副本创建重载也行不通,因为您无法根据参数区分这两个重载。向每个类添加像 brandCopy(t: String) 这样的方法是该解决方案适用的,但您必须手动将此方法添加到每个案例类。
    • 我认为你是对的......它不适用于复制,因为参数数量未知,因为它是编译器生成的。但是...我很高兴 Lee 让我想起了这个术语:结构类型!
    【解决方案2】:

    仅从您的函数来看,根本不清楚Tcopy 方法,更不用说这个方法也应该有brand 作为参数之一。

    我认为你能得到的最接近的东西是视图边界的使用,比如

    trait WithBrandCopyable[T] {
      def copy(brand: String): T
      def brand: String
    }
    
    object Classes {
      case class Class1(brand: String, number: Int)
      case class Class2(factor: Double, brand: String)
    
      implicit def class1ToWithBrandCopyable(obj: Class1) = new WithBrandCopyable[Class1] {
        def copy(brand: String) = obj.copy(brand = brand)
        def brand = obj.brand
      }
    
      implicit def class2ToWithBrandCopyable(obj: Class2) = new WithBrandCopyable[Class2] {
        def copy(brand: String) = obj.copy(brand = brand)
        def brand = obj.brand
      }
    }
    
    object Main {
      def fixBrand[T <% WithBrandCopyable[T]](obj: T) =
        obj.copy(brand = obj.brand.toLowerCase)
    
      def main(args: Array[String]) {
        import Classes._  // Import both classes and implicit conversions
    
        val obj1 = Class1("BrAnD1", 10)
        val obj2 = Class2(0.3, "BRAnd2")
    
        println(fixBrand(obj1))  // Prints Class1("brand1",10)
        println(fixBrand(obj2))  // Prints Class2(0.3,"brand2")
      }
    }
    

    当然,变化是可能的(例如,使用成熟的类型类),但它们都将包含某种形式的隐式。

    请注意,可以将WithBrandCopyable 设为每个案例类的超特征,然后对泛型参数使用普通的上限。我使用隐式转换有两个原因。首先,因为我想保留copy 方法。不可能在某些特征中定义 copy(brand: String) 方法,然后由您的案例类继承此特征,因为它会与编译器生成的 copy(&lt;case class arguments&gt;) 方法发生冲突,正如马克在他对另一个答案的评论中提到的那样。其次,隐式转换根本不需要您修改案例类 - 编写转换方法就足够了。

    但是,这种方法需要一些样板文件,即接口特征和每个案例类到此特征的隐式转换。可以通过使用宏来减少这个样板,但我认为不会那么多。

    这个样板确实是有原因的。 obj.copy(brand = brand) 方法调用在源代码中可能看起来一样,但实际上它是对两个完全不同的方法的调用:第一个签名像(String, Int) =&gt; Class1,第二个签名像(Double, String) =&gt; Class2,更不用说它们属于不同的没有共同祖先的类。当然,这些调用的字节码会有所不同。宏在这里会有所帮助,但据我所知,它们仍然不足以完全减少这个样板,至少在当前稳定版本的 Scala 中是这样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多