Scala: создание черты, которая заставляет классы повторно реализовывать определенные методы - PullRequest
0 голосов
/ 26 февраля 2020

Я пытаюсь реализовать черту, которая заставляет каждый класс, который ее расширяет (и не является абстрактным), реализовывать определенные методы (даже если они уже существуют в суперклассах). Конкретно это должно выглядеть так:

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)
  }
}

Приведенный выше код в идеале не должен компилироваться (поскольку отлаживаемый класс не реализует необходимые методы). На самом деле это не только компилирует, но и не выдает исключение времени выполнения (оно просто использует реализации класса объекта по умолчанию).

До сих пор ничего не удавалось создать ожидаемое поведение. Я думаю, что макросы могут помочь, но я не уверен, могут ли макросы express что-то вроде:

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

Я знаю, что мог бы позволить некоторому внешнему сценарию выполнить проверку и связать его с задачей компиляции gradle, но я надеюсь на решение, которое может быть реализовано как часть самого проекта (поскольку это сделало бы его независимым от используемого конвейера сборки, и поскольку оно должно быть проще и проще в обслуживании / расширении, чем написание сценария, сканирующего весь исходный код )

Ответы [ 3 ]

3 голосов
/ 26 февраля 2020

Это близко к этому (и, безусловно, улучшение по сравнению с тем, что у меня есть), но это не делает именно то, что я хотел. Если у меня есть «цепочка» классов, то достаточно, чтобы верх цепочки реализовывал методы.

Подход типа классов может помочь с этим, например,

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

3 голосов
/ 26 февраля 2020

На мой взгляд, вы должны просто сделать это абстрактным.

// 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

Макросы должны быть последними, что вы пытаетесь.

0 голосов
/ 26 февраля 2020

На основании этого я написал следующее, что удовлетворяет мои потребности и переопределяет реализации по умолчанию:

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'
  }
}

...