【问题标题】:Get companion object of class by given generic type Scala通过给定的泛型类型 Scala 获取类的伴随对象
【发布时间】:2012-02-28 16:12:40
【问题描述】:

我要做的是创建一个函数,该函数将采用泛型类并在其中使用静态方法(对不起,Java 语言,我的意思是它的伴随对象的方法)。

trait Worker {def doSth: Unit}

class Base

object Base extends Worker

// this actually wouldn't work, just to show what I'm trying to achieve
def callSthStatic[T that companion object is <: Worker](implicit m: Manifest[T]) {
  // here I want to call T.doSth (on T object)
  m.getMagicallyCompanionObject.doSth
}

有什么想法吗?

【问题讨论】:

    标签: class scala object types implicit


    【解决方案1】:

    A gist by Miles Sabin可能会给你一个提示:

    trait Companion[T] {
      type C
      def apply() : C
    }
    
    object Companion {
      implicit def companion[T](implicit comp : Companion[T]) = comp()
    }
    
    object TestCompanion {
      trait Foo
    
      object Foo {
        def bar = "wibble"
    
        // Per-companion boilerplate for access via implicit resolution
        implicit def companion = new Companion[Foo] {
          type C = Foo.type
          def apply() = Foo
        }
      }
    
      import Companion._
    
      val fc = companion[Foo]  // Type is Foo.type
      val s = fc.bar           // bar is accessible
    }
    

    如果使用 Scala 2.9.x,则应使用 -Ydependent-method-types 标志编译。

    【讨论】:

    • 我添加了实际的 Gist 内容 - Gist 可能会消失,并且只有链接才有资格作为评论。
    • 我在“implicit def partner[T] (隐式comp:Companion [T])= comp()”与scala 2.9.1。我做错了吗? :-)
    • 我会在括号中添加,如果类型 Foo 是可见的,那么它的伴生对象也是可见的,所以虽然这看起来很漂亮,但我认为它在实践中没有用。
    • 这是一个不错的技巧。我必须阅读一些有关依赖方法类型的信息,但这似乎有效!谢谢
    • 如果没有“每个同伴样板”部分,有什么方法可以让它工作?
    【解决方案2】:

    您可以使用反射来获取伴生类及其实例,但这依赖于 Scala 内部结构,这些内部结构可能会在遥远的(?)未来发生变化。当您获得 AnyRef 时,没有类型安全性。但是没有必要为你的类和对象添加任何隐式。

    def companionOf[T : Manifest] : Option[AnyRef] = try{
      val classOfT = implicitly[Manifest[T]].erasure
      val companionClassName = classOfT.getName + "$"
      val companionClass = Class.forName(companionClassName)
      val moduleField = companionClass.getField("MODULE$")
      Some(moduleField.get(null))
    } catch {
      case e => None
    }
    
    case class A(i : Int)
    
    companionOf[A].collect{case a : A.type  => a(1)}
    // res1: Option[A] = Some(A(1))
    

    【讨论】:

    • 谢谢,反射会解决问题...直到 scala 版本更新,因为伴生对象的名称不承诺以这种方式保留。
    • 嗯,没错。但我认为命名不会很快改变,因为这个约定至少从早期的 Scala 2.x 天开始就持续了。
    【解决方案3】:

    当我忘记如何执行此操作并且答案对我来说不是百分百满意时,我会一直点击此页面。以下是我如何处理反射:

    val thisClassCompanion = m.reflect(this).symbol.companion.asModule
    val structural = m.reflectModule(thisClassCompanion)
                      .instance.asInstanceOf[{def doSth: Unit}]
    

    您可能需要验证该类实际上有一个伴生对象或伴生对象。asModule 将抛出反射异常不是模块

    更新:为清楚起见添加了另一个示例:

        object CompanionUtil {
    
      import scala.reflect.runtime.{currentMirror => cm}
    
      def companionOf[T, CT](implicit tag: TypeTag[T]): CT = {
        Try[CT] {
          val companionModule = tag.tpe.typeSymbol.companion.asModule
          cm.reflectModule(companionModule).instance.asInstanceOf[CT]
        }
      }.getOrElse(throw new RuntimeException(s"Could not get companion object for type ${tag.tpe}"))
    
    }
    

    【讨论】:

    • 什么是 m?你能举一个完整的例子吗?我正在寻找可以像这样使用的东西:abstract class Whatever[T] = { val companion = companionOf[T] }
    • m 是当前镜像,我只是将它重命名为 import import scala.reflect.runtime.{currentMirror => m}
    • 请注意,CT 是伴随对象的类型,即T.type,例如companionOf[Foo, Foo.type] 其中Foo 是具有伴随对象的类的名称。
    • 根据答案,这里有一个获取指定类对象的伴生对象类型引用的方法:
      def companionOf[CT](clazz: Class[_]): CT = { import scala.reflect.runtime.{currentMirror =&gt; cm} Try[CT] { val companionModule = cm.classSymbol(clazz).companion.asModule cm.reflectModule(companionModule).instance.asInstanceOf[CT] }.getOrElse(throw new RuntimeException(s"Could not get companion object for $clazz")) }
    【解决方案4】:

    根据Miquel的回答,这里有一个获取指定类对象的伴生对象类型引用的方法:

      /**
        * Returns the companion object type reference for the specified class
        *
        * @param clazz The class whose companion is required
        * @tparam CT Type of the companion object
        * @return The type of the relevant companion object
        */
      def companionOf[CT](clazz: Class[_]): CT = {
        import scala.reflect.runtime.{currentMirror => cm}
        Try[CT] {
          val companionModule = cm.classSymbol(clazz).companion.asModule
          cm.reflectModule(companionModule).instance.asInstanceOf[CT]
        }.getOrElse(throw new RuntimeException(s"Could not get companion object for $clazz"))
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-08
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 2012-05-17
      • 2021-10-15
      • 1970-01-01
      相关资源
      最近更新 更多