@@ -14,101 +14,180 @@ permalink: "/zh-cn/scala3/book/:title.html"
14
14
---
15
15
16
16
17
- _ 类型类_是一种抽象的参数化类型,它允许您在不使用子类型的情况下向任何封闭数据类型添加新行为。
17
+ _ 类型类_ 是一种抽象的参数化类型,它允许您在不使用子类型的情况下向任何封闭数据类型添加新行为。
18
+ 如果你从 Java 那边过来,你可以将类型类视为类似于 [ ` java.util.Comparator[T] ` ] [ comparator ] 的东西。
19
+
20
+ > Oliveira 等人写的论文 [ “Type Classes as Objects and Implicits”] [ typeclasses-paper ] (2010) 讨论了在 Scala 中类型类背后的基本观点。
21
+ > 虽然论文用了旧的 Scala 版本,但其中的观点至今依然有用。
22
+
18
23
这在多用例中很有用,例如:
19
24
20
25
- 表达你不拥有的类型——来自标准库或第三方库——如何符合这种行为
21
26
- 为多种类型表达这种行为,而不涉及这些类型之间的子类型关系
22
27
23
- 在 Scala 3 中,类型类只是具有一个或多个参数的 traits,其实现由 ` given ` 实例提供。
28
+ 类型类只是具有一个或多个参数的 traits,其实现在 Scala 3 中,由 ` given ` 实例提供,在 Scala 2 中用 ` implicit ` 值 。
24
29
25
30
## 例子
26
31
27
- 例如,` Show ` 是 Haskell 中众所周知的类型类,下面的代码显示了在 Scala 3 中实现它的一种方法。
32
+ 例如,` Show ` 是 Haskell 中众所周知的类型类,下面的代码显示了在 Scala 中实现它的一种方法。
28
33
如果您认为 Scala 类没有 ` toString ` 方法,您可以定义一个 ` Show ` 类型类,然后把此行为添加到任意的类,这个类是能够转换为自定义字符串。
29
34
30
35
### 类型类
31
36
32
37
创建类型类的第一步是声明具有一个或多个抽象方法的参数化 trait。
33
38
因为 ` Showable ` 只有一个名为 ` show ` 的方法,所以写成这样:
34
39
40
+ {% tabs 'definition' class=tabs-scala-version %}
41
+ {% tab 'Scala 2' %}
42
+ ``` scala
43
+ // a type class
44
+ trait Showable [A ] {
45
+ def show (a : A ): String
46
+ }
47
+ ```
48
+ {% endtab %}
49
+ {% tab 'Scala 3' %}
35
50
``` scala
36
51
// a type class
37
52
trait Showable [A ]:
38
53
extension(a : A ) def show : String
39
54
```
55
+ {% endtab %}
56
+ {% endtabs %}
40
57
41
- 这是 Scala 3 的说法,任何实现此 trait 的类型都必须定义 ` show ` 方法的工作方式。
42
- 请注意,语法非常接近普通的 trait:
58
+ 请注意,通常当你要定义 ` Show ` trait时,下面这样的办法接近普通的面向对象的办法:
43
59
60
+ {% tabs 'trait' class=tabs-scala-version %}
61
+ {% tab 'Scala 2' %}
62
+ ``` scala
63
+ // a trait
64
+ trait Show {
65
+ def show : String
66
+ }
67
+ ```
68
+ {% endtab %}
69
+ {% tab 'Scala 3' %}
44
70
``` scala
45
71
// a trait
46
72
trait Show :
47
73
def show : String
48
74
```
75
+ {% endtab %}
76
+ {% endtabs %}
49
77
50
78
有几件重要的事情需要指出:
51
79
52
- 1 . 像 ` Showable ` 这样的类型类有一个类型参数 ` A ` 来说明我们为哪种类型提供了 ` show ` 的实现;相反,像 ` Show ` 这样的正常特征不会 。
53
- 2 . 要将 show 功能添加到特定类型 ` A ` ,正常 trait 需要 ` A extends Show ` ,而对于类型类,我们需要实现 ` Showable[A] ` 。
54
- 3 . 为了在两个 ` Showable ` 中允许相同的方法调用语法来模仿 ` Show ` ,我们将 ` Showable.show ` 定义为扩展方法。
80
+ 1 . 像 ` Showable ` 这样的类型类有一个类型参数 ` A ` 来说明我们为哪种类型提供了 ` show ` 的实现;相反,像 ` Show ` 这样的传统的 trait 不会 。
81
+ 2 . 要将 show 功能添加到特定类型 ` A ` ,传统的 trait 需要 ` A extends Show ` ,而对于类型类,我们需要实现 ` Showable[A] ` 。
82
+ 3 . 在 Scala 3 中, 为了在两个 ` Showable ` 中允许相同的方法调用语法来模仿 ` Show ` ,我们将 ` Showable.show ` 定义为扩展方法。
55
83
56
84
### 实现具体实例
57
85
58
86
下一步是确定在应用程序中,` Showable ` 适用于哪些类,然后为它们实现该行为。
59
87
例如,为这个 ` Person ` 类实现 ` Showable ` :
60
88
89
+ {% tabs 'person' %}
90
+ {% tab 'Scala 2 and 3' %}
61
91
``` scala
62
92
case class Person (firstName : String , lastName : String )
63
93
```
94
+ {% endtab %}
95
+ {% endtabs %}
64
96
65
- 你将为 ` Showable[Person] ` 定义一个 ` given ` 值。
66
- 这段代码为 ` Person ` 类提供了一个 ` Showable ` 的具体实例:
97
+ 你将为 ` Showable[Person] ` 定义一个 _ 规范值_ ,例如下面的代码为 ` Person ` 类提供了一个 ` Showable ` 的实例:
67
98
99
+ {% tabs 'instance' class=tabs-scala-version %}
100
+ {% tab 'Scala 2' %}
101
+ ``` scala
102
+ implicit val showablePerson : Showable [Person ] = new Showable [Person ] {
103
+ def show (p : Person ): String =
104
+ s " ${p.firstName} ${p.lastName}"
105
+ }
106
+ ```
107
+ {% endtab %}
108
+ {% tab 'Scala 3' %}
68
109
``` scala
69
110
given Showable [Person ] with
70
111
extension(p : Person ) def show : String =
71
112
s " ${p.firstName} ${p.lastName}"
72
113
```
73
-
74
- 如图所示,这被定义为 ` Person ` 类的扩展方法,它使用 ` show ` 方法主体内的引用 ` p ` 。
114
+ {% endtab %}
115
+ {% endtabs %}
75
116
76
117
### 使用类型类
77
118
78
119
现在你可以像这样使用这个类型类:
79
120
121
+ {% tabs 'usage' class=tabs-scala-version %}
122
+ {% tab 'Scala 2' %}
123
+ ``` scala
124
+ val person = Person (" John" , " Doe" )
125
+ println(showablePerson.show(person))
126
+ ```
127
+
128
+ 注意,在实践中,类型类一般与类型未知的值一起使用,而不像下面章节展示的 ` Person ` 类。
129
+ {% endtab %}
130
+ {% tab 'Scala 3' %}
80
131
``` scala
81
132
val person = Person (" John" , " Doe" )
82
133
println(person.show)
83
134
```
135
+ {% endtab %}
136
+ {% endtabs %}
84
137
85
138
同样,如果 Scala 没有可用于每个类的 ` toString ` 方法,您可以使用此技术将 ` Showable ` 行为添加到您希望能够转换为 ` String ` 的任何类。
86
139
87
140
### 编写使用类型类的方法
88
141
89
142
与继承一样,您可以定义使用 ` Showable ` 作为类型参数的方法:
90
143
144
+ {% tabs 'method' class=tabs-scala-version %}
145
+ {% tab 'Scala 2' %}
146
+ ``` scala
147
+ def showAll [A ](as : List [A ])(implicit showable : Showable [A ]): Unit =
148
+ as.foreach(a => println(showable.show(a)))
149
+
150
+ showAll(List (Person (" Jane" ), Person (" Mary" )))
151
+ ```
152
+ {% endtab %}
153
+ {% tab 'Scala 3' %}
91
154
``` scala
92
- def showAll [S : Showable ](xs : List [S ]): Unit =
93
- xs .foreach(x => println(x .show))
155
+ def showAll [A : Showable ](as : List [A ]): Unit =
156
+ as .foreach(x => println(a .show))
94
157
95
158
showAll(List (Person (" Jane" ), Person (" Mary" )))
96
159
```
160
+ {% endtab %}
161
+ {% endtabs %}
97
162
98
163
### 具有多种方法的类型类
99
164
100
165
请注意,如果要创建具有多个方法的类型类,则初始语法如下所示:
101
166
167
+ {% tabs 'multiple-methods' class=tabs-scala-version %}
168
+ {% tab 'Scala 2' %}
169
+ ``` scala
170
+ trait HasLegs [A ] {
171
+ def walk (a : A ): Unit
172
+ def run (a : A ): Unit
173
+ }
174
+ ```
175
+ {% endtab %}
176
+ {% tab 'Scala 3' %}
102
177
``` scala
103
178
trait HasLegs [A ]:
104
179
extension (a : A )
105
180
def walk (): Unit
106
181
def run (): Unit
107
182
```
183
+ {% endtab %}
184
+ {% endtabs %}
108
185
109
186
### 一个真实的例子
110
187
111
188
有关如何在 Scala 3 中使用类型类的真实示例,请参阅[ 多元相等性部分] [ multiversal ] 中的 ` CanEqual ` 讨论。
112
189
113
-
190
+ [ typeclasses-paper ] : https://infoscience.epfl.ch/record/150280/files/TypeClasses.pdf
191
+ [ typeclasses-chapter] : {% link _ overviews/scala3-book/ca-type-classes.md %}
192
+ [ comparator ] : https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html
114
193
[ multiversal] : {% link _ zh-cn/overviews/scala3-book/ca-multiversal-equality.md %}
0 commit comments