有没有办法隐式获取对象的实例
Is there a way to implicitly get instance of an object
我们想为任意 Enumerations
和 frameless 创建一个编码器,其中
基本上是创建从任意 Enumeration
到 Byte
的双向映射。目前我们不太理想的解决方案是为我们所有的枚举实例提供证据,以便反序列化器可以拾取它并在该实例上调用 apply
,一种从 [=13= 创建 Enumeration
的方法].我们想找到一种无需定义这些隐式值的方法,而是让它们自动从 E
类型中获取。据我们所知,对象类型与单个实例是一一对应的,所以我们希望有一种机制可以做到这一点。
例如下面的作品
import frameless._
object Color extends Enumeration {
type Color = Value
val Red, Green, Blue = Value
}
object State extends Enumeration {
type State = Value
val Running, Stopped, Finished = Value
}
implicit val colorEvidence = Color // we want to spare these lines
implicit val stateEvidence = State // we want to spare these lines
implicit def enumToByteInjection[E <: Enumeration](implicit e: E):
Injection[E#Value, Byte] = Injection(_.id.toByte, e.apply(_))
解法一(反思)
这里编译和 运行s 当用 scalac 2.12.4
编译时:
object Color extends Enumeration {
type Color = Value
val Red, Green, Blue = Value
}
object State extends Enumeration {
type State = Value
val Running, Stopped, Finished = Value
}
/** Dummy replacement with similar signature */
class Injection[A, B]()
import scala.reflect.runtime.universe.TypeTag
object ItDoesNotWorkInReplObjectsMustBeTopLevel {
implicit def enumToByteInjection[E <: Enumeration](implicit tt: TypeTag[E]): Injection[E#Value, Byte] = {
val ru = scala.reflect.runtime.universe
val classLoaderMirror = ru.runtimeMirror(getClass.getClassLoader)
val moduleSymbol = ru.typeOf[E].termSymbol.asModule
val moduleMirror = classLoaderMirror.reflectModule(moduleSymbol)
val companionObject = moduleMirror.instance.asInstanceOf[E]
println(s"/* 1 */ Materialize companion object $companionObject out of nothing!")
/* 2 */ ???
/* 3 */ // profit!
}
/** A function that requires implicit `Injection` */
def testNeedsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit =
println("replace ??? above to continue here")
def main(args: Array[String]): Unit = {
/** Test whether an implicit injection is constructed */
testNeedsInjection[Color.type] // compiles (crashes, as expected, but compiles)
}
}
它当然会因为 ???
处缺少实现而崩溃,但这是 在 隐式伴随对象被召唤到存在之后出现的。
陷阱:
- 当 运行 作为脚本
时不起作用
- 在 REPL 中不起作用(需要对象 top-level)
- 确保您使用的是正确的类加载器,提供类似
classOf[Unit].classLoader
的内容会导致 NoSuchClassException
s。
- 枚举需要
TypeTag
s(与 top-level 上的 具体 枚举一起使用时应该不是问题,但如果该方法应该深埋在库中,但仍然有 "access to the surface":那么您将不得不通过每个方法将 TypeTag
拉到表面)。
解决方案 2(隐式对象)
如果所有枚举都在您的控制之下,那么您可以简单地声明枚举对象本身 implicit
。以下编译得很好,所有隐式都按预期插入:
implicit object Color extends Enumeration {
type Color = Value
val Red, Green, Blue = Value
}
implicit object State extends Enumeration {
type State = Value
val Running, Stopped, Finished = Value
}
/** Dummy replacement with similar signature */
class Injection[A, B]()
implicit def enumToByteInjection[E <: Enumeration](implicit e: E): Injection[E#Value, Byte] = ???
/** A function that requires implicit `Injection` */
def needsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit = ???
/** Test whether an implicit injection is constructed */
needsInjection[Color.type] // compiles (crashes, as expected, but compiles)
解法三(无形魔法)
我们还可以使用 Shapeless Witness
es 为我们的枚举类型调用单例值。 Shapeless 使用编译时反射和代码生成为给定类型创建实例。
import shapeless._
implicit def enumToByteInjection[E <: Enumeration](implicit w: Witness.Aux[E]):
Injection[E#Value, Byte] = Injection(_.id.toByte, w.value.apply(_))
我们想为任意 Enumerations
和 frameless 创建一个编码器,其中
基本上是创建从任意 Enumeration
到 Byte
的双向映射。目前我们不太理想的解决方案是为我们所有的枚举实例提供证据,以便反序列化器可以拾取它并在该实例上调用 apply
,一种从 [=13= 创建 Enumeration
的方法].我们想找到一种无需定义这些隐式值的方法,而是让它们自动从 E
类型中获取。据我们所知,对象类型与单个实例是一一对应的,所以我们希望有一种机制可以做到这一点。
例如下面的作品
import frameless._
object Color extends Enumeration {
type Color = Value
val Red, Green, Blue = Value
}
object State extends Enumeration {
type State = Value
val Running, Stopped, Finished = Value
}
implicit val colorEvidence = Color // we want to spare these lines
implicit val stateEvidence = State // we want to spare these lines
implicit def enumToByteInjection[E <: Enumeration](implicit e: E):
Injection[E#Value, Byte] = Injection(_.id.toByte, e.apply(_))
解法一(反思)
这里编译和 运行s 当用 scalac 2.12.4
编译时:
object Color extends Enumeration {
type Color = Value
val Red, Green, Blue = Value
}
object State extends Enumeration {
type State = Value
val Running, Stopped, Finished = Value
}
/** Dummy replacement with similar signature */
class Injection[A, B]()
import scala.reflect.runtime.universe.TypeTag
object ItDoesNotWorkInReplObjectsMustBeTopLevel {
implicit def enumToByteInjection[E <: Enumeration](implicit tt: TypeTag[E]): Injection[E#Value, Byte] = {
val ru = scala.reflect.runtime.universe
val classLoaderMirror = ru.runtimeMirror(getClass.getClassLoader)
val moduleSymbol = ru.typeOf[E].termSymbol.asModule
val moduleMirror = classLoaderMirror.reflectModule(moduleSymbol)
val companionObject = moduleMirror.instance.asInstanceOf[E]
println(s"/* 1 */ Materialize companion object $companionObject out of nothing!")
/* 2 */ ???
/* 3 */ // profit!
}
/** A function that requires implicit `Injection` */
def testNeedsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit =
println("replace ??? above to continue here")
def main(args: Array[String]): Unit = {
/** Test whether an implicit injection is constructed */
testNeedsInjection[Color.type] // compiles (crashes, as expected, but compiles)
}
}
它当然会因为 ???
处缺少实现而崩溃,但这是 在 隐式伴随对象被召唤到存在之后出现的。
陷阱:
- 当 运行 作为脚本 时不起作用
- 在 REPL 中不起作用(需要对象 top-level)
- 确保您使用的是正确的类加载器,提供类似
classOf[Unit].classLoader
的内容会导致NoSuchClassException
s。 - 枚举需要
TypeTag
s(与 top-level 上的 具体 枚举一起使用时应该不是问题,但如果该方法应该深埋在库中,但仍然有 "access to the surface":那么您将不得不通过每个方法将TypeTag
拉到表面)。
解决方案 2(隐式对象)
如果所有枚举都在您的控制之下,那么您可以简单地声明枚举对象本身 implicit
。以下编译得很好,所有隐式都按预期插入:
implicit object Color extends Enumeration {
type Color = Value
val Red, Green, Blue = Value
}
implicit object State extends Enumeration {
type State = Value
val Running, Stopped, Finished = Value
}
/** Dummy replacement with similar signature */
class Injection[A, B]()
implicit def enumToByteInjection[E <: Enumeration](implicit e: E): Injection[E#Value, Byte] = ???
/** A function that requires implicit `Injection` */
def needsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit = ???
/** Test whether an implicit injection is constructed */
needsInjection[Color.type] // compiles (crashes, as expected, but compiles)
解法三(无形魔法)
我们还可以使用 Shapeless Witness
es 为我们的枚举类型调用单例值。 Shapeless 使用编译时反射和代码生成为给定类型创建实例。
import shapeless._
implicit def enumToByteInjection[E <: Enumeration](implicit w: Witness.Aux[E]):
Injection[E#Value, Byte] = Injection(_.id.toByte, w.value.apply(_))