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

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
\n", "\n", "# Class Composition with Mixins\n", "Mixins are traits which are used to compose a class." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I'm an instance of class B\n", "I'M AN INSTANCE OF CLASS B\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mA\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mB\u001b[39m\n", "defined \u001b[32mtrait\u001b[39m \u001b[36mC\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mD\u001b[39m\n", "\u001b[36md\u001b[39m: \u001b[32mD\u001b[39m = ammonite.$sess.cmd0$Helper$D@36fa807" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abstract class A {\n", " val message: String\n", "}\n", "class B extends A {\n", " val message = \"I'm an instance of class B\"\n", "}\n", "trait C extends A {\n", " def loudMessage = message.toUpperCase()\n", "}\n", "class D extends B with C\n", "\n", "val d = new D\n", "println(d.message) // I'm an instance of class B\n", "println(d.loudMessage) // I'M AN INSTANCE OF CLASS B" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Class `D` has a superclass `B` and a mixin `C`. Classes can only have one superclass but many mixins (using the keywords `extends` and `with` respectively). The mixins and the superclass may have the same supertype.\n", "\n", "Now let's look at a more interesting example starting with an abstract class:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mAbsIterator\u001b[39m" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abstract class AbsIterator {\n", " type T\n", " def hasNext: Boolean\n", " def next(): T\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The class has an abstract type `T` and the standard iterator methods.\n", "\n", "Next, we'll implement a concrete class (all abstract members `T`, `hasNext`, and `next` have implementations):" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mStringIterator\u001b[39m" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class StringIterator(s: String) extends AbsIterator {\n", " type T = Char\n", " private var i = 0\n", " def hasNext = i < s.length\n", " def next() = {\n", " val ch = s charAt i\n", " i += 1\n", " ch\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`StringIterator` takes a `String` and can be used to iterate over the String (e.g. to see if a String contains a certain character).\n", "\n", "Now let's create a trait which also extends `AbsIterator`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mRichIterator\u001b[39m" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait RichIterator extends AbsIterator {\n", " def foreach(f: T => Unit): Unit = while (hasNext) f(next())\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This trait implements `foreach` by continually calling the provided function `f: T => Unit` on the next element (`next()`) as long as there are further elements (`while (hasNext)`). Because `RichIterator` is a trait, it doesn't need to implement the abstract members of AbsIterator.\n", "\n", "We would like to combine the functionality of `StringIterator` and `RichIterator` into a single class." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject\u001b[39m \u001b[36mStringIteratorTest\u001b[39m" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "object StringIteratorTest extends App {\n", " class RichStringIter extends StringIterator(\"Scala\") with RichIterator\n", " val richStringIter = new RichStringIter\n", " richStringIter foreach println\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The new class `RichStringIter` has `StringIterator` as a superclass and `RichIterator` as a mixin.\n", "\n", "With single inheritance we would not be able to achieve this level of flexibility.\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 }