<?xml version="1.0" encoding="UTF-8"?> <chapter annotations="slide" version="5.1" xml:id="sw1ChapterInheritance" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:svg="http://www.w3.org/2000/svg" xmlns:ns="http://docbook.org/ns/transclusion" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:db="http://docbook.org/ns/docbook"> <title>Inheritance</title> <figure xml:id="sd1_inherit_fig_guessWho"> <title>Guess who's inheriting the money</title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/guessWho.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_inherit_fig_biologicalInherit"> <title>Biology and inheritance</title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/biologicalInheritance.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_DuplicateCode"> <title>Duplicate code</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="none">public class Rectangle{ // Center coordinate <emphasis role="red">private double x</emphasis>; <co linkends="sda_inherit_fig_DuplicateCode-1" xml:id="sda_inherit_fig_DuplicateCode-1-co"/> <emphasis role="red">private double y</emphasis>; <emphasis role="red">public move (double dx, double dy){</emphasis> <co linkends="sda_inherit_fig_DuplicateCode-2" xml:id="sda_inherit_fig_DuplicateCode-2-co"/> <emphasis role="red">x += dx; y += dy; }</emphasis> private double width, height; <co linkends="sda_inherit_fig_DuplicateCode-3" xml:id="sda_inherit_fig_DuplicateCode-3-co"/> ... }</programlisting></td> <td valign="top"><programlisting language="none">public class Circle { // Center coordinate <emphasis role="red">private double x</emphasis>; <coref linkend="sda_inherit_fig_DuplicateCode-1-co"/> <emphasis role="red">private double y</emphasis>; <emphasis role="red">public move (double dx, double dy){</emphasis> <coref linkend="sda_inherit_fig_DuplicateCode-2-co"/> <emphasis role="red">x += dx; y += dy; }</emphasis> private double radius; <coref linkend="sda_inherit_fig_DuplicateCode-3-co"/> ... }</programlisting></td> </tr> </informaltable> </figure> <calloutlist> <callout arearefs="sda_inherit_fig_DuplicateCode-1-co" xml:id="sda_inherit_fig_DuplicateCode-1"> <para>The center coordinate (<code language="java">x</code>|<code language="java">y)</code> appears both in <classname>Rectangle</classname> and <classname>Circle</classname>.</para> </callout> <callout arearefs="sda_inherit_fig_DuplicateCode-2-co" xml:id="sda_inherit_fig_DuplicateCode-2"> <para>The move(...) method is being defined <emphasis role="bold">identically</emphasis> both in <classname>Rectangle</classname> and <classname>Circle</classname>!</para> </callout> <callout arearefs="sda_inherit_fig_DuplicateCode-3-co" xml:id="sda_inherit_fig_DuplicateCode-3"> <itemizedlist> <listitem> <para><property>width</property> and <property>height</property> only appear in class <classname>Rectangle</classname>.</para> </listitem> <listitem> <para><property>radius</property> only appears in class Circle.</para> </listitem> </itemizedlist> </callout> </calloutlist> <figure xml:id="sda_inherit_fig_ideaFactorOutCommonCode"> <title>Idea: Centralize common code</title> <itemizedlist> <listitem> <para>Create a parent class <classname>Shape</classname> containing common code portions.</para> </listitem> <listitem> <para>Relate both <classname>Rectangle</classname> and <classname>Circle</classname> to <classname>Shape</classname>.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sda_inherit_fig_shapeCommonSpecific"> <title>Common and specific properties</title> <informaltable border="0"> <colgroup width="40%"/> <colgroup width="20%"/> <colgroup width="40%"/> <tr> <th align="center" colspan="3" valign="top">Common <classname>Rectangle</classname> and <classname>Circle</classname> attributes:</th> </tr> <tr> <td valign="top"/> <td valign="top"><programlisting language="java">double x; double y;</programlisting></td> <td valign="top"/> </tr> <tr> <th align="center" valign="top"><classname>Rectangle</classname> attributes</th> <td/> <th align="center" valign="top"><classname>Circle</classname> attributes</th> </tr> <tr> <td valign="top"><programlisting language="java">double width; double height;</programlisting></td> <td/> <td valign="top"><programlisting language="java">double radius;</programlisting></td> </tr> </informaltable> </figure> <figure xml:id="sda_inherit_fig_shapeInheritPrinciple"> <title>Basic shape inheritance</title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/shapeBasicInherit.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_intro"> <title>Inheritance</title> <itemizedlist> <listitem> <para>Derived classes inherit state and behaviour.</para> </listitem> <listitem> <para>Refinement, specialization.</para> </listitem> <listitem> <para><quote>is-A</quote> relationship:</para> <itemizedlist> <listitem> <para>A rectangle is a shape.</para> </listitem> <listitem> <para>A circle is a shape.</para> </listitem> </itemizedlist> </listitem> </itemizedlist> </figure> <figure xml:id="sda_inherit_fig_shapeSimpleImplement"> <title>Implementing <classname>Shape</classname> hierarchy</title> <informaltable border="0"> <colgroup width="25%"/> <colgroup width="25%"/> <colgroup width="25%"/> <colgroup width="25%"/> <tr> <td valign="top"/> <td colspan="2" valign="top"><programlisting language="java">public class Shape { private double x, y; }</programlisting></td> <td valign="top"/> </tr> <tr> <td colspan="2" valign="top"><programlisting language="java">public class Rectangle extends Shape { private double width; private double height; }</programlisting></td> <td colspan="2" valign="top"><programlisting language="java">public class Circle extends Shape { private double radius; }</programlisting></td> </tr> </informaltable> </figure> <figure xml:id="sda_inherit_fig_createShapesInstances"> <title>Creating instances</title> <programlisting language="java">final double x = 2, y = 3; final Shape shape = new Shape(x, y); final double width = 2, height = 5; final Rectangle r = new Rectangle(x, y , width, height); final double radius = 2.5; final Circle circle = new Circle(x, y , radius);</programlisting> </figure> <figure xml:id="sda_inherit_fig_createShapesConstruct"> <title><classname>Shape</classname> constructor</title> <programlisting language="java">/** * Creating a shape located at center coordinate. * @param x The center's x component. * @param y The center's y component. */ public Shape(double x,double y) { this.x = x; this.y = y; }</programlisting> </figure> <section xml:id="sd1_inherit_sect_inheritToString"> <title>Overriding toString()</title> <figure xml:id="sda_inherit_fig_shapeInstanceLog"> <title><classname>Shape</classname> log info</title> <programlisting language="none">public class Run { static final Logger log = LogManager.getLogger(Run.class); public static void main(String[] args) { final double <emphasis role="red">x = 2.0</emphasis>, <emphasis role="red">y = 3.0</emphasis>; final Shape shape = new Shape(x, y); <emphasis role="red">log.info(shape)</emphasis>; ...</programlisting> <screen>2017... INFO [main] inherit.Run ... - <emphasis role="red">inherit.Shape@37d31475</emphasis></screen> <para>Desired:</para> <screen>2017... INFO [main] inherit.Run ... - <emphasis role="red">(2.0|3.0)</emphasis></screen> </figure> <figure xml:id="sda_inherit_fig_shapeToStringRedefine"> <title>Overwriting <methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString--">toString()</methodname></title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/shapeOverrideToStringIde.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_shapeExtendObject"> <title><classname>Shape</classname> extending <classname>Object</classname></title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/shapeRedefineToString.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_createRectangle"> <title>Creating <classname>Rectangle</classname> instances</title> <programlisting language="java">final Rectangle r = new Rectangle(x, y <co linkends="sda_inherit_fig_createRectangle-1" xml:id="sda_inherit_fig_createRectangle-1-co"/>, width, height <co linkends="sda_inherit_fig_createRectangle-2" xml:id="sda_inherit_fig_createRectangle-2-co"/>);</programlisting> <calloutlist> <callout arearefs="sda_inherit_fig_createRectangle-1-co" xml:id="sda_inherit_fig_createRectangle-1"> <para>Center coordinate components <quote>belonging</quote> to superclass <classname>Shape</classname>.</para> </callout> <callout arearefs="sda_inherit_fig_createRectangle-2-co" xml:id="sda_inherit_fig_createRectangle-2"> <para>width and height <quote>belonging</quote> to class <classname>Rectangle</classname>.</para> </callout> </calloutlist> <para>Solution: Nested constructor call.</para> </figure> <figure xml:id="sda_inherit_fig_RectangleConstructor"> <title><classname>Rectangle</classname> constructor</title> <programlisting language="java">/** * Creating a rectangle at (x|y) of given width and height. * @param x Center's x component. * @param y Center's y component. * @param width Rectangle's width. * @param height Rectangle's height. */ public Rectangle(double x, double y, double width, double height) { super(x, y) <co linkends="sda_inherit_fig_RectangleConstructor-1" xml:id="sda_inherit_fig_RectangleConstructor-1-co"/>; this.width = width; this.height = height <co linkends="sda_inherit_fig_RectangleConstructor-2" xml:id="sda_inherit_fig_RectangleConstructor-2-co"/>; }</programlisting> </figure> <calloutlist> <callout arearefs="sda_inherit_fig_RectangleConstructor-1-co" xml:id="sda_inherit_fig_RectangleConstructor-1"> <para>Passing center coordinate to superclass constructor.</para> <note> <para>This must be the first statement.</para> </note> </callout> <callout arearefs="sda_inherit_fig_RectangleConstructor-2-co" xml:id="sda_inherit_fig_RectangleConstructor-2"> <para>Processing <code language="java">width</code> and <code language="java">height</code> in <quote>current</quote> subclass.</para> </callout> </calloutlist> <figure xml:id="sda_inherit_fig_RectangleLogging"> <title>Logging <classname>Rectangle</classname> instances</title> <programlisting language="java">public class Run { static final Logger log = LogManager.getLogger(Run.class); public static void main(String[] args) { final double x = 2, y = 3, <emphasis role="red">width = 3.</emphasis>, <emphasis role="red">height = 4.</emphasis>; final Rectangle r = new Rectangle(x, y, width, height); log.info(r); ...</programlisting> <screen>2017 ... INFO [main] inherit.Run (Run.java:15) - (2.0|3.0)</screen> <para>Desired:</para> <screen>2017 ... INFO Rectangle at (2.0|3.0), <emphasis role="red">width= 3.0</emphasis>, <emphasis role="red">height=4.0</emphasis></screen> </figure> <figure xml:id="sda_inherit_fig_RectangleOverrideToStringAgain"> <title>Override <methodname>toString()</methodname> again.</title> <programlisting language="java">public class Rectangle extends Shape { @Override public String toString() { return "Rectangle at " + super.toString() <co linkends="sda_inherit_fig_RectangleOverrideToStringAgain-1" xml:id="sda_inherit_fig_RectangleOverrideToStringAgain-1-co"/> + ", width= " + width + ", height=" + height; <co linkends="sda_inherit_fig_RectangleOverrideToStringAgain-2" xml:id="sda_inherit_fig_RectangleOverrideToStringAgain-2-co"/> } ...</programlisting> <calloutlist> <callout arearefs="sda_inherit_fig_RectangleOverrideToStringAgain-1-co" xml:id="sda_inherit_fig_RectangleOverrideToStringAgain-1"> <para>Include center coordinate from superclass: The <code language="java">super</code> keyword will call the superclass' method <methodname>toString()</methodname>.</para> </callout> <callout arearefs="sda_inherit_fig_RectangleOverrideToStringAgain-2-co" xml:id="sda_inherit_fig_RectangleOverrideToStringAgain-2"> <para>Append <quote>own</quote> <code language="java">width</code> and <code language="java">height</code> attributes.</para> </callout> </calloutlist> </figure> <figure xml:id="sda_inherit_fig_rectangleExtendShapa"> <title><classname>Rectangle</classname> extending <classname>Shape´</classname></title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/rectangleRedefineToString.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_CircleComplete"> <title>Implementing <classname>Circle</classname></title> <programlisting language="java">public class Circle extends Shape { /** * Creating a circle of given center and radius * @param x Center's x component. * @param y Center's y component. * @param radius The circle's radius. */ public Circle(double x,double y, double radius) { super(x, y); this.radius = radius; } @Override public String toString() { return "Circle at " + super.toString() +", radius= " + radius; } private double radius; }</programlisting> </figure> <figure xml:id="sda_inherit_fig_shapeToString"> <title><classname>Shape</classname> and <methodname>toString()</methodname></title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/shapeToString.svg"/> </imageobject> </mediaobject> </figure> </section> <section xml:id="sd1_inherit_sect_final"> <title><code language="java">final</code> methods</title> <figure xml:id="sda_inherit_fig_moveRectangle"> <title>Moving <classname>Shape</classname> instances</title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/rectangleTranslate.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_shapeMove"> <title>Implementing <classname>Shape</classname> movements</title> <programlisting language="java">public class Shape { /** * Move by a given translation vector * @param xTrans Translation's x component * @param yTrans Translation's y component */ public void move(final int xTrans, final int yTrans) { x += xTrans; y += yTrans; } ...</programlisting> </figure> <figure xml:id="sda_inherit_fig_shapeMoveProblemSubclass"> <title>Fools are everywhere!</title> <programlisting language="java">public class Rectangle extends Shape { @Override public void move(int xTrans, int yTrans) { // I'm so dumb! ... }</programlisting> </figure> <figure xml:id="sda_inherit_fig_shapeMoveProblemSubclassSolution"> <title>Solution: <emphasis role="red">final</emphasis> prevents overriding</title> <programlisting language="none">public abstract class Shape { ... public <emphasis role="red">final</emphasis> void move(final int xTrans, final int yTrans) { x += xTrans; y += yTrans; }...</programlisting> <programlisting language="java">public class Rectangle extends Shape { // Syntax error: 'move(int, int)' cannot override // 'move(int, int)' in 'inherit.Shape'; overridden method is final @Override public void move(int xTrans, int yTrans) {...</programlisting> </figure> </section> <section xml:id="sd1_inherit_sect_abstractMethods"> <title>Abstract methods</title> <figure xml:id="sda_inherit_fig_subclassImplementGetArea"> <title>Calculating a shape's area</title> <informaltable border="0"> <tr> <td valign="top"><programlisting language="java">public class Rectangle extents Shape { /** * Calculate the area. * @return The rectangle's area */ public double getArea() { return width * height; }</programlisting></td> <td valign="top"><programlisting language="java">public class Circle extents Shape { /** * Calculate the area. * @return The circle's area */ public double getArea() { return Math.PI * radius * radius; }</programlisting></td> </tr> </informaltable> </figure> <figure xml:id="sda_inherit_fig_shapePolymorphicAreaCall"> <title>Desired: Polymorphic <methodname>getArea()</methodname> call</title> <programlisting language="none">final Shape[] shapes <co linkends="sda_inherit_fig_shapePolymorphicAreaCall-1" xml:id="sda_inherit_fig_shapePolymorphicAreaCall-1-co"/> = { new Circle(1, 1, 2.) <co linkends="sda_inherit_fig_shapePolymorphicAreaCall-2" xml:id="sda_inherit_fig_shapePolymorphicAreaCall-2-co"/>, new Rectangle(1, -1, 2., 3.)<coref linkend="sda_inherit_fig_shapePolymorphicAreaCall-2-co"/>}; for (final Shape s : shapes) { System.out.println(s.toString() + ": <emphasis role="red">area = " + s.getArea()</emphasis>); <co linkends="sda_inherit_fig_shapePolymorphicAreaCall-3" xml:id="sda_inherit_fig_shapePolymorphicAreaCall-3-co"/> }</programlisting> <screen>Circle at (1.0|1.0), radius= 2.0: <emphasis role="red">area = 12.566370614359172</emphasis> Rectangle at (1.0|-1.0), width= 2.0, height=3.0: <emphasis role="red">area = 6.0</emphasis></screen> </figure> <calloutlist> <callout arearefs="sda_inherit_fig_shapePolymorphicAreaCall-1-co" xml:id="sda_inherit_fig_shapePolymorphicAreaCall-1"> <para>An array of <classname>Shape</classname> references.</para> </callout> <callout arearefs="sda_inherit_fig_shapePolymorphicAreaCall-2-co" xml:id="sda_inherit_fig_shapePolymorphicAreaCall-2"> <para>A <classname>Rectangle</classname> <quote>is a</quote> <classname>Shape</classname> and likewise is <classname>Circle</classname>. We can thus assign both <classname>Circle</classname> and <classname>Rectangle</classname> instances to variables (or array elements) of type <classname>Shape</classname> by means of upcasting.</para> </callout> <callout arearefs="sda_inherit_fig_shapePolymorphicAreaCall-3-co" xml:id="sda_inherit_fig_shapePolymorphicAreaCall-3"> <para>Polymorphic dispatch: Depending on the object's type the <xref linkend="glo_Java"/> runtime will automatically choose either <classname>Rectangle</classname>.<methodname>toString()</methodname>/ <classname>Rectangle</classname>.<methodname>getArea()</methodname> or <classname>Circle</classname>.<methodname>getArea()</methodname> / <classname>Circle</classname>.<methodname>toString()</methodname>/ <classname>Circle</classname>.<methodname>getArea()</methodname> respectively.</para> </callout> </calloutlist> <figure xml:id="sda_inherit_fig_getAreaPolymorphicProblem"> <title>Problems:</title> <itemizedlist> <listitem> <para>No meaningful <methodname>getArea()</methodname> method in class <classname>Shape</classname> possible.</para> </listitem> <listitem> <para>Meaningful implementations exist both in subclass<classname> Rectangle</classname> and <classname>Circle</classname>.</para> </listitem> </itemizedlist> <para>Solution: Abstract method <methodname>getArea()</methodname> in superclass <classname>Shape</classname>.</para> </figure> <figure xml:id="sda_inherit_fig_implementAbstractGetArea"> <title><code language="java">abstract</code> method <methodname>getArea()</methodname></title> <informaltable border="0"> <colgroup width="25%"/> <colgroup width="25%"/> <colgroup width="25%"/> <colgroup width="25%"/> <tr> <td valign="top"/> <td colspan="2" valign="top"><programlisting language="java">abstract<co linkends="sda_inherit_fig_implementAbstractGetArea-1" xml:id="sda_inherit_fig_implementAbstractGetArea-1-co"/> public class Shape { /** * Calculate the shape's area. * @return The shape's area */ abstract<co linkends="sda_inherit_fig_implementAbstractGetArea-2" xml:id="sda_inherit_fig_implementAbstractGetArea-2-co"/> public double getArea()<co linkends="sda_inherit_fig_implementAbstractGetArea-3" xml:id="sda_inherit_fig_implementAbstractGetArea-3-co"/>; ...</programlisting></td> <td valign="top"/> </tr> <tr> <td colspan="2" valign="top"><programlisting language="java">public class Rectangle extends Shape { @Override<co linkends="sda_inherit_fig_implementAbstractGetArea-4" xml:id="sda_inherit_fig_implementAbstractGetArea-4-co"/> public double getArea() { return width * height; }...</programlisting></td> <td colspan="2" valign="top"><programlisting language="java">public class Circle ... { @Override<coref linkend="sda_inherit_fig_implementAbstractGetArea-4-co"/> public double getArea() { return Math.PI * radius * radius; } ...</programlisting></td> </tr> </informaltable> </figure> <calloutlist> <callout arearefs="sda_inherit_fig_implementAbstractGetArea-1-co" xml:id="sda_inherit_fig_implementAbstractGetArea-1"> <para>Superclass <classname>Shape</classname> contains an <code language="java">abstract</code> method and must thus itself be declared <code language="java">abstract</code> as well.</para> <note> <para>You cannot create instances of <code language="java">abstract</code> classes. You may however create instances of derived non-<code language="java">abstract</code> classes.</para> </note> </callout> <callout arearefs="sda_inherit_fig_implementAbstractGetArea-2-co" xml:id="sda_inherit_fig_implementAbstractGetArea-2"> <para>Method <classname>getArea()</classname> cannot be implemented in a meaningful way. Its <code language="java">abstract</code> modifier is a promise that some concrete (= non-<code language="java">abstract</code>) subclass will either offer an implementation of <classname>getArea()</classname> or will have an intermediate parent class doing so.</para> <para>In other words: The <code language="java">abstract</code> keyword requires a corresponding implementation in some derived non-<code language="java">abstract</code> subclass.</para> </callout> <callout arearefs="sda_inherit_fig_implementAbstractGetArea-3-co" xml:id="sda_inherit_fig_implementAbstractGetArea-3"> <para>An <code language="java">abstract</code> method must not have an implementing body <code language="java">{...}</code>.</para> </callout> <callout arearefs="sda_inherit_fig_implementAbstractGetArea-4-co" xml:id="sda_inherit_fig_implementAbstractGetArea-4"> <para>Both <classname>Rectangle</classname> and <classname>Circle</classname> are concrete (=non-<code language="java">abstract</code>) classes and are thus obliged to provide an implementation of <methodname>double getArea()</methodname>.</para> </callout> </calloutlist> <figure xml:id="sda_inherit_fig_shapeGetAreaAbstract"> <title><code language="java">abstract</code> method <methodname>getArea()</methodname></title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/shapeGetAreaAbstract.multi.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sda_inherit_fig_whatIsShapeAnyway"> <title>What's a <quote>shape</quote> anyway?</title> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/ghost.svg"/> </imageobject> </mediaobject> </figure> <figure xml:id="sd1_inherit_fig_noAbstractInstances"> <title>No instances of <code language="java">abstract</code> classes.</title> <programlisting language="none">final Shape s = new Shape(1., 2.); // <emphasis role="red">'Shape' is abstract; cannot be instantiated</emphasis></programlisting> </figure> <figure xml:id="sd1_inherit_fig_getAreaCompulsory"> <title>Mandatory <methodname>getArea()</methodname> implementation.</title> <programlisting language="none">// Error: <emphasis role="red">Class 'Circle' must either be declared abstract or</emphasis> // <emphasis role="red">implement abstract method 'getArea()' in 'Shape'</emphasis> public class Circle extends Shape { public Circle(double x,double y, double radius) { super(x, y); this.radius = radius; } private double radius; }</programlisting> </figure> <figure xml:id="sd1_inherit_fig_abstractFacts"> <title>Facts about <code language="java">abstract</code> fields, methods and classes.</title> <itemizedlist> <listitem> <para>A class containing an <code language="java">abstract</code> method must itself be declared <code language="java">abstract</code>.</para> </listitem> <listitem> <para><code language="java">abstract</code> classes are allowed to host non-<code language="java">abstract</code> methods.</para> </listitem> <listitem> <para>A class may be declared <code language="java">abstract</code> irrespective of purely containing non-<code language="java">abstract</code> methods.</para> </listitem> </itemizedlist> </figure> <section xml:id="sw1SectGeometryInherit"> <title>Geometry classes reconsidered</title> <para>Starting in <xref linkend="sd1_qanda_geometry_Rectangle"/> you implemented rectangle and circle related classes. You may have observed translation related parameters to be shape independent.</para> <para>Our <classname>Rectangle</classname> and <classname>Circle</classname> instances are being described by <code language="java">width</code>, <code language="java">height</code> and <code>radius</code>. Implementing a drawing application requires <emphasis>arbitrary</emphasis> objects to be moved from one position to another e.g.:</para> <figure xml:id="sd1FigFigureMove"> <title>Moving shapes</title> <mediaobject> <imageobject> <imagedata fileref="Ref/Fig/figureMove.fig"/> </imageobject> </mediaobject> </figure> <para>We might implement translation (move) related parameters and methods in both classes Rectangle and Circle independently. But since the underlying operation is indeed shape independent we choose a different approach using inheritance in the subsequent exercises.</para> <qandaset defaultlabel="qanda" xml:id="sd1QandaFigureBaseClass"> <title>Defining a <classname>Shape</classname> class hierarchy</title> <qandadiv> <qandaentry> <question> <para>Our two geometric primitives circle and rectangle will need a reference point (x,y) in order to allow for defining <methodname>move(...)</methodname> operations. We choose the center of gravity both for rectangles and circles:</para> <mediaobject> <imageobject> <imagedata fileref="Ref/Fig/figureCenter.fig"/> </imageobject> </mediaobject> <para>We thus need two additional parameters <property>x</property> and <property>y</property> representing an object's position. Consider the following inheritance diagram and implement the three <xref linkend="glo_Java"/> classes <classname>Shape</classname>, <classname>Circle</classname> and <classname>Rectangle</classname>:</para> <mediaobject> <imageobject> <imagedata fileref="Ref/Inherit/qandaShapeBasic.svg"/> </imageobject> </mediaobject> <para>Create unit tests checking for correct implementation of the translation example from <xref linkend="sd1FigFigureMove"/> and the all other methods.</para> <para>Why is <methodname>move(...)</methodname> being defined returning an instance of class <classname>Shape</classname>?</para> <tip> <para>Think of multiple / combined operations.</para> </tip> </question> <answer> <annotation role="make"> <para role="eclipse">Sd1/Figure/BaseClass</para> </annotation> <para>Defining <methodname>move(...)</methodname> returning an instance of class <classname>Shape</classname> allows for operation chaining:</para> <programlisting language="java">final Circle c = new Circle(1., 2., 3.0); double p = c.move(1, 5).move(-3, 7).getPrimeter();</programlisting> </answer> </qandaentry> </qandadiv> </qandaset> <section xml:id="sd1SectFigureScale"> <title>Scaling shapes</title> <qandaset defaultlabel="qanda" xml:id="sd1QandaFigureScaling"> <qandadiv> <qandaentry> <question> <para>We want shapes to be scalable:</para> <mediaobject> <imageobject> <imagedata fileref="Ref/Fig/figureScale.fig"/> </imageobject> </mediaobject> <para>In the above example the rectangle is being <quote>inflated</quote> whereas the circle is being shrunk to half its original size. Add a corresponding <methodname>scale(...)</methodname> method to the <classname>Shape</classname> inheritance hierarchy which allows for operation chaining as well.</para> <para>Provide appropriate unit tests.</para> </question> <answer> <annotation role="make"> <para role="eclipse">Sd1/Figure/Scale</para> </annotation> <para>This task is pretty much straightforward. Since scaling requires specific details (like radius or width and height) a <methodname>scale()</methodname> method cannot be implemented on top level of our inheritance hierarchy. We thus start by defining an abstract method in our class <classname>Shape</classname>:</para> <programlisting language="java">/** * * @param factor Scale the current shape by this value. * @return The current object. */ public abstract Shape scale(double factor);</programlisting> <para>This method has to be implemented in our two concrete classes <classname>Circle</classname> and <classname>Rectangle</classname>.</para> <para>Sensible unit tests may be based on the observation that:</para> <itemizedlist> <listitem> <para>A shape's perimeter grows linear with the scaling factor.</para> </listitem> <listitem> <para>A shape's area grows linear with the scaling factor's square.</para> </listitem> </itemizedlist> </answer> </qandaentry> </qandadiv> </qandaset> </section> <section xml:id="sd1SectFigureToString"> <title>Providing <methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString--">toString()</methodname> methods</title> <qandaset defaultlabel="qanda" xml:id="sd1QandaFigureToString"> <qandadiv> <qandaentry> <question> <para>Consider:</para> <programlisting language="java"> final Circle c = new Circle(-2, -1, 3.5); final Rectangle r = new Rectangle(3, 1, 1.5, 4.4); System.out.println(c); System.out.println(r);</programlisting> <para>This creates the following output:</para> <screen>de.hdm_stuttgart.mi.sd1.shape.model.Circle@659e0bfd de.hdm_stuttgart.mi.sd1.shape.model.Rectangle@2a139a55</screen> <para>This result is due to the invocation of the <methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString--">toString()</methodname> method being defined in the <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html">Object</classname> superclass. Override this method in <classname>Shape</classname>, <classname>Circle</classname> and <classname>Rectangle</classname> accordingly to get:</para> <screen>Circle (-2.0,-1.0), radius=3.5 Rectangle (3.0,1.0), width=1.5, height=4.4</screen> <para>Provide appropriate unit tests.</para> <tip> <para>You may access <methodname>Shape.toString()</methodname> from derived classes by using <code language="java">super()</code>.</para> </tip> </question> <answer> <annotation role="make"> <para role="eclipse">Sd1/Figure/ToString</para> </annotation> </answer> </qandaentry> </qandadiv> </qandaset> </section> </section> </section> <section xml:id="sd1_inherit_sect_protected"> <title><code language="java">protected</code> access</title> <figure xml:id="sda_inherit_fig_protectedCreationTime"> <title><code language="java">protected</code> access</title> <programlisting language="none"><emphasis role="red">package model</emphasis>; public abstract class Shape <co linkends="sda_inherit_fig_protectedCreationTime-1" xml:id="sda_inherit_fig_protectedCreationTime-1-co"/>{ final protected long <emphasis role="red">creationTime</emphasis> <co linkends="sda_inherit_fig_protectedCreationTime-2" xml:id="sda_inherit_fig_protectedCreationTime-2-co"/>= System.nanoTime(); ... } ------------------------------------------------ <emphasis role="red">package model.sub</emphasis>; public class Rectangle <co linkends="sda_inherit_fig_protectedCreationTime-3" xml:id="sda_inherit_fig_protectedCreationTime-3-co"/>extends Shape { static final Logger log = LogManager.getLogger(Rectangle.class); @Override public double getArea() { log.info("Rectangle creation time:" + <emphasis role="red">creationTime</emphasis> <co linkends="sda_inherit_fig_protectedCreationTime-4" xml:id="sda_inherit_fig_protectedCreationTime-4-co"/>); return width * height; } ... }</programlisting> </figure> <calloutlist> <callout arearefs="sda_inherit_fig_protectedCreationTime-1-co" xml:id="sda_inherit_fig_protectedCreationTime-1"> <para>Defining superclass <classname>Shape</classname> in package <code language="java">model</code>.</para> </callout> <callout arearefs="sda_inherit_fig_protectedCreationTime-2-co" xml:id="sda_inherit_fig_protectedCreationTime-2"> <para>Defining a <code language="java">protected</code> instance attribute <property>creationTime</property> in superclass <classname>Shape</classname>.</para> </callout> <callout arearefs="sda_inherit_fig_protectedCreationTime-3-co" xml:id="sda_inherit_fig_protectedCreationTime-3"> <para>Deriving class <classname>Rectangle</classname> in different package <code language="java">model.sub</code> from superclass <code language="java">Shape</code>.</para> </callout> <callout arearefs="sda_inherit_fig_protectedCreationTime-4-co" xml:id="sda_inherit_fig_protectedCreationTime-4"> <para>Accessing superclass attribute <property>creationTime</property> across package boundary.</para> </callout> </calloutlist> <qandaset defaultlabel="qanda" xml:id="sd1_qanda_protectedPackagePrivate"> <title><code language="java">protected</code> vs. <quote>package private</quote></title> <qandadiv> <qandaentry> <question> <para>Implement <xref linkend="sda_inherit_fig_protectedCreationTime"/> in your IDE but exchanging <classname>Rectangle</classname> by <classname>Circle</classname> accordingly. Then execute <classname>Circle</classname>.<methodname>getArea()</methodname> and watch the logging outcome.</para> <para>Now apply the following two changes:</para> <orderedlist> <listitem> <para>Remove <code language="java">creationTime</code>'s <code language="java">protected</code> modifier.</para> </listitem> <listitem> <para>Move class <classname>Circle</classname> to package <package>model</package>.</para> </listitem> </orderedlist> <para>Are you still able to run your code? Explain the result.</para> </question> <answer> <para>Implementing class <classname>Circle</classname> is straightforward:</para> <programlisting language="none">public class Circle extends Shape { static final Logger log = LogManager.getLogger(Circle.class); @Override public double getArea() { log.info("Circle creation time:" + creationTime ); return Math.PI * radius * radius; } private double radius; }</programlisting> <para>Executing <code language="java">new Circle().getArea()</code> results in:</para> <screen>2018-01-03 08:31:18,811 INFO [main] sup.Circle (Circle.java:11) - Circle creation time:3340216708709</screen> <para>Removing <code language="java">protected</code> yields a compile time error:</para> <programlisting language="none">public class Circle extends Shape { static final Logger log = LogManager.getLogger(Circle.class); @Override public double getArea() { <emphasis role="red">// Error: 'creationTime' is not public in 'testprotect.model.Shape'. // Cannot be accessed from outside package</emphasis> log.info("Circle creation time:" + creationTime ); ...</programlisting> <para>Moving class <classname>Circle</classname> to package <package>model</package> resolves the issue: Having both classes <classname>Shape</classname> and <classname>Circle</classname> in a common package <package>model</package> implies <quote>package private</quote> access. Thus every method in <classname>Circle</classname> has full access to all fields and methods of <classname>Shape</classname>.</para> <note> <para>In a production environment this may or may not be desired and is thus a design choice.</para> </note> </answer> </qandaentry> </qandadiv> </qandaset> <qandaset defaultlabel="qanda" xml:id="sd1_qanda_protectedAccessProblem"> <title><code language="java">protected</code> access involving different instances</title> <qandadiv> <qandaentry> <question> <para>We reconsider class <classname>AlphaSub</classname> from <xref linkend="sd1_qanda_oracleAccessExample"/>:</para> <programlisting language="java">package package_two; import package_one.Alpha; public class AlphaSub extends Alpha { void dummy(/* Inherited */) { int v; v = attribPublic; v = attribProtected; v = <emphasis role="red">attribDefault</emphasis>; v = <emphasis role="red">attribPrivate</emphasis>; } }</programlisting> <para>If we try to access a different instance's <code language="java">attribProtected</code> we fail:</para> <programlisting language="java">package package_two; import package_one.Alpha; public class AlphaSub extends Alpha { void dummy(final Alpha a) { int v; ... v = a.<emphasis role="red">attribProtected</emphasis>; ... } }</programlisting> <para>Why is this access prohibited? Both the calling instance and the argument <code language="java">a</code> still belong to the <classname>Alpha</classname> / <classname>AlphaSum</classname> class hierarchy. Explain the different behavior.</para> </question> <answer> <para><code language="java">protected</code> inherited access grants an instance's subclass method access to a superclass attribute or method involving one common instance.</para> <para>The current use case differs: We have two (presumably different) instances one trying to access another's <code language="java">protected</code> attribute. This does not involve inheritance at all and thus fails.</para> </answer> </qandaentry> </qandadiv> </qandaset> </section> <section xml:id="sd1_sect_finalClass"> <title><code language="java">final</code> classes</title> <figure xml:id="sd1_inherit_fig_finalClass"> <title><code language="java">final</code> classes</title> <programlisting language="java">public final class Shape { ... } ------------------------- public class Rectangle extends Shape { // Error: final class cannot be extended ...</programlisting> </figure> <figure xml:id="sd1_inherit_fig_finalClassRatio"> <title><code language="java">final</code> classes, rationale</title> <itemizedlist> <listitem> <para>Design decision.</para> </listitem> <listitem> <para>Slight performance gain.</para> </listitem> </itemizedlist> <note> <para>Prominent Example: <classname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/String.html">java.lang.String</classname>.</para> </note> </figure> </section> <section xml:id="sd1_inherit_sect_instanceOf"> <title>The <code language="java" xlink:href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2">instanceof</code> operator</title> <figure xml:id="sd1_inherit_fig_defeatPolymorphism"> <title><quote>Defeating</quote> polymorphism</title> <informaltable border="0"> <colgroup width="68%"/> <colgroup width="32%"/> <tr> <td valign="top"><programlisting language="java">public static void main(String[] args) { final Shape[] shapes = { new Circle(1, 1, 2.), new Rectangle(1, -1, 2., 3.)}; print(shapes); } static void print(final Shape[] shapes) { for (final Shape s : shapes) { if (s <link xlink:href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2">instanceof</link> Rectangle) { System.out.println("Type Rectangle"); } else if (s <link xlink:href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2">instanceof</link> Circle) { System.out.println("Type Circle"); } } }</programlisting></td> <td valign="top"><screen>Type Circle Type Rectangle</screen></td> </tr> </informaltable> </figure> <figure xml:id="sd1_inherit_fig_shapeDefineEquals"> <title>Defining <methodname>equals(...)</methodname> of <classname>Shape</classname> instances</title> <para>Two <classname>Shape</classname> instances shall be considered equal if:</para> <itemizedlist> <listitem> <para>Both instances are of common type <abbrev>i.e.</abbrev> either <classname>Rectangle</classname> or <classname>Circle</classname>.</para> </listitem> <listitem> <para>Their center coordinates match within a threshold of <inlineequation> <m:math display="inline"> <m:msup> <m:mi>10</m:mi> <m:mrow> <m:mo>-</m:mo> <m:mi>15</m:mi> </m:mrow> </m:msup> </m:math> </inlineequation>.</para> </listitem> <listitem> <para><code language="java">width</code> and <code language="java">height</code> or <code language="java">radius</code> match within a threshold of <inlineequation> <m:math display="inline"> <m:msup> <m:mi>10</m:mi> <m:mrow> <m:mo>-</m:mo> <m:mi>15</m:mi> </m:mrow> </m:msup> </m:math> </inlineequation>.</para> </listitem> </itemizedlist> </figure> <figure xml:id="sd1_inherit_fig_equalByCoordinate"> <title>Comparing center coordinates</title> <programlisting language="java">public abstract class Shape { private double x, y; protected boolean equalCenter(final Shape o) { return Math.abs(o.x - x) + Math.abs(o.y - y) < 1.E-15; } ...</programlisting> </figure> <figure xml:id="sd1_inherit_fig_rectangleEquals"> <title>Implementing <classname>Rectangle</classname>.<methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#equals-java.lang.Object-">equals()</methodname></title> <programlisting language="java">public class Rectangle extends Shape { @Override public boolean equals(Object o) { if (o <link xlink:href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2">instanceof</link> Rectangle){ final Rectangle oRectangle = (Rectangle) o; return super.equalCenter(oRectangle) && Math.abs(oRectangle.width- width) + Math.abs(oRectangle.height- height) < 1.E-15; } return false; } ...</programlisting> <remark>if <code language="java">o == null</code> then <code language="java">o <link xlink:href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2">instanceof</link> Rectangle</code> evaluates to <code language="java">false</code>.</remark> </figure> <figure xml:id="sd1_inherit_fig_circleEquals"> <title>Implementing <classname>Circle</classname>.<methodname xlink:href="https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#equals-java.lang.Object-">equals()</methodname></title> <programlisting language="java">public class Circle extends Shape { @Override public boolean equals(final Object o) { if (o <link xlink:href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2">instanceof</link> Circle){ final Circle oCircle = (Circle) o; return super.equalCenter(o) && Math.abs(oCircle.radius - radius) < 1.E-15; } return false; } ...</programlisting> </figure> <figure xml:id="sd1_inherit_fig_testShapeEquals"> <title>Testing equality of <classname>Shape</classname> objects</title> <informaltable border="0"> <colgroup width="72%"/> <colgroup width="28%"/> <tr> <td valign="top"><programlisting language="java">final Rectangle r1 = new Rectangle(2, 3, 1,4), r2 = new Rectangle(2, 3, 2,8), r3 = new Rectangle(2, 3, 1,4); final Circle c = new Circle(2,3, 7); System.out.println("r1.equals(r2): " + r1.equals(r2)); System.out.println("r1.equals(r3): " + r1.equals(r3)); System.out.println("c.equals(r1): " + c.equals(r1));</programlisting></td> <td valign="top"><screen>r1.equals(r2): false r1.equals(r3): true c.equals(r1): false</screen></td> </tr> </informaltable> </figure> </section> </chapter>