未验证 提交 2df4ffb2 编写于 作者: 梦境迷离's avatar 梦境迷离 提交者: GitHub

opt (#201)

上级 08a99b34
...@@ -61,7 +61,9 @@ object Cache { ...@@ -61,7 +61,9 @@ object Cache {
override def getTField(key: String, field: CaseClassField)(implicit override def getTField(key: String, field: CaseClassField)(implicit
keyBuilder: CacheKeyBuilder[String] keyBuilder: CacheKeyBuilder[String]
): Future[Option[field.Field]] = ): Future[Option[field.Field]] =
getT(key).map(opt => opt.flatMap(t => CaseClassExtractor.getFieldValueUnSafely[cache.Out](t, field))) getT(key).map(opt =>
opt.flatMap(t => CaseClassExtractor.ofValue[cache.Out](t, field).asInstanceOf[Option[field.Field]])
)
override def clear(): Future[Unit] = cache.clear() override def clear(): Future[Unit] = cache.clear()
} }
...@@ -91,7 +93,7 @@ object Cache { ...@@ -91,7 +93,7 @@ object Cache {
override def getTField(key: String, field: CaseClassField)(implicit override def getTField(key: String, field: CaseClassField)(implicit
keyBuilder: CacheKeyBuilder[String] keyBuilder: CacheKeyBuilder[String]
): Identity[Option[field.Field]] = ): Identity[Option[field.Field]] =
getT(key).flatMap(t => CaseClassExtractor.getFieldValueUnSafely[cache.Out](t, field)) getT(key).flatMap(t => CaseClassExtractor.ofValue[cache.Out](t, field).asInstanceOf[Option[field.Field]])
override def clear(): Identity[Unit] = cache.clear() override def clear(): Identity[Unit] = cache.clear()
} }
......
...@@ -35,42 +35,25 @@ import scala.util.{ Failure, Success } ...@@ -35,42 +35,25 @@ import scala.util.{ Failure, Success }
*/ */
object CaseClassExtractor { object CaseClassExtractor {
def getFieldValueUnSafely[T: ru.TypeTag](obj: T, field: CaseClassField)(implicit /** Using the characteristics of the product type to get the field value should force the conversion externally
classTag: ClassTag[T] * (safely).
): Option[field.Field] = { */
val mirror = ru.runtimeMirror(getClass.getClassLoader) def ofValue[T <: Product](t: T, field: CaseClassField): Option[Any] = macro macroImpl[T]
val fieldOption = scala.util.Try(
getMethods[T]
.filter(_.name.toTermName.decodedName.toString == field.stringify)
.map(m => mirror.reflect(obj).reflectField(m).get)
.headOption
.map(_.asInstanceOf[field.Field])
)
fieldOption match {
case Success(value) => value
case Failure(exception) => exception.printStackTrace(); None
}
}
def getMethods[T: ru.TypeTag]: List[ru.MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
def getFieldValueSafely[T <: Product, Field](t: T, name: String): Option[Field] = macro macroImpl[T, Field]
def macroImpl[T: c.WeakTypeTag, Field: c.WeakTypeTag]( def macroImpl[T: c.WeakTypeTag](
c: whitebox.Context c: whitebox.Context
)(t: c.Expr[T], name: c.Expr[String]): c.Expr[Option[Field]] = { )(t: c.Expr[T], field: c.Expr[CaseClassField]): c.Expr[Option[Any]] = {
import c.universe._ import c.universe._
val typ = TypeName(c.weakTypeOf[Field].typeSymbol.name.decodedName.toString) // scalafmt: { maxColumn = 400 }
val tree = val tree =
q""" q"""
if ($t == null) None else { if ($t == null) None else {
val idx = $t.productElementNames.indexOf($name) val _field = $field
Option($t.productElement(idx).asInstanceOf[$typ]) _field.${TermName(CaseClassField.fieldNamesTermName)}.find(kv => kv._2 == _field.${TermName(CaseClassField.stringifyTermName)})
.map(kv => $t.productElement(kv._1))
} }
""" """
exprPrintTree[Option[Field]](c)(tree) exprPrintTree[Option[Any]](c)(tree)
} }
...@@ -83,4 +66,28 @@ object CaseClassExtractor { ...@@ -83,4 +66,28 @@ object CaseClassExtractor {
) )
c.Expr[Field](resTree) c.Expr[Field](resTree)
} }
/** Using scala reflect to get the field value (safely).
*/
@deprecated
def reflectValue[T: ru.TypeTag](obj: T, field: CaseClassField)(implicit
classTag: ClassTag[T]
): Option[field.Field] = {
val mirror = ru.runtimeMirror(getClass.getClassLoader)
val fieldOption = scala.util.Try(
getMethods[T]
.filter(_.name.toTermName.decodedName.toString == field.stringify)
.map(m => mirror.reflect(obj).reflectField(m).get)
.headOption
.map(_.asInstanceOf[field.Field])
)
fieldOption match {
case Success(value) => value
case Failure(exception) => exception.printStackTrace(); None
}
}
def getMethods[T: ru.TypeTag]: List[ru.MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
} }
...@@ -31,13 +31,18 @@ trait CaseClassField { ...@@ -31,13 +31,18 @@ trait CaseClassField {
def stringify: String def stringify: String
type Field type Field
/** product index -> name, scala2.11 2.12 not `productElementNames`
*/
val fieldIndexNames: Map[Int, String]
} }
object CaseClassField { object CaseClassField {
final val classNameTermName = "CaseClassField" final val classNameTermName = "CaseClassField"
final val stringifyTermName = "stringify" final val stringifyTermName = "stringify"
final val fieldTermName = "Field" final val fieldTermName = "Field"
final val fieldNamesTermName = "fieldIndexNames"
def apply[T <: Product](field: T => Any): CaseClassField = macro selectFieldMacroImpl[T] def apply[T <: Product](field: T => Any): CaseClassField = macro selectFieldMacroImpl[T]
...@@ -74,17 +79,20 @@ object CaseClassField { ...@@ -74,17 +79,20 @@ object CaseClassField {
} }
val fieldNameTypeName = TermName(s"${CaseClassField.classNameTermName}$$$fieldName") val fieldNameTypeName = TermName(s"${CaseClassField.classNameTermName}$$$fieldName")
val res = q""" val res =
q"""
case object $fieldNameTypeName extends $packageName.${TypeName(CaseClassField.classNameTermName)} { case object $fieldNameTypeName extends $packageName.${TypeName(CaseClassField.classNameTermName)} {
override def ${TermName(CaseClassField.stringifyTermName)}: String = $fieldName override def ${TermName(CaseClassField.stringifyTermName)}: String = $fieldName
override type ${TypeName(CaseClassField.fieldTermName)} = $genericType override type ${TypeName(CaseClassField.fieldTermName)} = $genericType
override val ${TermName(CaseClassField.fieldNamesTermName)} =
(${caseClassParams.indices.toList} zip ${caseClassParams.map(_.name.decodedName.toString)}).toMap
} }
$fieldNameTypeName $fieldNameTypeName
""" """
exprPrintTree[CaseClassField](c)(res) exprPrintTree[CaseClassField](c)(res)
} }
private def getCaseClassParams[T: c.WeakTypeTag](c: whitebox.Context): List[c.Symbol] = { def getCaseClassParams[T: c.WeakTypeTag](c: whitebox.Context): List[c.Symbol] = {
import c.universe._ import c.universe._
val parameters = c.weakTypeOf[T].resultType.member(TermName("<init>")).typeSignature.paramLists val parameters = c.weakTypeOf[T].resultType.member(TermName("<init>")).typeSignature.paramLists
if (parameters.size > 1) { if (parameters.size > 1) {
......
...@@ -31,10 +31,9 @@ import org.scalatest.matchers.should.Matchers ...@@ -31,10 +31,9 @@ import org.scalatest.matchers.should.Matchers
class CaseClassExtractorTest extends AnyFlatSpec with Matchers { class CaseClassExtractorTest extends AnyFlatSpec with Matchers {
"CaseClassExtractorTest1" should "safe" in { "CaseClassExtractorTest1" should "safe" in {
val obj = TestEntity("name", "id", "key", Some(1)) val obj = TestEntity("name", "id", "key", Some(1))
val keyField = CaseClassField[TestEntity](_.key) val key = CaseClassExtractor.ofValue(obj, CaseClassField[TestEntity](_.key))
val key = println(key)
CaseClassExtractor.getFieldValueSafely[TestEntity, keyField.Field](obj, keyField.stringify)
assert(key == Option("key")) assert(key == Option("key"))
} }
...@@ -47,9 +46,8 @@ class CaseClassExtractorTest extends AnyFlatSpec with Matchers { ...@@ -47,9 +46,8 @@ class CaseClassExtractorTest extends AnyFlatSpec with Matchers {
} }
"CaseClassExtractorTest3" should "unsafe" in { "CaseClassExtractorTest3" should "unsafe" in {
val obj = TestEntity("name", "id", "key", Some(1)) val obj = TestEntity("name", "id", "key", Some(1))
val key: Option[CaseClassField#Field] = val key: Option[CaseClassField#Field] = CaseClassExtractor.reflectValue(obj, CaseClassField[TestEntity](_.key))
CaseClassExtractor.getFieldValueUnSafely(obj, CaseClassField[TestEntity](_.key))
assert(key == Option("key")) assert(key == Option("key"))
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册