{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
\n", "\n", "# Classes\n", "\n", "Classes in Scala are blueprints for creating objects. They can contain methods,\n", "values, variables, types, objects, traits, and classes which are collectively called _members_. Types, objects, and traits will be covered later in the tour.\n", "\n", "## Defining a class\n", "A minimal class definition is simply the keyword `class` and\n", "an identifier. Class names should be capitalized." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mUser\u001b[39m\n", "\u001b[36muser1\u001b[39m: \u001b[32mUser\u001b[39m = ammonite.$sess.cmd0$Helper$User@75e7dbbd" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class User\n", "\n", "val user1 = new User" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(2, 3)\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPoint\u001b[39m\n", "\u001b[36mpoint1\u001b[39m: \u001b[32mPoint\u001b[39m = (2, 3)\n", "\u001b[36mres1_2\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Point(var x: Int, var y: Int) {\n", "\n", " def move(dx: Int, dy: Int): Unit = {\n", " x = x + dx\n", " y = y + dy\n", " }\n", "\n", " override def toString: String =\n", " s\"($x, $y)\"\n", "}\n", "\n", "val point1 = new Point(2, 3)\n", "point1.x // 2\n", "println(point1) // prints (2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This `Point` class has four members: the variables `x` and `y` and the methods `move` and\n", "`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.ipynb), it is tagged with the `override` keyword.\n", "\n", "## Constructors\n", "\n", "Constructors can have optional parameters by providing a default value like so:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPoint\u001b[39m\n", "\u001b[36morigin\u001b[39m: \u001b[32mPoint\u001b[39m = ammonite.$sess.cmd2$Helper$Point@7c93be3e\n", "\u001b[36mpoint1\u001b[39m: \u001b[32mPoint\u001b[39m = ammonite.$sess.cmd2$Helper$Point@2643ecab" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Point(var x: Int = 0, var y: Int = 0)\n", "\n", "val origin = new Point // x and y are both set to 0\n", "val point1 = new Point(1)\n", "println(point1.x) // prints 1\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPoint\u001b[39m\n", "\u001b[36mpoint2\u001b[39m: \u001b[32mPoint\u001b[39m = ammonite.$sess.cmd3$Helper$Point@4fe09999" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Point(var x: Int = 0, var y: Int = 0)\n", "val point2 = new Point(y=2)\n", "println(point2.y) // prints 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is also a good practice to enhance clarity.\n", "\n", "## Private Members and Getter/Setter Syntax\n", "Members are public by default. Use the `private` access modifier\n", "to hide them from outside of the class." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WARNING: Out of bounds\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPoint\u001b[39m\n", "\u001b[36mpoint1\u001b[39m: \u001b[32mPoint\u001b[39m = ammonite.$sess.cmd4$Helper$Point@3e08416e" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Point {\n", " private var _x = 0\n", " private var _y = 0\n", " private val bound = 100\n", "\n", " def x = _x\n", " def x_= (newValue: Int): Unit = {\n", " if (newValue < bound) _x = newValue else printWarning\n", " }\n", "\n", " def y = _y\n", " def y_= (newValue: Int): Unit = {\n", " if (newValue < bound) _y = newValue else printWarning\n", " }\n", "\n", " private def printWarning = println(\"WARNING: Out of bounds\")\n", "}\n", "\n", "val point1 = new Point\n", "point1.x = 99\n", "point1.y = 101 // prints the warning" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "Primary constructor parameters with `val` and `var` are public. However, because `val`s are immutable, you can't write the following." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd5.sc:3: reassignment to val\n", "val res5_2 = point.x = 3 // <-- does not compile\n", " ^Compilation Failed" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "class Point(val x: Int, val y: Int)\n", "val point = new Point(1, 2)\n", "point.x = 3 // <-- does not compile" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parameters without `val` or `var` are private values, visible only within the class." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd5.sc:3: value x is not a member of Helper.this.Point\n", "val res5_2 = point.x // <-- does not compile\n", " ^Compilation Failed" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "class Point(x: Int, y: Int)\n", "val point = new Point(1, 2)\n", "point.x // <-- does not compile" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Scala (2.13)", "language": "scala", "name": "scala213" }, "language_info": { "codemirror_mode": "text/x-scala", "file_extension": ".scala", "mimetype": "text/x-scala", "name": "scala", "nbconvert_exporter": "script", "version": "2.13.1" } }, "nbformat": 4, "nbformat_minor": 4 }