【问题标题】:How to define anything that extends this trait如何定义扩展此特征的任何内容
【发布时间】:2011-07-11 11:53:10
【问题描述】:

参考以下代码sn-p:

 trait Fruit {
   val color:String
   def == (fruit:Fruit) = this.color == fruit.color
 }

 case class Orange(color:String) extends Fruit 

 case class Apple(color:String) extends Fruit

正如预期的那样,Orange("red") == Orange("red")true。但是,我想强制只能比较相同类型的水果,例如Orange("red") == Apple("red") 应该给出错误。我们能否以优雅的方式在 trait Fruit== 签名中强制执行此操作?

编辑:我希望在编译时捕获错误,而不是在运行时。

【问题讨论】:

    标签: scala scala-2.8


    【解决方案1】:

    试试:

     trait Fruit {
       val color: String
       def == (fruit: Fruit) = getClass == fruit.getClass && color == fruit.color
     }
    

    【讨论】:

    • 这并不完美,因为它会返回 false 而不是编译器错误。
    • @Jus12 好的,在你编辑之后,我不得不再想一想......嗯,是否可以在编译时检查颜色?我不这么认为。您可以检查类型,但不能检查 color 的运行时分配。
    • 好的,我知道你想要什么。您希望只允许比较 (==) 橙子和橙子以及苹果和苹果。
    【解决方案2】:

    Scalaz 有一个 Equal “类型类”可以解决这个问题,尽管它使用了不同的运算符。

    https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Equal.scala

    它的核心基本上是这个(虽然我使用 === 他们使用一些 unicode)

    /** Defines a type safe === operator */
    trait Equals[A] {
     def ===(y : A) : Boolean
    }
    
    /** A conventient way to define Equals traits based on the == operator */
    def equalA[A](x : A) = new Equals[A] {
      def ===(y : A) = x == y
    }
    

    而且是这样使用的

    // one for oranges
    implicit val EqualsOrange = equalA[Orange] _
    
    // one for apples
    implicit val EqualsApple = equalA[Apple] _
    
    
    Orange("red") === Orange("red") // true
    
    Orange("red") === Orange("green") // false
    
    Orange("red") === Apple("red") // Compile error
    

    【讨论】:

    • 感谢您的参考。出于好奇,如何使用标准键盘输入 字符?
    • 取决于操作系统。在 Ubuntu 下,您按住 CTRL-SHIFT-U,松开,然后键入 225F 并按空格键。在 Windows 下,我认为您按住 ALT 并键入 225F,但我可能是错的。但你不需要。如果你看看github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/…Scalaz 只是让 ≟ 成为 === 的同义词。
    【解决方案3】:

    不幸的是,您无法静态检查...至少不使用 ==,它使用 Java 的 Object#equals 方法,其中所有内容都根据原始对象强调定义。

    如果您想要类型安全,那么您唯一的选择是实现另一个运算符,可能类似于 =|=,然后将其与类型类结合以确保您的安全。

    我相信 scalaz 对类型安全的平等也有一些有用的东西,但对库的了解不足以肯定地说明这一点。

    您可以采取的另一种方法是使用canEqual 模式as described here,它只在运行时是安全的。这已被案例类使用,并提供了一种在 LSP 适合相等时选择性地破坏 LSP 的好方法。

    【讨论】:

      【解决方案4】:

      如果您愿意将方法名称从 == 更改为其他名称,我们可以这样做:

      trait Fruit {
       type FruitType <: Fruit
       val color:String
       def === (fruit:FruitType) = this.color == fruit.color
      
      }
      
      
      case class Orange(color:String) extends { type FruitType = Orange } with Fruit 
      
      case class Apple(color:String) extends {type FruitType = Apple } with Fruit
      

      如果我们比较苹果和橙子,我们会得到:

      Apple("red") === Orange("red")
      <console>:11: error: type mismatch;
       found   : Orange
       required: Apple
             Apple("red") === Orange("red")
      

      和苹果与我们得到的相同颜色的苹果:

      Apple("red") === Apple("red")
      res10: Boolean = true
      

      我们得到不同颜色的苹果:

      Apple("green") === Apple("red")
      res11: Boolean = false
      

      【讨论】:

      • 这是个好建议。我希望有一种更优雅的方式。
      猜你喜欢
      • 2021-02-13
      • 1970-01-01
      • 2019-05-11
      • 2015-01-21
      • 1970-01-01
      • 2019-06-11
      • 2018-02-06
      • 2015-06-03
      • 2020-07-27
      相关资源
      最近更新 更多