Skip to content

Commit d2d7331

Browse files
committed
code tabs for tour pkg objects
1 parent 69a362b commit d2d7331

File tree

2 files changed

+95
-10
lines changed

2 files changed

+95
-10
lines changed

_tour/package-objects.md

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
11
---
22
layout: tour
3-
title: Package Objects
3+
title: Top Level Definitions in Packages
44
partof: scala-tour
55

66
num: 36
77
previous-page: packages-and-imports
88
---
99

10-
# Package objects
10+
Often, it is convenient to have definitions accessible accross an entire package, and not need to invent a
11+
name for a wrapper `object` to contain them.
1112

12-
Scala provides package objects as a convenient container shared across an entire package.
13+
{% tabs pkg-obj-vs-top-lvl_1 class=tabs-scala-version %}
14+
{% tab 'Scala 2' for=pkg-obj-vs-top-lvl_1 %}
15+
Scala 2 provides _package objects_ as a convenient container shared across an entire package.
1316

1417
Package objects
1518
can contain arbitrary definitions, not just variable and method definitions. For instance, they are frequently
1619
used to hold package-wide type aliases and implicit conversions. Package objects can even inherit
1720
Scala classes and traits.
1821

22+
> In a future version of Scala 3, package objects will be removed in favor of top level definitions.
23+
1924
By convention, the source code for a package object is usually put in a source file named `package.scala`.
2025

2126
Each package is allowed to have one package object. Any definitions placed in a package object are considered
2227
members of the package itself.
2328

29+
{% endtab %}
30+
{% tab 'Scala 3' for=pkg-obj-vs-top-lvl_1 %}
31+
In Scala 3, any kind of definition can be declared at the top level of a package. For example, classes, enums,
32+
methods and variables.
33+
34+
Any definitions placed at the top level of a package are considered members of the package itself.
35+
36+
> In Scala 2 top-level method, type and variable definitions had to be wrapped in a **package object**.
37+
> These are still usable in Scala 3 for backwards compatibility. You can see how they work by switching tabs.
38+
39+
{% endtab %}
40+
{% endtabs %}
41+
2442
See example below. Assume first a class `Fruit` and three `Fruit` objects in a package
2543
`gardening.fruits`:
2644

45+
46+
{% tabs pkg-obj-vs-top-lvl_2 %}
47+
{% tab 'Scala 2 and 3' for=pkg-obj-vs-top-lvl_2 %}
2748
```
2849
// in file gardening/fruits/Fruit.scala
2950
package gardening.fruits
@@ -33,10 +54,15 @@ object Apple extends Fruit("Apple", "green")
3354
object Plum extends Fruit("Plum", "blue")
3455
object Banana extends Fruit("Banana", "yellow")
3556
```
57+
{% endtab %}
58+
{% endtabs %}
3659

3760
Now assume you want to place a variable `planted` and a method `showFruit` directly into package `gardening.fruits`.
3861
Here's how this is done:
3962

63+
{% tabs pkg-obj-vs-top-lvl_3 class=tabs-scala-version %}
64+
{% tab 'Scala 2' for=pkg-obj-vs-top-lvl_3 %}
65+
4066
```
4167
// in file gardening/fruits/package.scala
4268
package gardening
@@ -47,13 +73,29 @@ package object fruits {
4773
}
4874
}
4975
```
76+
{% endtab %}
77+
{% tab 'Scala 3' for=pkg-obj-vs-top-lvl_3 %}
78+
79+
```
80+
// in file gardening/fruits/package.scala
81+
package gardening.fruits
82+
83+
val planted = List(Apple, Plum, Banana)
84+
def showFruit(fruit: Fruit): Unit =
85+
println(s"${fruit.name}s are ${fruit.color}")
86+
```
87+
{% endtab %}
88+
{% endtabs %}
5089

51-
As an example of how to use this, the following object `PrintPlanted` imports `planted` and `showFruit` in exactly the same
52-
way it imports class `Fruit`, using a wildcard import on package gardening.fruits:
90+
As an example of how to use this, the following program `PrintPlanted` imports `planted` and `showFruit` in exactly the same
91+
way it imports class `Fruit`, using a wildcard import on package `gardening.fruits`:
5392

93+
{% tabs pkg-obj-vs-top-lvl_4 class=tabs-scala-version %}
94+
{% tab 'Scala 2' for=pkg-obj-vs-top-lvl_4 %}
5495
```
5596
// in file PrintPlanted.scala
5697
import gardening.fruits._
98+
5799
object PrintPlanted {
58100
def main(args: Array[String]): Unit = {
59101
for (fruit <- planted) {
@@ -62,11 +104,53 @@ object PrintPlanted {
62104
}
63105
}
64106
```
107+
{% endtab %}
108+
{% tab 'Scala 3' for=pkg-obj-vs-top-lvl_4 %}
109+
```
110+
// in file PrintPlanted.scala
111+
import gardening.fruits.*
112+
113+
@main def PrintPlanted: Unit =
114+
for fruit <- planted do
115+
showFruit(fruit)
116+
```
117+
{% endtab %}
118+
{% endtabs %}
119+
120+
### Aggregating Several Definitions at the Package Level
121+
122+
Often, your project may have several reusable definitions defined in various modules, that you
123+
wish to aggregate at the top level of a package.
65124

66-
Package objects are like other objects, which means you can use inheritance for building them. For example, one might mix in a couple of traits:
125+
For example, some helper methods in the trait `FruitHelpers` and
126+
some term/type aliases in trait `FruitAliases`. Here is how you can put all their definitions at the level of the `fruit`
127+
package:
128+
129+
{% tabs pkg-obj-vs-top-lvl_5 class=tabs-scala-version %}
130+
{% tab 'Scala 2' for=pkg-obj-vs-top-lvl_5 %}
131+
132+
Package objects are like other objects, which means you can use inheritance for building them.
133+
So here we mix in the helper traits as parents of the package object.
67134

68135
```
69-
package object fruits extends FruitAliases with FruitHelpers {
70-
// helpers and variables follows here
71-
}
136+
package gardening
137+
138+
// `fruits` instead inherits its members from its parents.
139+
package object fruits extends FruitAliases with FruitHelpers
140+
```
141+
{% endtab %}
142+
{% tab 'Scala 3' for=pkg-obj-vs-top-lvl_5 %}
143+
144+
In Scala 3, it is preferred to use `export` to compose members from several objects into a single scope.
145+
Here we define private objects that mix in the helper traits, then export their members at the top level:
146+
147+
```
148+
package gardening.fruits
149+
150+
private object FruitAliases extends FruitAliases
151+
private object FruitHelpers extends FruitHelpers
152+
153+
export FruitHelpers.*, FruitAliases.*
72154
```
155+
{% endtab %}
156+
{% endtabs %}

_tour/packages-and-imports.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ One convention is to name the package the same as the directory containing the S
3838
UserPreferences.scala
3939
- test
4040
```
41-
Notice how the `users` directory is within the `scala` directory and how there are multiple Scala files within the package. Each Scala file in the package could have the same package declaration. The other way to declare packages is by using braces:
41+
Notice how the `users` directory is within the `scala` directory and how there are multiple Scala files within the package. Each Scala file in the package could have the same package declaration. The other way to declare packages is by nesting them inside each other:
4242

4343
{% tabs packages-and-imports_2 class=tabs-scala-version %}
4444
{% tab 'Scala 2' for=packages-and-imports_2 %}
@@ -58,6 +58,7 @@ package users {
5858
package users:
5959
package administrators:
6060
class NormalUser
61+
6162
package normalusers:
6263
class NormalUser
6364
```

0 commit comments

Comments
 (0)