diff --git a/tutorials/tour/_posts/2017-02-13-classes.md b/tutorials/tour/_posts/2017-02-13-classes.md index ba6df31033..b172c144fb 100644 --- a/tutorials/tour/_posts/2017-02-13-classes.md +++ b/tutorials/tour/_posts/2017-02-13-classes.md @@ -9,46 +9,103 @@ categories: tour num: 4 next-page: traits previous-page: unified-types +topics: classes +prerequisite-knowledge: no-return-keyword, type-declaration-syntax, string-interpolation, procedures --- -Classes in Scala are static templates that can be instantiated into many objects at runtime. -Here is a class definition which defines a class `Point`: +Classes in Scala are blueprints for creating objects. They can contain methods, +values, variables, types, objects, traits, and classes which are collectively called _members_. Types, objects, and traits will be covered later in the tour. + +## Defining a class +A minimal class definition is simply the keyword `class` and +an identifier. Class names should be capitalized. +```tut +class User + +val user1 = new User +``` +The keyword `new` is used to create an instance of the class. `User` has a default constructor which takes no arguments because no constructor was defined. However, you'll often want a constructor and class body. Here is an example class definition for a point: ```tut class Point(var x: Int, var y: Int) { + def move(dx: Int, dy: Int): Unit = { x = x + dx y = y + dy } + override def toString: String = - "(" + x + ", " + y + ")" + s"($x, $y)" } + +val point1 = new Point(2, 3) +point1.x // 2 +println(point1) // prints (x, y) ``` -Classes in Scala are parameterized with constructor arguments. The code above defines two constructor arguments, `x` and `y`; they are both visible in the whole body of the class. +This `Point` class has four members: the variables `x` and `y` and the methods `move` and +`toString`. Unlike many other languages, the primary constructor is in the class signature `(var x: Int, var y: Int)`. The `move` method takes two integer arguments and returns the Unit value `()`, which carries no information. This corresponds roughly with `void` in Java-like languages. `toString`, on the other hand, does not take any arguments but returns a `String` value. Since `toString` overrides `toString` from [`AnyRef`](unified-types.html), it is tagged with the `override` keyword. + +## Constructors + +Constructors can have optional parameters by providing a default value like so: -The class also includes two methods, `move` and `toString`. `move` takes two integer arguments but does not return a value (the return type `Unit` corresponds to `void` in Java-like languages). `toString`, on the other hand, does not take any parameters but returns a `String` value. Since `toString` overrides the pre-defined `toString` method, it is tagged with the `override` keyword. +```tut +class Point(var x: Int = 0, var y: Int = 0) -Note that in Scala, it isn't necessary to say `return` in order to return a value. The value returned from a method is simply the last value in the method body. In the case of the `toString` method above, the expression after the equals sign is evaluated and returned to the caller. +val origin = new Point // x and y are both set to 0 +val point1 = new Point(1) +println(point1.x) // prints 1 -Classes are instantiated with the `new` primitive, as follows: +``` +In this version of the `Point` class, `x` and `y` have the default value `0` so no arguments are required. However, because the constructor reads arguments left to right, if you just wanted to pass in a `y` value, you would need to name the parameter. +``` +class Point(var x: Int = 0, var y: Int = 0) +val point2 = new Point(y=2) +println(point2.y) // prints 2 +``` + +This is also a good practice to enhance clarity. + +## Private Members and Getter/Setter Syntax +Members are public by default. Use the `private` access modifier +to hide them from outside of the class. ```tut -object Classes { - def main(args: Array[String]) { - val pt = new Point(1, 2) - println(pt) - pt.move(10, 10) - println(pt) +class Point { + private var _x = 0 + private var _y = 0 + private val bound = 100 + + def x = _x + def x_= (newValue: Int): Unit = { + if (newValue < bound) _x = newValue else printWarning + } + + def y = _y + def y_= (newValue: Int): Unit = { + if (newValue < bound) _y = newValue else printWarning } + + private def printWarning = println("WARNING: Out of bounds") } -``` -The program defines an executable application Classes in form of a top-level [singleton object](singleton-objects) with a `main` method. The `main` method creates a new `Point` and stores it in value `pt`. Note that values defined with the `val` construct are different from variables defined with the `var` construct (see class `Point` above) in that they do not allow updates; i.e. the value is constant. +val point1 = new Point +point1.x = 99 +point1.y = 101 // prints the warning +``` +In this version of the `Point` class, the data is stored in private variables `_x` and `_y`. There are methods `def x` and `def y` for accessing the private data. `def x_=` and `def y_=` are for validating and setting the value of `_x` and `_y`. Notice the special syntax for the setters: the method has `_=` appended to the identifier of the getter and the parameters come after. -Here is the output of the program: +Primary constructor parameters with `val` and `var` are public. However, because `val`s are immutable, you can't write the following. +``` +class Point(val x: Int, val y: Int) +val point = new Point(1, 2) +point.x = 3 // <-- does not compile +``` +Parameters without `val` or `var` are private values, visible only within the class. ``` -(1, 2) -(11, 12) +class Point(x: Int, y: Int) +val point = new Point(1, 2) +point.x // <-- does not compile ```