【问题标题】:Scala: Creating a trait that forces classes to re-implement certain methodsScala:创建一个强制类重新实现某些方法的特征
【发布时间】:2020-02-26 08:53:43
【问题描述】:

我正在尝试实现一个特征,它强制每个扩展它的类(并且不是抽象的)实现某些方法(即使它们已经存在于超类中)。具体应该是这样的:

trait Debugable {

  override def hashCode(): Int = ???

  override def equals(obj: Any): Boolean = ???

  override def toString: String = ???

}

这就是特征,这就是实现:

class TestClass {

}

object TestClass{
  def main(args: Array[String]): Unit = {
    val t = new TestClass
    println(t)
  }
}

上面的代码最好不要编译(因为可调试的类没有实现所需的方法)。实际上,这不仅可以编译,而且不会引发运行时异常(它只是采用对象类的默认实现)。

到目前为止,没有任何东西能够产生预期的行为。我认为宏可以提供帮助,但我不确定宏是否可以表达如下内容:

foreach class
     if class.traits.contains(debugable)
         return class.methods.contains(toString)

我知道我可以让一些外部脚本进行检查并将其与 gradle 编译任务捆绑在一起,但我希望有一个可以作为项目本身的一部分实施的解决方案(因为这将使它独立于使用的构建管道,因为它应该比编写爬取整个源代码的脚本更简单、更容易维护/扩展)

【问题讨论】:

    标签: scala macros traits static-analysis


    【解决方案1】:

    这很接近(当然比我所拥有的有所改进), 但它并不完全符合我的要求。如果我有一个“链” 类,那么链的顶部就可以实现 方法。

    Typeclass 方法可以帮助解决这个问题,例如,

    trait Debuggable[T] {
      def hashCode(v: T): Int
      def equals(v: T, b: Any): Boolean
      def toString(v: T): String
    }
    
    class Foo
    class Bar
    class Qux extends Foo
    
    object Debuggable {
      implicit val fooDebuggable: Debuggable[Foo] = new Debuggable[Foo] {
        def hashCode(v: Foo) = 42
        def equals(v: Foo, b: Any) = true
        def toString(v: Foo) = "woohoo"
      }
      implicit val barDebuggable: Debuggable[Bar] = new Debuggable[Bar] {
        def hashCode(v: Bar) = 24
        def equals(v: Bar, b: Any) = false
        def toString(v: Bar) = "boohoo"
      }
    }
    
    import Debuggable._
    def debug[T](v: T)(implicit ev: Debuggable[T]) = ???
    debug(new Foo)   // OK
    debug(new Bar)   // OK
    debug(new Qux)   // Error despite Qux <:< Foo
    
    

    【讨论】:

    • 老实说,这比我的方法要好得多。你不需要扩展任何东西,你可以为你没有的类型实现 Debugable;无权访问。我的回答只是简单的问题解决方案......这是正确的。
    • 这很棒。只是为了向阅读本文的任何人澄清这一点:与第一个解决方案不同,特征的方法覆盖对象的方法,这可能是也可能不是问题,具体取决于上下文
    【解决方案2】:

    在我看来,你应该把它抽象化。

    // Start writing your ScalaFiddle code here
    trait Debugable {
      def debugHashCode:Int
      def debugEquals(obj: Any): Boolean
      def debugToString: String
    
      override def hashCode(): Int = debugHashCode
      override def equals(obj: Any): Boolean = debugEquals(obj)
      override def toString: String = debugToString
    }
    //this will not compile
    class TestClass extends Debugable { }
    //this is OK but you need to implement 3 methods later :)
    abstract class TestClass2 extends Debugable {}
    

    https://scalafiddle.io/sf/bym3KFM/0

    宏应该是你尝试的最后一件事。

    【讨论】:

    • 这很接近(当然比我所拥有的有所改进),但它并没有完全符合我的要求。如果我有一个类的“链”,那么链的顶部就足以实现方法。
    【解决方案3】:

    基于此,我编写了以下内容,满足了我的需求并覆盖了默认实现:

    trait Debuggable_Helper[T]{
      def hashCode(v: T): Int
      def equals(v: T, b: Any): Boolean
      def toString(v: T): String
    
    }
    
    trait Debuggable[T] extends Debuggable_Helper [Debuggable [T]]{
      override def hashCode(): Int = hashCode(this)
      override def equals(b: Any): Boolean = equals(this, b)
      override def toString(): String = toString(this)
    
    }
    
    class Foo extends Debuggable[Foo]{
      def hashCode(v: Debuggable[Foo]) = 42
      def equals(v: Debuggable[Foo], b: Any) = true
      def toString(v: Debuggable[Foo]) = "woohoo"
    
    }
    class Qux extends Foo with Debuggable[Qux] //does not compile
    
    
    object Test{
      def main(args: Array[String]): Unit = {
        println(new Foo)   // OK - prints 'woohoo'
      }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 2023-03-13
      • 1970-01-01
      • 2012-11-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多