0
# Annotation System
1
2
Scala 3 provides a rich annotation system for controlling compiler behavior, optimization hints, API design, and metaprogramming capabilities.
3
4
## Experimental and Stability Annotations
5
6
### Experimental Features
7
8
```scala { .api }
9
class experimental extends StaticAnnotation
10
class alpha extends StaticAnnotation
11
```
12
13
- **`@experimental`**: Mark APIs that are experimental and may change
14
- **`@alpha`**: Mark APIs that are in alpha stage and unstable
15
16
Usage:
17
```scala
18
@experimental
19
def newFeature(): String = "This API may change"
20
21
@alpha
22
class BetaClass:
23
def method(): Int = 42
24
25
// Usage requires explicit import
26
import scala.language.experimental.featureName
27
val result = newFeature() // Requires experimental import
28
```
29
30
## Capability System Annotations
31
32
### Capability Marking
33
34
```scala { .api }
35
class capability extends StaticAnnotation
36
```
37
38
Mark classes as capability classes for the capture checking system:
39
40
```scala
41
@capability
42
class FileReader
43
44
@capability
45
class NetworkAccess
46
47
def readFile(using FileReader): String = "file content"
48
def fetchData(using NetworkAccess): String = "network data"
49
```
50
51
## Method and Constructor Annotations
52
53
### Constructor Restrictions
54
55
```scala { .api }
56
class constructorOnly extends StaticAnnotation
57
```
58
59
Restrict annotation usage to constructors only:
60
61
```scala
62
@constructorOnly
63
class ValidatedInt(value: Int):
64
require(value >= 0, "Must be non-negative")
65
66
// Can only be used in constructors
67
class Person(@ValidatedInt age: Int, name: String)
68
```
69
70
### Method Naming
71
72
```scala { .api }
73
class targetName(name: String) extends StaticAnnotation
74
```
75
76
Override the name used for the method in compiled bytecode:
77
78
```scala
79
class Calculator:
80
@targetName("add")
81
def +(x: Int, y: Int): Int = x + y
82
83
@targetName("multiply")
84
def *(x: Int, y: Int): Int = x * y
85
86
// Bytecode will have methods named "add" and "multiply"
87
val calc = Calculator()
88
val sum = calc + (5, 3) // Calls "add" in bytecode
89
val product = calc * (4, 7) // Calls "multiply" in bytecode
90
```
91
92
### Static Method Generation
93
94
```scala { .api }
95
class static extends StaticAnnotation
96
```
97
98
Generate static methods in the bytecode:
99
100
```scala
101
object MathUtils:
102
@static
103
def square(x: Int): Int = x * x
104
105
@static
106
def cube(x: Double): Double = x * x * x
107
108
// Generates static methods in Java bytecode for Java interop
109
// Java code can call: MathUtils.square(5)
110
```
111
112
## Optimization and Performance Annotations
113
114
### Thread Safety
115
116
```scala { .api }
117
class threadUnsafe extends StaticAnnotation
118
```
119
120
Mark code as thread-unsafe for optimization hints:
121
122
```scala
123
@threadUnsafe
124
class FastCounter:
125
private var count = 0
126
def increment(): Unit = count += 1
127
def get: Int = count
128
129
// Compiler can apply single-threaded optimizations
130
```
131
132
### Trait Transparency
133
134
```scala { .api }
135
class transparentTrait extends StaticAnnotation
136
```
137
138
Mark traits as transparent for better optimization:
139
140
```scala
141
@transparentTrait
142
trait Logging:
143
def log(msg: String): Unit = println(s"[LOG] $msg")
144
145
class Service extends Logging:
146
def process(): Unit =
147
log("Processing started")
148
// Processing logic
149
log("Processing completed")
150
```
151
152
### Loop Unrolling
153
154
```scala { .api }
155
class unroll extends StaticAnnotation
156
```
157
158
Hint to the compiler to unroll loops or recursive calls:
159
160
```scala
161
@unroll
162
def factorial(n: Int): Int =
163
if n <= 1 then 1
164
else n * factorial(n - 1)
165
166
@unroll
167
def sumArray(arr: Array[Int]): Int =
168
var sum = 0
169
var i = 0
170
while i < arr.length do
171
sum += arr(i)
172
i += 1
173
sum
174
```
175
176
## Binary Compatibility Annotations
177
178
### Public Visibility Control
179
180
```scala { .api }
181
class publicInBinary extends StaticAnnotation
182
```
183
184
Force methods to be public in bytecode even if they're private in Scala:
185
186
```scala
187
class Library:
188
@publicInBinary
189
private def internalMethod(): String = "internal"
190
191
def publicMethod(): String = internalMethod()
192
193
// internalMethod will be public in Java bytecode for framework access
194
```
195
196
### Initialization Control
197
198
```scala { .api }
199
class init extends StaticAnnotation
200
```
201
202
Control initialization order and behavior:
203
204
```scala
205
class Database:
206
@init
207
def initialize(): Unit =
208
// Critical initialization code
209
println("Database initialized")
210
211
def query(sql: String): String =
212
// Query implementation
213
s"Result for: $sql"
214
```
215
216
## Meta-Programming Annotations
217
218
### Macro Annotations
219
220
```scala { .api }
221
trait MacroAnnotation extends StaticAnnotation
222
```
223
224
Base trait for creating macro annotations that transform code at compile time:
225
226
```scala
227
import scala.annotation.MacroAnnotation
228
import scala.quoted.*
229
230
class toString extends MacroAnnotation:
231
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
232
import quotes.reflect.*
233
234
tree match
235
case ClassDef(name, constr, parents, self, body) =>
236
// Generate toString method
237
val toStringMethod = DefDef.copy(tree.asInstanceOf[DefDef])(
238
name = "toString",
239
paramss = List(List()),
240
tpt = TypeTree.of[String],
241
rhs = Some(Literal(StringConstant(s"Instance of $name")))
242
)
243
List(ClassDef.copy(tree.asInstanceOf[ClassDef])(
244
name, constr, parents, self, body :+ toStringMethod
245
))
246
case _ => List(tree)
247
248
// Usage
249
@toString
250
class Person(name: String, age: Int)
251
252
val person = Person("Alice", 30)
253
println(person.toString) // "Instance of Person"
254
```
255
256
### Refining Annotations
257
258
```scala { .api }
259
trait RefiningAnnotation extends StaticAnnotation
260
```
261
262
Base trait for annotations that refine types:
263
264
```scala
265
import scala.annotation.RefiningAnnotation
266
267
class positive extends RefiningAnnotation
268
269
def process(@positive x: Int): Int =
270
require(x > 0)
271
x * 2
272
273
// The annotation refines the type to indicate positive integers
274
val result = process(5) // OK
275
// val invalid = process(-3) // Could be caught by static analysis
276
```
277
278
## Usage Examples
279
280
### Combining Annotations
281
282
```scala
283
@experimental
284
@targetName("advancedCalculation")
285
class Calculator:
286
@threadUnsafe
287
@unroll
288
private def fastCompute(n: Int): Long =
289
if n <= 1 then 1L
290
else n * fastCompute(n - 1)
291
292
@publicInBinary
293
@static
294
def compute(x: Int): Long = fastCompute(x)
295
296
// This class demonstrates multiple annotation types:
297
// - Experimental API that may change
298
// - Custom bytecode method name
299
// - Thread-unsafe optimization
300
// - Loop unrolling hint
301
// - Public binary visibility for private method
302
// - Static method generation
303
```
304
305
### Custom Domain Annotations
306
307
```scala
308
// Define domain-specific annotations
309
class authenticated extends StaticAnnotation
310
class cached(timeoutSeconds: Int) extends StaticAnnotation
311
class rateLimit(requestsPerMinute: Int) extends StaticAnnotation
312
313
class UserService:
314
@authenticated
315
@cached(300) // 5 minute cache
316
def getUserProfile(userId: String): UserProfile =
317
// Expensive database operation
318
UserProfile(userId, "User Name")
319
320
@authenticated
321
@rateLimit(100) // 100 requests per minute
322
def updateUser(userId: String, data: UserData): Unit =
323
// Update user data
324
println(s"Updating user $userId")
325
326
// These annotations can be processed by frameworks or macros
327
// to automatically implement cross-cutting concerns
328
```
329
330
### Validation Annotations
331
332
```scala
333
class min(value: Int) extends StaticAnnotation
334
class max(value: Int) extends StaticAnnotation
335
class email extends StaticAnnotation
336
class notNull extends StaticAnnotation
337
338
case class User(
339
@notNull @min(1) name: String,
340
@min(0) @max(150) age: Int,
341
@email email: String
342
)
343
344
// These annotations can be processed at compile time
345
// to generate validation logic
346
```
347
348
### API Evolution Annotations
349
350
```scala
351
class deprecated(message: String, since: String) extends StaticAnnotation
352
class migration(from: String, to: String) extends StaticAnnotation
353
354
class APIService:
355
@deprecated("Use newMethod instead", "3.1.0")
356
def oldMethod(): String = "old implementation"
357
358
@migration(from = "oldMethod", to = "newMethod")
359
def newMethod(): String = "new implementation"
360
361
@experimental
362
def futureMethod(): String = "may change in future versions"
363
364
// Provides clear evolution path for APIs
365
val service = APIService()
366
val result1 = service.oldMethod() // Compiler warning about deprecation
367
val result2 = service.newMethod() // Current recommended approach
368
val result3 = service.futureMethod() // Requires experimental import
369
```
370
371
### Framework Integration Annotations
372
373
```scala
374
// HTTP framework annotations
375
class GET(path: String) extends StaticAnnotation
376
class POST(path: String) extends StaticAnnotation
377
class PathParam(name: String) extends StaticAnnotation
378
class QueryParam(name: String) extends StaticAnnotation
379
380
class UserController:
381
@GET("/users/{id}")
382
def getUser(@PathParam("id") userId: String): User =
383
User(userId, "User Name")
384
385
@POST("/users")
386
def createUser(user: User): User =
387
// Create user logic
388
user
389
390
@GET("/users")
391
def searchUsers(@QueryParam("name") name: String): List[User] =
392
// Search logic
393
List(User("1", name))
394
395
// Database mapping annotations
396
class Table(name: String) extends StaticAnnotation
397
class Column(name: String) extends StaticAnnotation
398
class Id extends StaticAnnotation
399
400
@Table("users")
401
case class UserEntity(
402
@Id @Column("user_id") id: String,
403
@Column("full_name") name: String,
404
@Column("email_address") email: String
405
)
406
```
407
408
The annotation system in Scala 3 provides powerful capabilities for controlling compiler behavior, enabling metaprogramming, and integrating with frameworks while maintaining type safety and compile-time verification.