简体   繁体   English

需要从案例类的特征中引用同伴对象的特征

[英]Need to Reference Trait on Companion Object From Trait on Case Class

I need to access a companion class with a specified trait -- from a trait intended for case classes. 我需要从用于案例类的特征中访问具有指定特征的同伴类。 I am almost certain that the Scala reflection library can accomplish this but I haven't quite been able to piece it together. 我几乎可以肯定,Scala反射库可以完成此任务,但我还无法将其组合在一起。

I created test code below that requires one section of ??? 我在下面创建了需要???的测试代码? be filled in with some reflection magic. 充满一些反射魔法。 The code compiles and runs as is -- with a notification due to the missing functionality. 该代码按原样编译并运行-由于缺少功能而发出通知。

Some related answers that I have seen on StackOverflow were from 2.10. 我在StackOverflow上看到的一些相关答案来自2.10。 Scala 2.12 compatible please. 请兼容Scala 2.12。

import scala.reflect.{ClassTag, classTag}

//for companion object
//accesses Fields of the associated case class to ensure the correctness
//note: abstract class -- not a trait due to issues using ClassTag on a trait
abstract class SupportsField1Companion[T: ClassTag] {
  //gets the names of all Fields on the associated case class
  val fieldNamesOfInstancedClass: Array[String] =
    classTag[T].runtimeClass.getDeclaredFields.map(_.getName)

  //prints the name and fields of the associated case class -- plus extra on success
  def printFieldNames(extra: String = ""): Unit = {
    val name = classTag[T].runtimeClass.getCanonicalName
    val fields = fieldNamesOfInstancedClass.reduceLeft(_ + ", " + _)
    println(s"Fields of $name: $fields" + extra)
  }
}

//for case classes
//IMPORTANT -- please do not parameterize this if possible
trait SupportsField1 {
  //some data for printing
  val field1: String = this.getClass.getCanonicalName + ": field1"

  //should get a reference to the associated companion object as instance of SupportsFieldsCompanion
  def getSupportsFieldsCompanion: SupportsField1Companion[this.type] = //this.type may be wrong
    ??? //TODO reflection magic required -- need functionality to retrieve companion object cast as type

  //calls a function on the associated Companion class
  def callPrintFuncOnCompanion(): Unit =
    getSupportsFieldsCompanion.printFieldNames(s" -- from ${this.getClass.getCanonicalName}")
}

//two case classes with the SupportsFieldsCompanion trait to ensure data is accessed correctly
object ExampleA extends SupportsField1Companion[ExampleA] {}
case class ExampleA() extends SupportsField1 {
  val fieldA: String = "ExampleA: fieldA"
}
object ExampleB extends SupportsField1Companion[ExampleB] {}
case class ExampleB() extends SupportsField1 {
  val fieldB: String = "ExampleB: fieldB"
}

object Run extends App {
  //create instanced classes and print some test data
  val exampleA = ExampleA()
  println(exampleA.field1) //prints "ExampleA: field1" due to trait SupportsFields
  println(exampleA.fieldA) //prints "ExampleA: fieldA" due to being of class ExampleA
  val exampleB = ExampleB()
  println(exampleB.field1) //prints "ExampleB: field1" due to trait SupportsFields
  println(exampleB.fieldB) //prints "ExampleB: fieldB" due to being of class ExampleB

  //via the SupportsFieldsCompanion trait on the companion objects,
  //call a function on each companion object to show that each companion is associated with the correct case class
  ExampleA.printFieldNames() //prints "Fields of ExampleA: fieldA, field1"
  ExampleB.printFieldNames() //prints "Fields of ExampleB: fieldB, field1"

  //test access of printFieldNames on companion object from instanced class
  try {
    exampleA.callPrintFuncOnCompanion() //on success, prints "Fields of ExampleA: fieldA, field1 -- from ExampleA"
    exampleB.callPrintFuncOnCompanion() //on success, prints "Fields of ExampleB: fieldB, field1 -- from ExampleB"
  } catch {
    case _: NotImplementedError => println("!!! Calling function on companion(s) failed.")
  }
}

There are lots of ways you can do this, but the following is probably one of the simplest that doesn't involve making assumptions about how Scala's companion object class name mangling works: 您可以通过多种方法来执行此操作,但是以下可能是最简单的方法之一,其中不涉及对Scala的伴随对象类名称修饰的工作方式进行假设:

def getSupportsFieldsCompanion: SupportsField1Companion[this.type] =
  scala.reflect.runtime.ReflectionUtils.staticSingletonInstance(
    this.getClass.getClassLoader,
    this.getClass.getCanonicalName
  ).asInstanceOf[SupportsField1Companion[this.type]]

This works as desired, but I'd probably type it as SupportsField1Companion[_] , and ideally I'd probably avoid having public methods on SupportsField1 that refer to SupportsField1Companion —actually ideally I'd probably avoid this approach altogether, but if you're committed I think the ReflectionUtil solution above is probably reasonable. 这可以按需工作,但是我可能将其键入为SupportsField1Companion[_] ,理想情况下,我可能会避免在SupportsField1上使用引用了SupportsField1Companion公共方法- 理想情况下,我可能会完全避免使用此方法,但是如果您重新提交,我认为上面的ReflectionUtil解决方案可能是合理的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM