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

Delete docs directory (#102)

* Delete docs directory
* Update README.md
* Update README_CN.md
上级 47ea7862
......@@ -36,7 +36,9 @@ Learn Scala macro and abstract syntax tree.
> The intellij plugin named `Scala-Macro-Tools` in marketplace.
**[Description of each annotation](./docs/English)**
# Document
[https://bitlap.org/lab/smt](https://bitlap.org/lab/smt)
# How to use
......@@ -61,4 +63,4 @@ Where `<your-scala-version>` must be the full scala version. For example 2.12.13
If that doesn't work, google for alternatives.
In version scala`2.13.x`, the functionality of macro paradise has been included in the scala compiler directly. However,
you must still enable the compiler flag `-Ymacro-annotations`.
\ No newline at end of file
you must still enable the compiler flag `-Ymacro-annotations`.
......@@ -16,21 +16,6 @@
**[中文说明](./README_CN.md) | [English](./README.md)**
# 我能学到什么
- Scala2 宏编程
- 了解Scala反射
- 熟悉插值语法的应用,了解Scala Spec
- 了解Scala AST相关的基本使用
- 知道如何编写宏注解(宏拓展)
- 知道如何使用宏生成对象,类,方法,字段
- 其他
- 知道劝退人的SBT如何优雅配置,诸如发布到云仓库,多版本构建,多模块项目
- 了解如何利用Intellij-Scala插件编写自己的Scala插件
- 了解Intellij是怎样支持语法提示的,插件工作流程
- 知道如何写好scalatest单元测试
- 类似的其他思考
# 环境
- 使用 Java 8, 11 编译通过
......@@ -51,7 +36,9 @@
> Intellij插件 `Scala-Macro-Tools`。
[各个注解的说明](./docs/中文简体)
# 文档
[https://bitlap.org/zh-CN/lab/smt](https://bitlap.org/zh-CN/lab/smt)
# 如何使用
......@@ -77,4 +64,4 @@ addCompilerPlugin("org.scalamacros" % "paradise_<your-scala-version>" % "<plugin
如果这不起作用,可以谷歌寻找替代品。
`scala 2.13.x`版本中,macro paradise的功能直接包含在scala编译器中。然而,仍然必须启用编译器标志`-Ymacro annotations`
\ No newline at end of file
`scala 2.13.x`版本中,macro paradise的功能直接包含在scala编译器中。然而,仍然必须启用编译器标志`-Ymacro annotations`
## @apply
The `@apply` annotation is used to generate `apply` method for primary construction of ordinary classes.
**Note**
- `verbose` Whether to enable detailed log.
- Only support `class`.
- Only support **primary construction**.
**Example**
```scala
@apply @toString class B2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L))
println(B2(1, 2))
```
\ No newline at end of file
## @builder
The `@builder` annotation is used to generate builder pattern for Scala classes.
**Note**
- Support `case class` / `class`.
- Only support for **primary constructor**.
- If there is no companion object, one will be generated to store the `builder` class and method.
**Example**
```scala
@builder
case class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
val ret = TestClass1.builder().i(1).j(0).x("x").build()
assert(ret.toString == "TestClass1(1,0,x,Some())")
```
**Macro expansion code**
```scala
object TestClass1 extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def builder(): TestClass1Builder = new TestClass1Builder();
class TestClass1Builder extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
private var i: Int = 0;
private var j: Int = _;
private var x: String = _;
private var o: Option[String] = Some("");
def i(i: Int): TestClass1Builder = {
this.i = i;
this
};
def j(j: Int): TestClass1Builder = {
this.j = j;
this
};
def x(x: String): TestClass1Builder = {
this.x = x;
this
};
def o(o: Option[String]): TestClass1Builder = {
this.o = o;
this
};
def build(): TestClass1 = TestClass1(i, j, x, o)
}
}
```
\ No newline at end of file
## @constructor
The `@constructor` annotation is used to generate secondary constructor method for classes, only when it has internal fields.
**Note**
- `verbose` Whether to enable detailed log.
- `excludeFields` Whether to exclude the specified `var` fields, default is `Nil`.
- Only support `class`.
- The internal fields are placed in the first bracket block if constructor is currying.
- The type of the internal field must be specified, otherwise the macro extension cannot get the type.
At present, only primitive types and string can be omitted. For example, `var i = 1; var j: int = 1; var k: Object = new Object()` is OK, but `var k = new object()` is not.
**Example**
```scala
@constructor(excludeFields = Seq("c"))
class A2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L)) {
private val a: Int = 1
var b: Int = 1 //The default value of the field is not carried to the apply parameter, so all parameters are required.
protected var c: Int = _
def helloWorld: String = "hello world"
}
println(new A2(1, 2, None, None, 100))
```
**Macro expansion code**
Only constructor
```scala
def <init>(int: Int, j: Int, k: Option[String], t: Option[Long], b: Int) = {
<init>(int, j, k, t);
this.b = b
}
```
\ No newline at end of file
## @elapsed
The `@elapsed` annotation is used to calculate the execution time of the method.
**Note**
- `limit` The log will be printed or output to the console if the execution time exceeds this value.
- If there is an `org.slf4j.Logger` object of `slf4j` in the owner scope of the method, this object is used; otherwise, `println` is used.
- `logLevel` Specifies the log level to print.
- The return type of supported method is not `Future[_]`.
- Use `map` to implement.
- The return type of the supported method is not `Future`.
- Use `try finally` to implement.
- Annotation is only supported use on non-abstract method.
**Example**
```scala
class A {
// Duration and TimeUnit must Full class name
@elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = io.github.dreamylost.LogLevel.WARN)
def helloScala1(t: String): Future[String] = {
Future(t)(scala.concurrent.ExecutionContext.Implicits.global)
}
@elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = io.github.dreamylost.LogLevel.INFO)
def helloScala2: String = Await.result(helloScala1("world"), Duration.Inf)
}
```
\ No newline at end of file
## @equalsAndHashCode
The `@equalsAndHashCode` annotation is used to generate `equals` and `hashCode` methods for ordinary classes, and them takes into account the influence of super classes.
**Note**
- `verbose` Whether to enable detailed log.
- `excludeFields` specifies whether to exclude fields that are not required for the `equals` and `hashCode` methods. Optional,
default is `Nil` (all `var` and `val` fields **exclude `protected [this]` and `private [this]`** in the class will be used to generate the two methods).
- Both `equals` and `hashCode` methods are affected by super classes, and `canEqual` uses `isInstanceOf` in `equals` method.
Some equals implementations use `that.getClass == this.getClass`
- It uses simple hashcode algorithm, and the hashcode of the parent class are accumulated directly. The algorithm is also used by `case class`.
- If the class of the annotation has already defined the `canEqual` method with the same signature, `canEqual` will not be generated.
- Include the internal fields defined within a class, which named internal fields or member fields here.
**Example**
```scala
@equalsAndHashCode(verbose = true)
class Person(var name: String, var age: Int)
```
**Macro expansion code**
```scala
class Person extends scala.AnyRef {
<paramaccessor> var name: String = _;
<paramaccessor> var age: Int = _;
def <init>(name: String, age: Int) = {
super.<init>();
()
};
def canEqual(that: Any) = that.isInstanceOf[Person];
override def equals(that: Any): Boolean = that match {
case (t @ (_: Person)) => t.canEqual(this).$amp$amp(Seq(this.name.equals(t.name), this.age.equals(t.age)).forall(((f) => f))).$amp$amp(true)
case _ => false
};
override def hashCode(): Int = {
val state = Seq(name, age);
state.map(((x$2) => x$2.hashCode())).foldLeft(0)(((a, b) => 31.$times(a).$plus(b)))
}
}
```
\ No newline at end of file
## @jacksonEnum
The `@jacksonEnum` annotation is used to provide `Jackson` serialization support for all Scala enumeration type parameters in the primary constructor of the class. (jackson and jackson-scala-module dependency needs to be introduced)
**Note**
- `verbose` Whether to enable detailed log. default is `false`.
- `nonTypeRefers` Specifies the enumeration type of the `TypeReference` subclass of `Jackson` that does not need to be created. default is `Nil`.
- Support `case class` and `class`.
- If the enumeration type has subclasses of `TypeReference`, no new subclasses will be generated,
and `JsonScalaEnumeration` annotation will not be added to the parameters repeatedly. This is mainly used to solve conflict problems.
**Example**
```scala
@jacksonEnum(nonTypeRefers = Seq("EnumType"))
class B(
var enum1: EnumType.EnumType,
enum2: EnumType2.EnumType2 = EnumType2.A,
i: Int)
```
**Macro expansion code**
```scala
class EnumType2TypeRefer extends _root_.com.fasterxml.jackson.core.`type`.TypeReference[EnumType2.type] {
def <init>() = {
super.<init>();
()
}
};
class B extends scala.AnyRef {
<paramaccessor> var enum1: JacksonEnumTest.this.EnumType.EnumType = _;
@new com.fasterxml.jackson.module.scala.JsonScalaEnumeration(classOf[EnumType2TypeRefer]) <paramaccessor> private[this] val enum2: JacksonEnumTest.this.EnumType2.EnumType2 = _;
<paramaccessor> private[this] val i: Int = _;
def <init>(enum1: JacksonEnumTest.this.EnumType.EnumType, @new com.fasterxml.jackson.module.scala.JsonScalaEnumeration(classOf[EnumType2TypeRefer]) enum2: JacksonEnumTest.this.EnumType2.EnumType2 = EnumType2.A, i: Int) = {
super.<init>();
()
}
};
()
```
\ No newline at end of file
## @json
The `@json` annotation is the quickest way to add a JSON format to your Play project's case classes.
**Note**
- This annotation is drawn from [json-annotation](https://github.com/kifi/json-annotation) and have some
optimization.
- It can also be used when there are other annotations on the case classes.
- Only an implicit `val` was generated automatically(Maybe generate a companion object if it not exists), and there are no other
operations.
**Example**
```scala
@json case class Person(name: String, age: Int)
```
You can now serialize/deserialize your objects using Play's convenience methods:
```scala
import play.api.libs.json._
val person = Person("Victor Hugo", 46)
val json = Json.toJson(person)
Json.fromJson[Person](json)
```
\ No newline at end of file
## @log
The `@log` annotation does not use mixed or wrapper, but directly uses macro to generate default log object and operate log. (Log dependency needs to be introduced)
**Note**
- `verbose` Whether to enable detailed log.
- `logType` Specifies the type of `log` that needs to be generated, default is `io.github.dreamylost.logs.LogType.JLog`.
- `io.github.dreamylost.logs.LogType.JLog` use `java.util.logging.Logger`
- `io.github.dreamylost.logs.LogType.Log4j2` use `org.apache.logging.log4j.Logger`
- `io.github.dreamylost.logs.LogType.Slf4j` use `org.slf4j.Logger`
- `io.github.dreamylost.logs.LogType.ScalaLoggingLazy` implement by `scalalogging.LazyLogging` but field was renamed to `log`
- `io.github.dreamylost.logs.LogType.ScalaLoggingStrict` implement by `scalalogging.StrictLogging` but field was renamed to `log`
- Support `class` and `object`.
**Example**
```scala
@log(verbose = true) class TestClass1(val i: Int = 0, var j: Int) {
log.info("hello")
}
@log(verbose=true, logType=io.github.dreamylost.logs.LogType.Slf4j)
class TestClass6(val i: Int = 0, var j: Int) {
log.info("hello world")
}
```
\ No newline at end of file
## @synchronized
The `@synchronized` annotation is a more convenient and flexible synchronous annotation.
**Note**
- `lockedName` The name of the custom lock obj, default is `this`.
- Support static and instance methods.
**Example**
```scala
private final val obj = new Object
@synchronized(lockedName = "obj") // The default is this. If you fill in a non existent field name, the compilation will fail.
def getStr3(k: Int): String = {
k + ""
}
// or
@synchronized //use this
def getStr(k: Int): String = {
k + ""
}
```
**Macro expansion code**
```scala
// Note that it will not judge whether synchronized already exists, so if synchronized already exists, it will be used twice.
// For example `def getStr(k: Int): String = this.synchronized(this.synchronized(k.$plus("")))
// It is not sure whether it will be optimized at the bytecode level.
def getStr(k: Int): String = this.synchronized(k.$plus(""))
```
\ No newline at end of file
## @toString
The `@toString` annotation is used to generate `toString` for Scala classes or a `toString` with parameter names for the case classes.
**Note**
- `verbose` Whether to enable detailed log.
- `includeFieldNames` Whether to include the names of the field in the `toString`, default is `true`.
- `includeInternalFields` Whether to include the internal fields defined within a class. Not in a primary constructor, default is `true`.
- `callSuper` Whether to include the super's `toString`, default is `false`. Not support if super class is a trait.
- Support `case class` and `class`.
**Example**
```scala
@toString class TestClass(val i: Int = 0, var j: Int) {
val y: Int = 0
var z: String = "hello"
var x: String = "world"
}
println(new TestClass(1, 2));
```
Detail options
| includeInternalFields / includeFieldNames | false | true |
| ----------------------------------------- | -------------------------------------- | ------------------------------------------------ |
| false | ```TestClass(1, 2)``` | ```TestClass(i=0, j=2)``` |
| true | ```TestClass(1, 2, 0, hello, world)``` | ```TestClass(i=1, j=2, y=0, z=hello, x=world)``` |
## @apply
`@apply`注解用于为普通类的主构造函数生成`apply`方法。
**说明**
- `verbose` 指定是否开启详细编译日志。可选,默认`false`
- 仅支持在`class`上使用且仅支持主构造函数。
**示例**
```scala
@apply @toString class B2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L))
println(B2(1, 2, None, None)) //0.1.0,不携带字段的默认值到apply参数中,所以参数都是必传
```
\ No newline at end of file
## @builder
`@builder`注解用于为Scala类生成构造器模式。
**说明**
- 支持普通类和样例类。
- 仅支持对主构造函数使用。
- 如果该类没有伴生对象,将生成一个伴生对象来存储`builder`方法和类。
**示例**
```scala
@builder
case class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
val ret = TestClass1.builder().i(1).j(0).x("x").build()
assert(ret.toString == "TestClass1(1,0,x,Some())")
```
**宏生成的中间代码**
```scala
object TestClass1 extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def builder(): TestClass1Builder = new TestClass1Builder();
class TestClass1Builder extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
private var i: Int = 0;
private var j: Int = _;
private var x: String = _;
private var o: Option[String] = Some("");
def i(i: Int): TestClass1Builder = {
this.i = i;
this
};
def j(j: Int): TestClass1Builder = {
this.j = j;
this
};
def x(x: String): TestClass1Builder = {
this.x = x;
this
};
def o(o: Option[String]): TestClass1Builder = {
this.o = o;
this
};
def build(): TestClass1 = TestClass1(i, j, x, o)
}
}
```
\ No newline at end of file
## @constructor
`@constructor`注解用于为普通类生成辅助构造函数。仅当类有内部字段时可用。
**说明**
- `verbose` 指定是否开启详细编译日志。可选,默认`false`
- `excludeFields` 指定是否需要排除不需要用于构造函数的`var`字段。可选,默认空(所有class内部的`var`字段都将作为构造函数的入参)。
- 仅支持在`class`上使用。
- 主构造函数存在柯里化时,内部字段被放置在柯里化的第一个括号块中。(生成的仍然是柯里化的辅助构造)
- 内部字段的类型需要显示指定,否则宏拓展无法获取到该类型。目前支持为基本类型和字符串实现省略。如`var i = 1; var j: Int = 1; var k: Object = new Object()`都是可以的,而`var k = new Object()`是不可以的。
**示例**
```scala
@constructor(excludeFields = Seq("c")) //排除c字段。其中,a是val的不需要手动指定,自动排除。
class A2(int: Int, val j: Int, var k: Option[String] = None, t: Option[Long] = Some(1L)) {
private val a: Int = 1
var b: Int = 1 // 不携带字段的默认值到apply参数中,所以参数都是必传
protected var c: Int = _
def helloWorld: String = "hello world"
}
println(new A2(1, 2, None, None, 100))
```
**宏生成的中间代码**
仅构造函数部分
```scala
def <init>(int: Int, j: Int, k: Option[String], t: Option[Long], b: Int) = {
<init>(int, j, k, t);
this.b = b
}
```
\ No newline at end of file
## @elapsed
`@elapsed`注解用于计算方法的执行耗时
**说明**
- `limit` 执行耗时超过该值则打印日志或输出到控制台。
- 方法的所有者作用域内有`slf4j``org.slf4j.Logger`对象,则使用该对象,否则使用`println`
- `logLevel` 指定打印的日志级别。
- 支持方法的返回类型为`Future[_]`
- 使用`map`实现。
- 支持方法的返回类型的不是`Future`
- 使用`try finally`实现。
- 仅能在非抽象方法上使用该注解。
**示例**
```scala
class A {
// Duration和TimeUnit必须是全类名
@elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = io.github.dreamylost.LogLevel.WARN)
def helloScala1(t: String): Future[String] = {
Future(t)(scala.concurrent.ExecutionContext.Implicits.global)
}
@elapsed(limit = scala.concurrent.duration.Duration(1, java.util.concurrent.TimeUnit.SECONDS), logLevel = io.github.dreamylost.LogLevel.INFO)
def helloScala2: String = Await.result(helloScala1("world"), Duration.Inf)
}
```
\ No newline at end of file
## @equalsAndHashCode
`@equalsAndHashCode`注解用于为普通类生成`equals``hashCode`方法,同时均考虑超类的影响。
**说明**
- `verbose` 指定是否开启详细编译日志。可选,默认`false`
- `excludeFields` 指定是否需要排除不需要用于`equals``hashCode`方法的字段。可选,默认空(class内部所有非`protected [this]``private [this]``var、val`字段都将被应用于生成这两个方法)。
- `equals``hashCode`方法均会被超类影响,`canEqual`使用`isInstanceOf`,有些人在实现时,使用的是`this.getClass == that.getClass`
- 采用简单hashCode算法,父类的hashCode是直接被累加的。该算法也是`case class`所使用的。
- 如果注解所在类已经定义了相同签名的`canEqual`方法,则不会生成该方法。
- 包括在类内部中定义的成员字段,在本库中称为内部字段。
**示例**
```scala
@equalsAndHashCode(verbose = true)
class Person(var name: String, var age: Int)
```
**宏生成的中间代码**
```scala
class Person extends scala.AnyRef {
<paramaccessor> var name: String = _;
<paramaccessor> var age: Int = _;
def <init>(name: String, age: Int) = {
super.<init>();
()
};
def canEqual(that: Any) = that.isInstanceOf[Person];
override def equals(that: Any): Boolean = that match {
case (t @ (_: Person)) => t.canEqual(this).$amp$amp(Seq(this.name.equals(t.name), this.age.equals(t.age)).forall(((f) => f))).$amp$amp(true)
case _ => false
};
override def hashCode(): Int = {
val state = Seq(name, age);
state.map(((x$2) => x$2.hashCode())).foldLeft(0)(((a, b) => 31.$times(a).$plus(b)))
}
}
```
\ No newline at end of file
## @jacksonEnum
`@jacksonEnum`注解用于为类的主构造函数中的所有Scala枚举类型的参数提供`Jackson`序列化的支持。(jackson和jackson-scala-module依赖需要自己引入)
**说明**
- `verbose` 指定是否开启详细编译日志。可选,默认`false`
- `nonTypeRefers` 指定不需要创建`Jackson``TypeReference`子类的枚举类型。可选,默认`Nil`
- 支持`case class``class`
- 如果枚举类型存在`TypeReference`的子类,则不会生成新的子类,也不会重复添加`@JsonScalaEnumeration`注解到参数上。这主要用于解决冲突问题。
**示例**
```scala
@jacksonEnum(nonTypeRefers = Seq("EnumType"))
class B(
var enum1: EnumType.EnumType,
enum2: EnumType2.EnumType2 = EnumType2.A,
i: Int)
```
**宏生成的中间代码**
```scala
class EnumType2TypeRefer extends _root_.com.fasterxml.jackson.core.`type`.TypeReference[EnumType2.type] {
def <init>() = {
super.<init>();
()
}
};
class B extends scala.AnyRef {
<paramaccessor> var enum1: JacksonEnumTest.this.EnumType.EnumType = _;
@new com.fasterxml.jackson.module.scala.JsonScalaEnumeration(classOf[EnumType2TypeRefer]) <paramaccessor> private[this] val enum2: JacksonEnumTest.this.EnumType2.EnumType2 = _;
<paramaccessor> private[this] val i: Int = _;
def <init>(enum1: JacksonEnumTest.this.EnumType.EnumType, @new com.fasterxml.jackson.module.scala.JsonScalaEnumeration(classOf[EnumType2TypeRefer]) enum2: JacksonEnumTest.this.EnumType2.EnumType2 = EnumType2.A, i: Int) = {
super.<init>();
()
}
};
()
```
\ No newline at end of file
## @json
`@json`注解是向Play项目的样例类添加json format对象的最快方法。
**说明**
- 此注释启发来自[json-annotation](https://github.com/kifi/json-annotation),并做了优化,现在它可以与其他注解同时使用。
- 只有一个隐式的`val`值会被自动生成(如果伴生对象不存在的话,还会生成一个伴生对象用于存放该隐式值),此外没有其他的操作。
**示例**
```scala
@json case class Person(name: String, age: Int)
```
现在,您可以使用Play的转化方法序列化或反序列化对象:
```scala
import play.api.libs.json._
val person = Person("Victor Hugo", 46)
val json = Json.toJson(person)
Json.fromJson[Person](json)
```
\ No newline at end of file
## @log
`@log`注解不使用混入和包装,而是直接使用宏生成默认的log对象来操作log。日志库的依赖需要自己引入。
**说明**
- `verbose` 指定是否开启详细编译日志。可选,默认`false`
- `logType` 指定需要生成的`log`的类型。可选,默认`io.github.dreamylost.logs.LogType.JLog`
- `io.github.dreamylost.logs.LogType.JLog` 使用 `java.util.logging.Logger`
- `io.github.dreamylost.logs.LogType.Log4j2` 使用 `org.apache.logging.log4j.Logger`
- `io.github.dreamylost.logs.LogType.Slf4j` 使用 `org.slf4j.Logger`
- `io.github.dreamylost.logs.LogType.ScalaLoggingLazy` 基于 `scalalogging.LazyLogging` 实现,但字段被重命名为`log`
- `io.github.dreamylost.logs.LogType.ScalaLoggingStrict` 基于 `scalalogging.StrictLogging`实现, 但字段被重命名为`log`
- 支持普通类,单例对象。
**示例**
```scala
@log(verbose = true) class TestClass1(val i: Int = 0, var j: Int) {
log.info("hello")
}
@log(verbose=true, logType=io.github.dreamylost.logs.LogType.Slf4j)
class TestClass6(val i: Int = 0, var j: Int) {
log.info("hello world")
}
```
\ No newline at end of file
## @synchronized
`@synchronized`注解是一个更方便、更灵活的用于同步方法的注解。
**说明**
- `lockedName` 指定自定义的锁对象的名称。可选,默认`this`
- 支持静态方法(`object`中的函数)和实例方法(`class`中的函数)。
**示例**
```scala
private final val obj = new Object
@synchronized(lockedName = "obj") // 如果您填写一个不存在的字段名,编译将失败。
def getStr3(k: Int): String = {
k + ""
}
// 或者
@synchronized //使用 this 作为锁对象
def getStr(k: Int): String = {
k + ""
}
```
**宏生成的中间代码**
```scala
// 注意,它不会判断synchronized是否已经存在,因此如果synchronized已经存在,它将被使用两次。如下
// `def getStr(k: Int): String = this.synchronized(this.synchronized(k.$plus("")))
// 目前还不确定是否在字节码级别会被优化。
def getStr(k: Int): String = this.synchronized(k.$plus(""))
```
\ No newline at end of file
## @toString
`@toString`注解用于为Scala类生成`toString`方法。
**说明**
- `verbose` 指定是否开启详细编译日志。可选,默认`false`
- `includeFieldNames` 指定是否在`toString`中包含字段的名称。可选,默认`true`
- `includeInternalFields` 指定是否包含类内部定义的字段。它们不是在主构造函数中。可选,默认`true`
- `callSuper` 指定是否包含`super``toString`方法值。如果超级类是一种特质,则不支持。可选,默认`false`
- 支持普通类和样例类。
**示例**
```scala
@toString class TestClass(val i: Int = 0, var j: Int) {
val y: Int = 0
var z: String = "hello"
var x: String = "world"
}
println(new TestClass(1, 2));
```
详细选项说明
| includeInternalFields / includeFieldNames | false | true |
| ----------------------------------------- | -------------------------------------- | ------------------------------------------------ |
| false | ```TestClass(1, 2)``` | ```TestClass(i=0, j=2)``` |
| true | ```TestClass(1, 2, 0, hello, world)``` | ```TestClass(i=1, j=2, y=0, z=hello, x=world)``` |
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册