代码生成之Scala宏编程 类Lombok工具的实现

本文介绍了Scala中的宏编程,包括宏的基本概念、使用场景和实现方式。通过例子展示了如何使用宏生成代码,如构建Builder模式和日志属性。此外,还提到了在IntelliJ IDEA中通过自定义插件使IDE识别宏生成的语法树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Scala 中的宏

  1. Scala 中的宏要复杂的多,但它对生成代码的方式,提供了更大的灵活性。
  2. Scala 宏仍然是用 Scala 写的,一定程序上保证了开发体验的一致。
  3. Scala 宏总是要返回一个AST,这需要你对 Scala AST 有一定的了解。
  4. 但 Quasiquotes 可以帮你轻松生成 AST。

如:

import scala.reflect.macros.blackbox
import scala.language.experimental.macros
object Hello {  
  def hello(msg:String): Unit = macro helloImpl
  def helloImpl(c: blackbox.Context)(msg: c.Expr[String]): c.Expr[Unit] = {
    import c.universe._
    c.Expr(q"""println("hello!")""")
  }
}

Scala 宏现状

  • 2.10.x
    • 需要引入外部编译器插件 macro paradise compiler plugin。
    • 需要引入org.scalamacros quasiquotes。
  • 2.11.x+
    • 引入scala-reflect即可。
    • 开始引入黑盒和白盒概念。
    • 宏实现直到2.13.7仍是实验性、不稳定的API。
  • Def macros 实现工具方法,类型转换,减少冗余代码等
  • Implicit macros 与隐式参数结合,在开源库中用的很多
  • Macro annotations 编译期反射
    • 2.13.x 需要scalac参数 -Ymacro-annotations
    • 2.11.x,2.12.x需要 macro paradise compiler plugin。
  • Scala3(dotty是Scala3编译器的名称)重新设计了宏API,不兼容Scala2。

注解宏

  • 同样是编译期改变代码行为,仅支持使用白盒宏。
  • 具有与Java Annotation Processor相似功能。
  • 与Scala注解也类似,继承StaticAnnotation特质。
  • 注解宏执行时依赖macro paradise compiler plugin。
  • 基本与使用Java注解一样。
    • 类上 @SerialVersionUID
    • 方法上 @tailrec @inline
    • 字段上 @transient @volatile @BeanProperty
    • 泛型上 @specialized
    • 其他 @unchecked

Scala中Lombok的可行性

字节码生成+Intellij支持
在这里插入图片描述
在这里custom-scala-plugin指的是自己编写的Scala-Macro-Tools插件。
在这里插入图片描述

编写自己的宏注解一 builder

步骤

  • 定义注解 限定使用场景:替普通类或样例类的主构造函数生成builder模式
  • 定义宏实现
    • 1.获取主构造函数中的必要内容
      • 类的类型名称
      • 是否样例类
      • 构造函数是否柯里化
      • 参数列表的语法树
        • 每个参数的名称
        • 每个参数的类型
        • 每个参数的默认值
        • 每个参数的修饰符
      • 类上的类型参数(泛型)和泛型边界限定符
    • 2.使用1获取的信息创建一个Builder类的语法树
      • 每个参数对应一个内部私有成员变量
      • 每个参数对应一个公共set方法
    • 3.使用1获取的信息创建一个builder方法的语法树,如有必要顺便创建一个该类对应的单例对象的语法树
    • 4.将builder方法和Builder类放入单例对象的定义中,返回

具体实现

/*
 * Copyright (c) 2021 jxnu-liguobin && contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package io.github.dreamylost.macros

import scala.reflect.macros.whitebox

/**
 *
 * @author 梦境迷离
 * @since 2021/7/7
 * @version 1.0
 */
object builderMacro {
   
   

  class BuilderProcessor(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
   
   

    import c.universe._

    private def getBuilderClassName(classTree: TypeName): TypeName = {
   
   
      TypeName(classTree.toTermName.decodedName.toString + "Builder")
    }

    private def getFieldDefinition(field: Tree): Tree = {
   
   
      val ValDef(_, name, tpt, rhs) = field
      q"private var $name: $tpt = $rhs"
    }

    private def getFieldSetMethod(typeName: TypeName, field: Tree, classTypeParams: List[Tree]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值