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

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
\n", "\n", "# Abstract Types\n", "\n", "Traits and abstract classes can have an abstract type member. This means that the concrete implementations define the actual type. Here's an example:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mBuffer\u001b[39m" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait Buffer {\n", " type T\n", " val element: T\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we have defined an abstract `type T`. It is used to describe the type of `element`. We can extend this trait in an abstract class, adding an upper-type-bound to `T` to make it more specific." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mSeqBuffer\u001b[39m" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abstract class SeqBuffer extends Buffer {\n", " type U\n", " type T <: Seq[U]\n", " def length = element.length\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how we can use yet another abstract `type U` as an upper-type-bound. This `class SeqBuffer` allows us to store only sequences in the buffer by stating that type `T` has to be a subtype of `Seq[U]` for a new abstract type `U`.\n", "\n", "Traits or [classes](classes.ipynb) with abstract type members are often used in combination with anonymous class instantiations. To illustrate this, we now look at a program which deals with a sequence buffer that refers to a list of integers:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "length = 2\n", "content = List(7, 8)\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mIntSeqBuffer\u001b[39m\n", "defined \u001b[32mfunction\u001b[39m \u001b[36mnewIntSeqBuf\u001b[39m\n", "\u001b[36mbuf\u001b[39m: \u001b[32mIntSeqBuffer\u001b[39m = ammonite.$sess.cmd2$Helper$$anon$1@34a80ef9" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abstract class IntSeqBuffer extends SeqBuffer {\n", " type U = Int\n", "}\n", "\n", "\n", "def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer =\n", " new IntSeqBuffer {\n", " type T = List[U]\n", " val element = List(elem1, elem2)\n", " }\n", "val buf = newIntSeqBuf(7, 8)\n", "println(\"length = \" + buf.length)\n", "println(\"content = \" + buf.element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here the factory `newIntSeqBuf` uses an anonymous class implementation of `IntSeqBuf` (i.e. `new IntSeqBuffer`), setting `type T` to a `List[Int]`.\n", "\n", "It is also possible to turn abstract type members into type parameters of classes and vice versa. Here is a version of the code above which only uses type parameters:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "length = 2\n", "content = List(7, 8)\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mBuffer\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mSeqBuffer\u001b[39m\n", "defined \u001b[32mfunction\u001b[39m \u001b[36mnewIntSeqBuf\u001b[39m\n", "\u001b[36mbuf\u001b[39m: \u001b[32mSeqBuffer\u001b[39m[\u001b[32mInt\u001b[39m, \u001b[32mSeq\u001b[39m[\u001b[32mInt\u001b[39m]] = ammonite.$sess.cmd3$Helper$$anon$1@5bd6b04e" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abstract class Buffer[+T] {\n", " val element: T\n", "}\n", "abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] {\n", " def length = element.length\n", "}\n", "\n", "def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] =\n", " new SeqBuffer[Int, List[Int]] {\n", " val element = List(e1, e2)\n", " }\n", "\n", "val buf = newIntSeqBuf(7, 8)\n", "println(\"length = \" + buf.length)\n", "println(\"content = \" + buf.element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we have to use [variance annotations](variances.ipynb) here (`+T <: Seq[U]`) in order to hide the concrete sequence implementation type of the object returned from method `newIntSeqBuf`. Furthermore, there are cases where it is not possible to replace abstract types with type parameters.\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 }