From 0a199b4f7ece069fd931d82d75d01fd0a97303e3 Mon Sep 17 00:00:00 2001 From: Martin Goik <goik@hdm-stuttgart.de> Date: Wed, 10 Jan 2018 11:25:47 +0100 Subject: [PATCH] refining equals() --- Doc/Sd1/inheritance.xml | 258 +++++++++++++++++++++++++++++----------- 1 file changed, 191 insertions(+), 67 deletions(-) diff --git a/Doc/Sd1/inheritance.xml b/Doc/Sd1/inheritance.xml index 6b162c21c..b53d71c42 100644 --- a/Doc/Sd1/inheritance.xml +++ b/Doc/Sd1/inheritance.xml @@ -29,8 +29,9 @@ }</programlisting></td> <td valign="top"><programlisting language="none">public class Circle { - // Center coordinate <coref linkend="sda_inherit_fig_codeDuplicate-1-co"/> - private <emphasis role="red">double x</emphasis>; + // Center coordinate + private <emphasis role="red">double x</emphasis>; <coref + linkend="sda_inherit_fig_codeDuplicate-1-co"/> private <emphasis role="red">double y</emphasis>; private double radius; <co linkends="sda_inherit_fig_codeDuplicate-3" @@ -437,15 +438,24 @@ public Rectangle(double x, double y, <programlisting language="java">public class Rectangle extends Shape { @Override public void move(int xTrans, int yTrans) { // I'm so dumb! + ... }</programlisting> + </figure> - <para>Solution: Prohibit overriding <methodname>move()</methodname> in - superclass <code language="java">Shape</code>:</para> + <figure xml:id="sda_inherit_fig_shapeMoveProblemSubclassSolution"> + <title>Solution: <emphasis role="red">final</emphasis> prevents + overriding</title> - <programlisting language="none">... public <emphasis role="red">final</emphasis> void move(final int xTrans, final int yTrans) { - x += xTrans; - y += yTrans; - } ...</programlisting> + <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> @@ -457,20 +467,20 @@ public Rectangle(double x, double y, <informaltable border="0"> <tr> - <td valign="top"><programlisting language="java"> /** + <td valign="top"><programlisting language="java">public class Rectangle extents Shape { + /** * Calculate the area. * @return The rectangle's area */ - @Override public double getArea() { return width * height; }</programlisting></td> - <td valign="top"><programlisting language="java"> /** + <td valign="top"><programlisting language="java">public class Circle extents Shape { + /** * Calculate the area. * @return The circle's area */ - @Override public double getArea() { return Math.PI * radius * radius; }</programlisting></td> @@ -479,19 +489,22 @@ public Rectangle(double x, double y, </figure> <figure xml:id="sda_inherit_fig_shapePolymorphicAreaCall"> - <title>Wanted: Polymorphic <methodname>getArea()</methodname> + <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 + 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 + 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>); + 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> @@ -511,28 +524,34 @@ Rectangle at (1.0|-1.0), width= 2.0, height=3.0: <emphasis role="red">area = 6.0 <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 upcasting.</para> + <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>Problem</title> + <title>Problems:</title> <itemizedlist> <listitem> - <para>No such <methodname>getArea()</methodname> method in class - <classname>Shape</classname>.</para> + <para>No meaningful <methodname>getArea()</methodname> method in + class <classname>Shape</classname> possible.</para> </listitem> <listitem> - <para>No meaningful implementation in - <classname>Shape</classname>.</para> - </listitem> - - <listitem> - <para>Meaningful implementations in both subclasses - <classname>Rectangle</classname> and - <classname>Circle</classname>.</para> + <para>Meaningful implementations exist both in subclass<classname> + Rectangle</classname> and <classname>Circle</classname>.</para> </listitem> </itemizedlist> @@ -595,20 +614,27 @@ Rectangle at (1.0|-1.0), width= 2.0, height=3.0: <emphasis role="red">area = 6.0 <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 any concrete (= non-<code + 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 a - parent class doing so.</para> + implementation of <classname>getArea()</classname> or will have an + intermediate parent class doing so.</para> - <para>The <code language="java">abstract</code> keyword requires a - corresponding implementation in any derived non-<code - language="java">abstract</code> subclass.</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" @@ -923,7 +949,8 @@ public abstract class Shape <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" @@ -935,36 +962,37 @@ public class Rectangle <co linkends="sda_inherit_fig_protectedCreationTime-3" linkends="sda_inherit_fig_protectedCreationTime-4" xml:id="sda_inherit_fig_protectedCreationTime-4-co"/>); return width * height; - } ... }</programlisting> + } ... +}</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> + <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-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-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> - </figure> + <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 @@ -1159,14 +1187,15 @@ public class Rectangle <tr> <td valign="top"><programlisting language="java">public static void main(String[] args) { - final Shape[] shapes = {new Circle(1, 1, 2.), + 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 instanceof Rectangle) { - System.out.println("Type Rectangle"); + System.out.println("Type Rectangle"); } else if (s instanceof Circle) { System.out.println("Type Circle"); } @@ -1179,6 +1208,57 @@ Type Rectangle</screen></td> </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> @@ -1191,6 +1271,27 @@ Type Rectangle</screen></td> ...</programlisting> </figure> + <figure xml:id="sd1_inherit_fig_rectangleEquals"> + <title>Implementing <classname>Rectangle</classname>.<methodname + xlink:href="https://docs.oracle.com/javase/9/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 instanceof 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> the expression <code + language="java">o instanceof 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/9/docs/api/java/lang/Object.html#equals-java.lang.Object-">equals()</methodname></title> @@ -1200,14 +1301,37 @@ Type Rectangle</screen></td> if (o instanceof Circle){ final Circle oCircle = (Circle) o; return super.equalCenter(o) && - Math.abs(oCircle.radius - radius) < 1.E-15; + Math.abs(oCircle.radius - radius) < 1.E-15; } return false; } ...</programlisting> + </figure> - <remark>if <code language="java">o == null</code> the expression <code - language="java">o instanceof Circle</code> evaluates to <code - language="java">false</code>.</remark> + <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> -- GitLab