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

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
\n", "\n", "# Traits\n", "\n", "Traits are used to share interfaces and fields between classes. They are similar to Java 8's interfaces. Classes and objects can extend traits but traits cannot be instantiated and therefore have no parameters.\n", "\n", "## Defining a trait\n", "A minimal trait is simply the keyword `trait` and an identifier:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mHairColor\u001b[39m" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait HairColor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Traits become especially useful as generic types and with abstract methods." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mIterator\u001b[39m" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait Iterator[A] {\n", " def hasNext: Boolean\n", " def next(): A\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Extending the `trait Iterator[A]` requires a type `A` and implementations of the methods `hasNext` and `next`.\n", "\n", "## Using traits\n", "Use the `extends` keyword to extend a trait. Then implement any abstract members of the trait using the `override` keyword:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mIterator\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mIntIterator\u001b[39m\n", "\u001b[36miterator\u001b[39m: \u001b[32mIntIterator\u001b[39m = ammonite.$sess.cmd2$Helper$IntIterator@577b372f\n", "\u001b[36mres2_3\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m0\u001b[39m\n", "\u001b[36mres2_4\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m1\u001b[39m" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait Iterator[A] {\n", " def hasNext: Boolean\n", " def next(): A\n", "}\n", "\n", "class IntIterator(to: Int) extends Iterator[Int] {\n", " private var current = 0\n", " override def hasNext: Boolean = current < to\n", " override def next(): Int = {\n", " if (hasNext) {\n", " val t = current\n", " current += 1\n", " t\n", " } else 0\n", " }\n", "}\n", "\n", "\n", "val iterator = new IntIterator(10)\n", "iterator.next() // returns 0\n", "iterator.next() // returns 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This `IntIterator` class takes a parameter `to` as an upper bound. It `extends Iterator[Int]` which means that the `next` method must return an Int.\n", "\n", "## Subtyping\n", "Where a given trait is required, a subtype of the trait can be used instead." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Harry\n", "Sally\n" ] }, { "data": { "text/plain": [ "\u001b[32mimport \u001b[39m\u001b[36mscala.collection.mutable.ArrayBuffer\n", "\n", "\u001b[39m\n", "defined \u001b[32mtrait\u001b[39m \u001b[36mPet\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mCat\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mDog\u001b[39m\n", "\u001b[36mdog\u001b[39m: \u001b[32mDog\u001b[39m = ammonite.$sess.cmd3$Helper$Dog@318a3e79\n", "\u001b[36mcat\u001b[39m: \u001b[32mCat\u001b[39m = ammonite.$sess.cmd3$Helper$Cat@190dc9a1\n", "\u001b[36manimals\u001b[39m: \u001b[32mArrayBuffer\u001b[39m[\u001b[32mPet\u001b[39m] = \u001b[33mArrayBuffer\u001b[39m(\n", " ammonite.$sess.cmd3$Helper$Dog@318a3e79,\n", " ammonite.$sess.cmd3$Helper$Cat@190dc9a1\n", ")" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import scala.collection.mutable.ArrayBuffer\n", "\n", "trait Pet {\n", " val name: String\n", "}\n", "\n", "class Cat(val name: String) extends Pet\n", "class Dog(val name: String) extends Pet\n", "\n", "val dog = new Dog(\"Harry\")\n", "val cat = new Cat(\"Sally\")\n", "\n", "val animals = ArrayBuffer.empty[Pet]\n", "animals.append(dog)\n", "animals.append(cat)\n", "animals.foreach(pet => println(pet.name)) // Prints Harry Sally" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `trait Pet` has an abstract field `name` which gets implemented by Cat and Dog in their constructors. On the last line, we call `pet.name` which must be implemented in any subtype of the trait `Pet`.\n", "

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 }