【问题标题】:How to define a type for a generic type如何为泛型类型定义类型
【发布时间】:2020-10-15 03:15:30
【问题描述】:

假设我有以下代码:

trait Trait[T <: Trait[T]] {
  def merge(t: T): T
}

case class A[T <: Trait[T]](t: T, i: Int)
case class B[T <: Trait[T]](t: T, str: String)

有没有一种方法可以定义一种类型来缩写我对 A 类和 B 类的定义?

比如:

type T2 = _ <: Trait[T2] // ???
case class A[T2](t: T2, i: Int)
case class B[T2](t: T2, str: String)

【问题讨论】:

    标签: scala generics f-bounded-polymorphism


    【解决方案1】:

    实际上,你不想要一个类型的别名,你想要一个绑定的别名。

    请看How to avoid duplication of type bound in Scala

    简而言之,您应该在任何需要的地方保留 F-bounds。 实际上,这不是代码重复。 TraitAB的类型参数T实际上是三个不同的类型参数,可以有不同的界限。

    但理论上你可以用macro annotation 来缩写边界,虽然这不值得而且通常会很危险,因为这可能会让你的队友感到惊讶,会使调试变得更加复杂,并且会混淆你的 IDE

    import scala.annotation.{StaticAnnotation, compileTimeOnly}
    import scala.language.experimental.macros
    import scala.reflect.macros.blackbox
    
    @compileTimeOnly("enable macro annotations")
    class fbound extends StaticAnnotation {
      def macroTransform(annottees: Any*): Any = macro FBoundMacro.impl
    }
    
    object FBoundMacro {
      def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
        import c.universe._
    
        def addFBound(tparam: Tree): Tree = tparam match {
          case q"$mods type $name[..$tparams] >: $low <: $high" =>
            val tparamsNames = tparams.map {
              case q"$_ type $nme[..$_] >: $_ <: $_" => nme
            }
            val fBound = tq"Trait[$name[..$tparamsNames]]"
            val high1 = high match {
              case tq"$EmptyTree" => fBound
              case tq"..$withTypes { ..$refinements }" =>
                val withTypes1 = withTypes :+ fBound
                tq"..$withTypes1 { ..$refinements }"
              case tq"$typ" => tq"$typ with $fBound"
            }
            q"$mods type $name[..$tparams] >: $low <: $high1"
        }
    
        annottees match {
          case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
            val tparams1 = addFBound(tparams.head) :: tparams.tail
            q"""
              $mods class $tpname[..$tparams1] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }
              ..$tail
            """
        }
      }
    }
    

    用法:

    trait Trait[T <: Trait[T]] {
      def merge(t: T): T
    }
    
    @fbound case class A[T](t: T, i: Int)
    @fbound case class B[T](t: T, str: String)
    
    //scalac: {
    //  case class A[T <: Trait[T]] extends scala.Product with scala.Serializable {
    //    <caseaccessor> <paramaccessor> val t: T = _;
    //    <caseaccessor> <paramaccessor> val i: Int = _;
    //    def <init>(t: T, i: Int) = {
    //      super.<init>();
    //      ()
    //    }
    //  };
    //  ()
    //}
    //scalac: {
    //  case class B[T <: Trait[T]] extends scala.Product with scala.Serializable {
    //    <caseaccessor> <paramaccessor> val t: T = _;
    //    <caseaccessor> <paramaccessor> val str: String = _;
    //    def <init>(t: T, str: String) = {
    //      super.<init>();
    //      ()
    //    }
    //  };
    //  ()
    //}
    

    【讨论】:

      猜你喜欢
      • 2019-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-26
      • 2019-10-02
      • 1970-01-01
      相关资源
      最近更新 更多