From 693ff6c9e769e4e928a78eed9548a13cbbcc7fde Mon Sep 17 00:00:00 2001 From: "Dr. Martin Goik" <goik@hdm-stuttgart.de> Date: Tue, 19 Feb 2019 09:38:18 +0100 Subject: [PATCH] Exam exercises to lecture notes --- Doc/Sd1/coreClasses.xml | 442 +++++++++++++++--- Doc/Sd1/inheritance.xml | 373 ++++++++++++--- .../mi/sd1/shape/model/Rectangle.java | 15 + .../mi/sd1/shape/model/Shape.java | 44 +- 4 files changed, 717 insertions(+), 157 deletions(-) diff --git a/Doc/Sd1/coreClasses.xml b/Doc/Sd1/coreClasses.xml index df418b865..44f0d046f 100644 --- a/Doc/Sd1/coreClasses.xml +++ b/Doc/Sd1/coreClasses.xml @@ -113,7 +113,7 @@ equals: true</screen></td> <itemizedlist> <listitem> <para>The <code language="java">==</code> operator acting on primitive - types compares values of expressions.</para> + types compares expression values.</para> </listitem> <listitem> @@ -136,28 +136,83 @@ equals: true</screen></td> </itemizedlist> </figure> - <figure xml:id="sd1_coreclasses_fig_hashPrinciple"> - <title>Hashing principle</title> + <section xml:id="sw1_sect_CoreClasses_hashing"> + <title>Objects, equals() and hash-values</title> - <informaltable border="0"> - <tr> - <td valign="top"><mediaobject> - <imageobject> - <imagedata fileref="Ref/CoreClasses/Hash/iceCreamPrices.jpg"/> - </imageobject> - </mediaobject></td> - - <td valign="middle"><quote>I want the 12p one</quote></td> - </tr> - </informaltable> - </figure> + <figure xml:id="sd1_coreclasses_fig_hashPrinciple"> + <title>Hashing principle</title> - <figure xml:id="sd1_coreclasses_fig_hashExample"> - <title><classname>Rectangle</classname> - <methodname>equals(...)</methodname> and - <methodname>hashCode()</methodname></title> - - <programlisting language="java">public class Rectangle { + <informaltable border="0"> + <tr> + <td valign="top"><mediaobject> + <imageobject> + <imagedata fileref="Ref/CoreClasses/Hash/iceCreamPrices.jpg"/> + </imageobject> + </mediaobject></td> + + <td valign="middle"><quote>I want the 12p one</quote></td> + </tr> + </informaltable> + </figure> + + <figure xml:id="sd1_coreclasses_fig_hashQuicklyIdentify"> + <title>Quickly identify by <quote>simple</quote> value</title> + + <itemizedlist> + <listitem> + <para>Where is the blond haired guy?</para> + </listitem> + + <listitem> + <para>I take the pink flower.</para> + </listitem> + + <listitem> + <para>The 334.50$ cellular phone.</para> + </listitem> + </itemizedlist> + </figure> + + <figure xml:id="sd1_coreclasses_fig_hashBasicIdea"> + <title>Hashing in Java and <methodname>equals()</methodname></title> + + <para>Method <methodname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#hashCode()">hashCode()</methodname>: + Instance 0 ⇨ o.<methodname>hashCode()</methodname>, of type <code + language="java">int</code>.</para> + + <itemizedlist> + <listitem> + <para>Same value on repeated invocation</para> + </listitem> + + <listitem> + <para>Objects with identical value with respect to + <methodname>equals()</methodname> must have identical hash + values:</para> + + <para><code language="java">true == a.equals(b)</code> ⟹ <code + language="java">a.hashCode() == b.hashCode()</code>.</para> + </listitem> + + <listitem> + <para>Conversely: Two instances differing with respect to + <methodname>equals()</methodname> may have identical hash + values.</para> + </listitem> + </itemizedlist> + + <para>Consequence: <methodname>equals()</methodname> and + <methodname>hashCode()</methodname> must be <emphasis + role="red">redefined simultaneously</emphasis>!</para> + </figure> + + <figure xml:id="sd1_coreclasses_fig_hashExample"> + <title><classname>Rectangle</classname> + <methodname>equals(...)</methodname> and + <methodname>hashCode()</methodname></title> + + <programlisting language="java">public class Rectangle { int width, height; @Override public boolean equals(Object o) { if (o instanceof Rectangle) { @@ -171,89 +226,328 @@ equals: true</screen></td> return width + height; } }</programlisting> - </figure> + </figure> - <figure xml:id="sd1_coreclasses_fig_hashRectangleValues"> - <title><classname>Rectangle</classname> hash values</title> + <figure xml:id="sd1_coreclasses_fig_hashRectangleValues"> + <title><classname>Rectangle</classname> hash values</title> - <informaltable border="1"> - <tr> - <th>width</th> + <informaltable border="1"> + <tr> + <th>width</th> - <th>height</th> + <th>height</th> - <th>hash value</th> - </tr> + <th>hash value</th> + </tr> - <tr> - <td valign="top">1</td> + <tr> + <td valign="top">1</td> - <td valign="top">3</td> + <td valign="top">3</td> - <td valign="top">4</td> - </tr> + <td valign="top">4</td> + </tr> - <tr> - <td valign="top">2</td> + <tr> + <td valign="top">2</td> - <td valign="top">2</td> + <td valign="top">2</td> - <td valign="top">4</td> - </tr> + <td valign="top">4</td> + </tr> - <tr> - <td valign="top">5</td> + <tr> + <td valign="top">5</td> - <td valign="top">5</td> + <td valign="top">5</td> - <td valign="top">10</td> - </tr> + <td valign="top">10</td> + </tr> - <tr> - <td valign="top">2</td> + <tr> + <td valign="top">2</td> - <td valign="top">7</td> + <td valign="top">7</td> - <td valign="top">9</td> - </tr> + <td valign="top">9</td> + </tr> - <tr> - <td valign="top">4</td> + <tr> + <td valign="top">4</td> - <td valign="top">9</td> + <td valign="top">9</td> - <td valign="top">13</td> - </tr> - </informaltable> - </figure> + <td valign="top">13</td> + </tr> + </informaltable> + </figure> - <figure xml:id="sd1_coreclasses_fig_hashRectangleBetter"> - <title><classname>Rectangle</classname> - <methodname>equals(...)</methodname> and - <methodname>hashCode()</methodname></title> + <figure xml:id="sd1_coreclasses_fig_hashRectangleBetter"> + <title><classname>Better </classname><methodname>hashCode()</methodname> + method</title> - <programlisting language="java">public class Rectangle { + <programlisting language="java">public class Rectangle { int width, height; ... @Override public int hashCode() { return width + <emphasis role="red">13 * height</emphasis>; } }</programlisting> - </figure> + </figure> - <figure xml:id="sd1_coreclasses_fig_hashEqualsContract"> - <title><classname>Contract - </classname><methodname>equals(...)</methodname> and - <methodname>hashCode()</methodname></title> + <qandaset defaultlabel="qanda" xml:id="qanda_sw1TimeperiodHashing"> + <title>Choosing a <quote>good</quote> + <methodname>hashCode()</methodname> method</title> - <para>If <methodname>a.equals(b)</methodname> ==> <code - language="java">a.hashCode() == b.hashCode()</code>. <emphasis - role="red">The reverse does not apply!</emphasis></para> + <qandadiv> + <qandaentry> + <question> + <para>We consider:</para> - <para>Consequence: <methodname>equals()</methodname> and - <methodname>hashCode()</methodname> must be <emphasis role="red">redefined - simultaneously</emphasis>!</para> - </figure> + <programlisting language="java">public class TimePeriod { + private final int hours, minutes, seconds; + /** + * A time period within a day e.g. 4 hours, 23 minutes and 11 seconds. + * + * @param hours Hours ranging from 0 to 23 + * @param minutes Minutes ranging from 0 to 59 + * @param seconds Seconds ranging from 0 to 59 + */ + public TimePeriod(final int hours, final int minutes, final int seconds) { + this.hours = hours; + this.minutes = minutes; + this.seconds = seconds; + } + + @Override + public boolean equals(final Object o) { + if (o instanceof TimePeriod) { + final TimePeriod other = (TimePeriod) o; + return hours == other.hours && + minutes == other.minutes && + seconds == other.seconds; + } else { + return false; + } + } +}</programlisting> + + <para>Which of the two <methodname>hashCode()</methodname> + implementations is better with respect to quickly telling whether + two <classname>TimePeriod</classname> instances are having the + same value or not?</para> + + <informaltable border="1"> + <tr> + <th>Method 1</th> + + <th>Method 2</th> + </tr> + + <tr> + <td valign="top"><programlisting language="java">public class TimePeriod { +... + @Override + public int hashCode() { + return seconds + 60 * (minutes + 60 * hours); + } +}</programlisting></td> + + <td valign="top"><programlisting language="java">public class TimePeriod { +... + @Override + public int hashCode() { + return seconds + minutes + hours; + } +}</programlisting></td> + </tr> + </informaltable> + + <tip> + <para>Excerpt from <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html">Object</classname>.<link + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#hashCode()">hashCode()</link>:</para> + + <blockquote> + <para>However, the programmer should be aware that producing + distinct integer results for unequal objects may improve the + performance of hash tables.</para> + </blockquote> + </tip> + </question> + + <answer> + <para>An ideal/perfect <methodname>hashCode()</methodname> method + in addition to <xref linkend="sd1_coreclasses_fig_hashBasicIdea"/> + will return different values whenever two instances <code>a</code> + and <code>b</code> differ in value with respect to the underlying + <methodname>equals()</methodname> method:</para> + + <para><code language="java">false == a.equals(b)</code> ⟹ <code + language="java">a.hashCode() != b.hashCode()</code>.</para> + + <para>Method 1 serves this purpose: Its returns a given + <classname>TimePeriod</classname> instance total amount of seconds + equivalent e.g turning (<emphasis role="rd">1</emphasis> hour, + <emphasis role="rd">2</emphasis> minutes, <emphasis + role="rd">5</emphasis> seconds) into (<emphasis + role="rd">1</emphasis> * 60 + <emphasis role="rd">2</emphasis>) * + 60 + <emphasis role="rd">5</emphasis> = 3725 seconds. Due its + specification a <classname>TimePeriod</classname> instance cannot + exceed 24 hours being equivalent to 24 * 60 * 60 = 86400 seconds. + A 4 byte <code language="java">int</code> is therefore safe with + respect to overflow issues.</para> + + <para>Conclusion: Whenever two instances of + <classname>TimePeriod</classname> differ their method 1 + <methodname>hashCode()</methodname> values will differ as + well.</para> + + <para>This does not hold with respect to method 2. Consider two + instances (1 hour, 2 minutes, 3 seconds) vs. (3 hours, 2 minutes, + 1 second) returning an identical + <methodname>hashCode()</methodname> of 1 + 2 + 3 = 3 + 2 + 1 = 6. + Thus method 2 requiring just two additions offers (slightly) + better runtime performance at the expense of a higher hash value + collision rate. </para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + + <qandaset defaultlabel="qanda" xml:id="qanda_sw1StringPerfectHash"> + <title><methodname>String</methodname> and good hashCode() + implementations.</title> + + <qandadiv> + <qandaentry> + <question> + <para>In the previous exercise we found an ideal + <methodname>hashCode()</methodname> implementation:</para> + + <programlisting language="java">public class TimePeriod { +... + @Override + public int hashCode() { + return seconds + 60 * (minutes + 60 * hours); + } +}</programlisting> + + <para>Is this possible for instances of String as well?</para> + + <tip> + <para>Consider the possible number of different strings.</para> + </tip> + </question> + + <answer> + <para>It is not possible to construct a perfect + <methodname>hashCode()</methodname> method acting on arbitrary + strings. A Java <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html">String</classname> + consists of individual <code language="java">char</code> elements + each requiring two bytes. Considering strings of fixed length we + have the following number of different strings:</para> + + <informaltable border="1"> + <tr> + <th>Number of chars</th> + + <th>Number of possible strings</th> + </tr> + + <tr> + <td valign="top">1</td> + + <td valign="top"><inlineequation> + <m:math display="inline"> + <m:msup> + <m:mi>2</m:mi> + + <m:mi>16</m:mi> + </m:msup> + </m:math> + </inlineequation></td> + </tr> + + <tr> + <td valign="top">2</td> + + <td valign="top"><inlineequation> + <m:math display="inline"> + <m:msup> + <m:mi>2</m:mi> + + <m:mrow> + <m:mi>2</m:mi> + + <m:mo>×</m:mo> + + <m:mi>16</m:mi> + </m:mrow> + </m:msup> + </m:math> + </inlineequation></td> + </tr> + + <tr> + <td valign="top">3</td> + + <td valign="top"><inlineequation> + <m:math display="inline"> + <m:msup> + <m:mi>2</m:mi> + + <m:mrow> + <m:mi>3</m:mi> + + <m:mo>×</m:mo> + + <m:mi>16</m:mi> + </m:mrow> + </m:msup> + </m:math> + </inlineequation></td> + </tr> + </informaltable> + + <para>A four byte <code language="java">int</code> only offers + <inlineequation> + <m:math display="inline"> + <m:msup> + <m:mi>2</m:mi> + + <m:mrow> + <m:mi>32</m:mi> + </m:mrow> + </m:msup> + </m:math> + </inlineequation> different values. Thus even mapping just one- + and two-<xref linkend="glo_unicode"/> character strings exceeds + the number of different <code language="java">int</code> values + thus requiring different string instances being mapped to + identical hash values. Consider for example:</para> + + <informaltable border="1"> + <tr> + <th>Code</th> + + <th>Execution result</th> + </tr> + + <tr> + <td valign="top"><programlisting language="java">System.out.println("hashcode of AA: " + "Aa".hashCode()); +System.out.println("hashcode of BB: " + "BB".hashCode());</programlisting></td> + + <td valign="top"><screen>hashcode of AA: 2112 +hashcode of BB: 2112</screen></td> + </tr> + </informaltable> + </answer> + </qandaentry> + </qandadiv> + </qandaset> + </section> <section xml:id="sd1SectVarargsFormat"> <title>Reconsidering System.out.format().</title> diff --git a/Doc/Sd1/inheritance.xml b/Doc/Sd1/inheritance.xml index 22f897d61..b4a694669 100644 --- a/Doc/Sd1/inheritance.xml +++ b/Doc/Sd1/inheritance.xml @@ -194,11 +194,12 @@ double height;</programlisting></td> <itemizedlist> <listitem> - <para>A rectangle is a shape.</para> + <para>A rectangle <emphasis role="red">is a</emphasis> + shape.</para> </listitem> <listitem> - <para>A circle is a shape.</para> + <para>A circle <emphasis role="red">is a</emphasis> shape.</para> </listitem> </itemizedlist> </listitem> @@ -267,7 +268,169 @@ public Shape(double x,double y) { }</programlisting> </figure> - <section xml:id="sd1_inherit_sect_inheritToString"> + <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. Coming soon ...</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 <link + xlink:href="https://gitlab.mi.hdm-stuttgart.de/goik/GoikLectures/blob/master/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Rectangle.java">Rectangle</link>(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> + + <calloutlist role="slideExclude"> + <callout arearefs="sda_inherit_fig_RectangleConstructor-1-co" + xml:id="sda_inherit_fig_RectangleConstructor-1"> + <para>Passing center coordinate to superclass + <classname>Shape</classname>'s constructor.</para> + + <note> + <para>This <emphasis>must</emphasis> be the first statement within + <classname>Rectangle</classname>'s constructor.</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">h</code></para> + </callout> + + <callout arearefs="sda_inherit_fig_RectangleConstructor-2-co"> + <para><code language="java">eight</code> in <quote>current</quote> + <classname>Rectangle</classname> constructor.</para> + </callout> + </calloutlist> + </figure> + + <section xml:id="sd1_inherit_sect_overrideEquals"> + <title>Overriding <methodname>equals()</methodname> and + <methodname>hashCode()</methodname></title> + + <figure xml:id="sda_inherit_fig_ShapeEquals"> + <title><classname>Shape</classname>.<methodname>equals()</methodname></title> + + <programlisting language="java">public abstract class Shape { + ... + @Override <co linkends="sda_inherit_fig_ShapeEquals-1" + xml:id="sda_inherit_fig_ShapeEquals-1-co"/> public boolean equals(final Object o) { + if (o instanceof Shape <co linkends="sda_inherit_fig_ShapeEquals-2" + xml:id="sda_inherit_fig_ShapeEquals-2-co"/>) { + final Shape other = (Shape) o; <co + linkends="sda_inherit_fig_ShapeEquals-3" + xml:id="sda_inherit_fig_ShapeEquals-3-co"/> + return x == other.x && y == other.y; <co + linkends="sda_inherit_fig_ShapeEquals-4" + xml:id="sda_inherit_fig_ShapeEquals-4-co"/> + } else { + return false; <co linkends="sda_inherit_fig_ShapeEquals-5" + xml:id="sda_inherit_fig_ShapeEquals-5-co"/> + } ...</programlisting> + + <calloutlist role="slideExclude"> + <callout arearefs="sda_inherit_fig_ShapeEquals-1-co" + xml:id="sda_inherit_fig_ShapeEquals-1"> + <para>Promise: The current method overrides a superclass + method.</para> + </callout> + + <callout arearefs="sda_inherit_fig_ShapeEquals-2-co" + xml:id="sda_inherit_fig_ShapeEquals-2"> + <para>Other instance is a <classname>Shape</classname> + object?</para> + </callout> + + <callout arearefs="sda_inherit_fig_ShapeEquals-3-co" + xml:id="sda_inherit_fig_ShapeEquals-3"> + <para>Casting other object to class + <classname>Shape</classname>.</para> + </callout> + + <callout arearefs="sda_inherit_fig_ShapeEquals-4-co" + xml:id="sda_inherit_fig_ShapeEquals-4"> + <para>Return <code language="java">true</code> if and only if both + center coordinate pairs are equal.</para> + </callout> + + <callout arearefs="sda_inherit_fig_ShapeEquals-5-co" + xml:id="sda_inherit_fig_ShapeEquals-5"> + <para>Other object distinct from class + <classname>Shape</classname>.</para> + </callout> + </calloutlist> + </figure> + + <figure xml:id="sda_inherit_fig_RectangleEquals"> + <title><classname>Rectangle</classname>.<methodname>equals()</methodname></title> + + <programlisting language="java">public class Rectangle extends Shape { + ... + @Override public boolean equals(final Object o) { + if (o instanceof Rectangle) { + final Rectangle other = (Rectangle) o; + return super.equals(o) <co linkends="sda_inherit_fig_RectangleEquals-1" + xml:id="sda_inherit_fig_RectangleEquals-1-co"/> && width == other.width && height == other.height <co + linkends="sda_inherit_fig_RectangleEquals-2" + xml:id="sda_inherit_fig_RectangleEquals-2-co"/>; + } else { + return false; + } ...</programlisting> + + <calloutlist role="slideExclude"> + <callout arearefs="sda_inherit_fig_RectangleEquals-1-co" + xml:id="sda_inherit_fig_RectangleEquals-1"> + <para>Including superclass method + <classname>Shape</classname>.<methodname>equals()</methodname>.</para> + </callout> + + <callout arearefs="sda_inherit_fig_RectangleEquals-2-co" + xml:id="sda_inherit_fig_RectangleEquals-2"> + <para>Return <code language="java">true</code> if and only if both + <code language="java">width</code> and <code + language="java">height</code> pairs are equal.</para> + </callout> + </calloutlist> + </figure> + </section> + + <section xml:id="sd1_inherit_sect_overrideToString"> <title>Overriding toString()</title> <figure xml:id="sda_inherit_fig_shapeInstanceLog"> @@ -309,71 +472,6 @@ public Shape(double x,double y) { </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> - - <calloutlist role="slideExclude"> - <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> - <figure xml:id="sda_inherit_fig_RectangleLogging"> <title>Logging <classname>Rectangle</classname> instances</title> @@ -409,22 +507,24 @@ public Rectangle(double x, double y, <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> + <para>The <code language="java" + xlink:href="https://docs.oracle.com/javase/tutorial/java/IandI/super.html">super</code> + keyword allows for calling the <methodname>toString()</methodname> + method from superclass <classname>Shape</classname>.</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> + <para>Append class <classname>Rectangle</classname>'s + <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> + <classname>Shape</classname></title> <mediaobject> <imageobject> @@ -465,6 +565,131 @@ public Rectangle(double x, double y, </imageobject> </mediaobject> </figure> + + <qandaset defaultlabel="qanda" + xml:id="sd1_quanda_inherit_StringBufferNoEqualMethod"> + <title><classname>String</classname> vs. + <classname>StringBuffer</classname></title> + + <qandadiv> + <qandaentry> + <question> + <para>Consider two <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuffer.html">StringBuffer</classname> + instances:</para> + + <informaltable border="1"> + <colgroup width="54%"/> + + <colgroup width="46%"/> + + <tr> + <th>Code</th> + + <th>Execution result</th> + </tr> + + <tr> + <td valign="top"><programlisting language="java">final <link + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuffer.html">StringBuffer</link> + a = new StringBuffer("test"), + b = new StringBuffer("test"); + +System.out.println(a.equals(b));</programlisting></td> + + <td valign="top"><screen>false</screen></td> + </tr> + </informaltable> + + <para>Strangely instances of String behave differently:</para> + + <informaltable border="1"> + <colgroup width="54%"/> + + <colgroup width="46%"/> + + <tr> + <th>Code</th> + + <th>Execution result</th> + </tr> + + <tr> + <td valign="top"><programlisting language="java">final String + a = new String("test"), + b = new String("test"); + +System.out.println(a.equals(b));</programlisting></td> + + <td valign="top"><screen>true</screen></td> + </tr> + </informaltable> + + <para>Explain this different behaviour.</para> + + <tip> + <para>Take a closer look at the <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html">String</classname> + and <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuffer.html">StringBuffer</classname> + <xref linkend="glo_API"/> regarding the + <methodname>toString()</methodname> method.</para> + </tip> + </question> + + <answer> + <para>The <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html">String</classname> + <xref linkend="glo_API"/> reveals:</para> + + <screen>public boolean equals (Object anObject) +Compares this string to the specified object. The result is true if and only if the argument is +not null and is a String object that represents the same sequence of characters as this object. + +Overrides: equals in class Object + +Parameters: +anObject - The object to compare this String against + +Returns: +true if the given object represents a String equivalent to this string, false otherwise</screen> + + <para>In a nutshell: Two instances of String will be compared for + equality character by character. The superclass <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html">Object</classname>.<methodname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)">equals()</methodname> + method is being overridden.</para> + + <para>In class <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuffer.html">StringBuffer</classname> + we do not find any <methodname>equals()</methodname> method. Thus + <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html">Object</classname>.<methodname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)">equals()</methodname> + is not being overridden. When comparing two instances of + <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuffer.html">StringBuffer</classname> + effectively <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html">Object</classname>.<methodname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)">equals()</methodname> + will be executed. Its <xref linkend="glo_API"/> reveals:</para> + + <screen>public boolean equals (Object obj) +Indicates whether some other object is "equal to" this one. +... +Parameters: +obj - the reference object with which to compare. +Returns: +true <emphasis role="red">if this object is the same as the obj argument</emphasis>; false otherwise.</screen> + + <para>Thus two instances of <classname + xlink:href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuffer.html">StringBuffer</classname> + will be compared for object identity rather than representing the + same string value.</para> + </answer> + </qandaentry> + </qandadiv> + </qandaset> </section> <section xml:id="sd1_inherit_sect_final"> diff --git a/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Rectangle.java b/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Rectangle.java index 20b554bb7..f0972879e 100644 --- a/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Rectangle.java +++ b/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Rectangle.java @@ -19,6 +19,21 @@ public class Rectangle extends Shape { setHeight(height); } + @Override + public boolean equals(final Object o) { + if (o instanceof Rectangle) { + final Rectangle other = (Rectangle) o; + return super.equals(o) && width == other.width && height == other.height; + } else { + return false; + } + } + + @Override + public int hashCode() { + return super.hashCode () + (int) (width + 13 * height); + } + @Override public Shape scale(final double factor) { width *= factor; diff --git a/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Shape.java b/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Shape.java index 4ef5a8671..6657a9f67 100644 --- a/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Shape.java +++ b/P/Sd1/Figure/ToString/src/main/java/de/hdm_stuttgart/mi/sd1/shape/model/Shape.java @@ -1,12 +1,11 @@ package de.hdm_stuttgart.mi.sd1.shape.model; /** - * @author goik - * + * Base class of geometric primitives like {@link Rectangle} and {@link Circle} */ public abstract class Shape { - private double x,y; + private double x, y; /** * Creating a shape with given center coordinates @@ -19,13 +18,27 @@ public abstract class Shape { setY(y); } + @Override + public boolean equals(final Object o) { + if (o instanceof Shape) { + final Shape other = (Shape) o; + return x == other.x && y == other.y; + } else { + return false; + } + } + + @Override + public int hashCode() { + return (int) (x + 13 * y); + } + @Override public String toString() { return "(" + x + ',' + y + ')'; } /** - * * @param factor Scale the current figure by this value. * @return The current object. */ @@ -33,20 +46,33 @@ public abstract class Shape { /** * The current shape's area. + * * @return e.g. with * height in case of a rectangle */ public abstract double getArea(); /** * The current shape's perimeter. - * @return e.g. with 2 * (height + width) in case of a rectangle + * + * @return e.g. with 2 * (height + width) in case of a rectangle */ public abstract double getPerimeter(); - public double getX() { return x;} - public void setX(final double x) { this.x = x;} - public double getY() { return y;} - public void setY(final double y) { this.y = y;} + public double getX() { + return x; + } + + public void setX(final double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(final double y) { + this.y = y; + } /** * Translating a shape by a given vector (x,y). -- GitLab