diff --git a/_overviews/scala3-book/types-generics.md b/_overviews/scala3-book/types-generics.md index afedd70a7f..c175158c55 100644 --- a/_overviews/scala3-book/types-generics.md +++ b/_overviews/scala3-book/types-generics.md @@ -13,6 +13,30 @@ Generic classes (or traits) take a type as _a parameter_ within square brackets The Scala convention is to use a single letter (like `A`) to name those type parameters. The type can then be used inside the class as needed for method instance parameters, or on return types: +{% tabs stack class=tabs-scala-version %} + +{% tab 'Scala 2' %} +```scala +// here we declare the type parameter A +// v +class Stack[A] { + private var elements: List[A] = Nil + // ^ + // Here we refer to the type parameter + // v + def push(x: A): Unit = + elements = elements.prepended(x) + def peek: A = elements.head + def pop(): A = { + val currentTop = peek + elements = elements.tail + currentTop + } +} +``` +{% endtab %} + +{% tab 'Scala 3' %} ```scala // here we declare the type parameter A // v @@ -21,19 +45,33 @@ class Stack[A]: // ^ // Here we refer to the type parameter // v - def push(x: A): Unit = { elements = elements.prepended(x) } + def push(x: A): Unit = + elements = elements.prepended(x) def peek: A = elements.head def pop(): A = val currentTop = peek elements = elements.tail currentTop ``` +{% endtab %} +{% endtabs %} This implementation of a `Stack` class takes any type as a parameter. The beauty of generics is that you can now create a `Stack[Int]`, `Stack[String]`, and so on, allowing you to reuse your implementation of a `Stack` for arbitrary element types. This is how you create and use a `Stack[Int]`: +{% tabs stack-usage class=tabs-scala-version %} +{% tab 'Scala 2' %} +``` +val stack = new Stack[Int] +stack.push(1) +stack.push(2) +println(stack.pop()) // prints 2 +println(stack.pop()) // prints 1 +``` +{% endtab %} +{% tab 'Scala 3' %} ``` val stack = Stack[Int] stack.push(1) @@ -41,6 +79,8 @@ stack.push(2) println(stack.pop()) // prints 2 println(stack.pop()) // prints 1 ``` +{% endtab %} +{% endtabs %} > See the [Variance section][variance] for details on how to express variance with generic types. diff --git a/_overviews/scala3-book/types-inferred.md b/_overviews/scala3-book/types-inferred.md index a33be89bad..733a8d540c 100644 --- a/_overviews/scala3-book/types-inferred.md +++ b/_overviews/scala3-book/types-inferred.md @@ -11,22 +11,32 @@ next-page: types-generics As with other statically typed programming languages, in Scala you can _declare_ a type when creating a new variable: +{% tabs xy %} +{% tab 'Scala 2 and 3' %} ```scala val x: Int = 1 val y: Double = 1 ``` +{% endtab %} +{% endtabs %} In those examples the types are _explicitly_ declared to be `Int` and `Double`, respectively. However, in Scala you generally don’t have to declare the type when defining value binders: +{% tabs abm %} +{% tab 'Scala 2 and 3' %} ```scala val a = 1 val b = List(1, 2, 3) val m = Map(1 -> "one", 2 -> "two") ``` +{% endtab %} +{% endtabs %} When you do this, Scala _infers_ the types, as shown in the following REPL interaction: +{% tabs abm2 %} +{% tab 'Scala 2 and 3' %} ```scala scala> val a = 1 val a: Int = 1 @@ -37,5 +47,7 @@ val b: List[Int] = List(1, 2, 3) scala> val m = Map(1 -> "one", 2 -> "two") val m: Map[Int, String] = Map(1 -> one, 2 -> two) ``` +{% endtab %} +{% endtabs %} Indeed, most variables are defined this way, and Scala’s ability to automatically infer types is one feature that makes it _feel_ like a dynamically typed language. diff --git a/_overviews/scala3-book/types-introduction.md b/_overviews/scala3-book/types-introduction.md index cf387c9ac7..f2fe4c245e 100644 --- a/_overviews/scala3-book/types-introduction.md +++ b/_overviews/scala3-book/types-introduction.md @@ -12,19 +12,27 @@ next-page: types-inferred Scala is a unique language in that it’s statically typed, but often _feels_ flexible and dynamic. For instance, thanks to type inference you can write code like this without explicitly specifying the variable types: +{% tabs hi %} +{% tab 'Scala 2 and 3' %} ```scala val a = 1 val b = 2.0 val c = "Hi!" ``` +{% endtab %} +{% endtabs %} That makes the code feel dynamically typed. And thanks to new features, like [union types][union-types] in Scala 3, you can also write code like the following that expresses very concisely which values are expected as arguments and which types are returned: +{% tabs union-example %} +{% tab 'Scala 3 Only' %} ```scala def isTruthy(a: Boolean | Int | String): Boolean = ??? def dogCatOrWhatever(): Dog | Plant | Car | Sun = ??? ``` +{% endtab %} +{% endtabs %} As the example suggests, when using union types, the types don’t have to share a common hierarchy, and you can still accept them as arguments or return them from a method.