如何有效/干净地覆盖复制方法 [英] how to efficiently/cleanly override a copy method

查看:59
本文介绍了如何有效/干净地覆盖复制方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个超类和一个子类,如下所示:

I have a super and a subclass as follows:

class Animal(var x: Int) {
   def greeting: String = "hi im an animal"
   def copy: Animal = new Animal(x)
}


class Lion(override var x: Int) extends Animal(x){
  override def greeting: String = "hi im a lion"
  override def copy: Lion = new Lion(x)
}

我希望它们都具有完全相同的复制功能(想象它比我给出的要大),除了返回类型之外,我希望 Lion 类在调用复制时返回一个 Lion.

I want both of them to have the exact same copy function (imagine it being larger than what I've given), except for the return type, I would like the Lion class to return a Lion when copy is invoked.

如何在不重复代码的情况下干净地覆盖 Animal 复制方法?

How can I cleanly override the Animal copy method without having code duplication?

推荐答案

原则上,方法apply/unapplycanEqual/equals/hashCodetoStringcopyproductArity/productElement/productIterator/productPrefix 可以使用 Shapeless case classes a la carte 虽然我不确定这是否适用于类层次结构.

In principle, methods apply/unapply, canEqual/equals/hashCode, toString, copy, productArity/productElement/productIterator/productPrefix can be generated with Shapeless case classes a la carte although I'm not sure whether this works with class hierarchies.

无论如何,您可以使用生成apply宏注释

Anyway, you can generate apply with a macro annotation

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

@compileTimeOnly("enable macro annotations")
class copy extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro CopyMacro.impl
}

object CopyMacro {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    annottees match {
      case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
        val paramNamess = paramss.map(_.map {
          case q"$_ val $tname: $_ = $_" => tname
          case q"$_ var $tname: $_ = $_" => tname
        })

        val tparamNames = tparams.map {
          case q"$_ type $tpname[..$_] = $_" => tpname
        }

        val doesOverrideCopy = parents.map {
          case q"${parent@tq"$_[..$_]"}(...$_)" => parent
          case     parent@tq"$_[..$_]"          => parent
        }.exists(tree => 
          c.typecheck(tree.duplicate, mode = c.TYPEmode)
            .tpe
            .member(TermName("copy")) != NoSymbol
        )

        val copyMod = if (doesOverrideCopy) Modifiers(Flag.OVERRIDE) else NoMods

        q"""
           $mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
             ..$stats
             $copyMod def copy: $tpname[..$tparamNames] = new $tpname[..$tparamNames](...$paramNamess)
           }
           ..$tail
        """
    }
  }
}

用法:

@copy
class Animal(val x: Int) {
  def greeting: String = "hi im an animal"
}

@copy
class Lion(override val x: Int) extends Animal(x) {
  override def greeting: String = "hi im a lion"
}

//scalac: {
//  class Animal extends scala.AnyRef {
//    <paramaccessor> val x: Int = _;
//    def <init>(x: Int) = {
//      super.<init>();
//      ()
//    };
//    def greeting: String = "hi im an animal";
//    def copy: Animal = new Animal(x)
//  };
//  ()
//}
//scalac: {
//  class Lion extends Animal(x) {
//    override <paramaccessor> val x: Int = _;
//    def <init>(x: Int) = {
//      super.<init>();
//      ()
//    };
//    override def greeting: String = "hi im a lion";
//    override def copy: Lion = new Lion(x)
//  };
//  ()
//}

或者,由于 class Animal(val x: Int)case-class-like 你可以尝试使用 shapeless.Generic

Alternatively, since class Animal(val x: Int) is case-class-like you can try to use shapeless.Generic

implicit class CopyOps[A](a: A)(implicit generic: Generic[A]) {
  def copy: A = generic.from(generic.to(a))
}

这篇关于如何有效/干净地覆盖复制方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆