From 745394e234e4046e42b63aeb2f789bd44c84283d Mon Sep 17 00:00:00 2001 From: Joshua Bloch Date: Sat, 11 Aug 2018 00:19:35 -0700 Subject: [PATCH 1/3] Cleaned up code examples from Chapter 8 (Methods) --- .../item50/{Attack.java => Attacks.java} | 8 +- src/effectivejava/chapter8/item50/Period.java | 6 +- .../chapter8/item52/Champagne.java | 1 + .../chapter8/item52/CollectionClassifier.java | 5 - .../item52/FixedCollectionClassifier.java | 23 +++++ .../chapter8/item52/Overriding.java | 1 + .../chapter8/item52/SetList.java | 2 +- .../chapter8/item52/SparklingWine.java | 1 + src/effectivejava/chapter8/item52/Wine.java | 1 + .../chapter8/item53/Varargs.java | 15 +-- src/effectivejava/chapter8/item55/Max.java | 12 ++- .../chapter8/item55/ParentPid.java | 4 +- .../chapter8/item56/DocExamples.java | 97 +++++++++++++++++++ 13 files changed, 146 insertions(+), 30 deletions(-) rename src/effectivejava/chapter8/item50/{Attack.java => Attacks.java} (68%) create mode 100644 src/effectivejava/chapter8/item52/FixedCollectionClassifier.java create mode 100644 src/effectivejava/chapter8/item56/DocExamples.java diff --git a/src/effectivejava/chapter8/item50/Attack.java b/src/effectivejava/chapter8/item50/Attacks.java similarity index 68% rename from src/effectivejava/chapter8/item50/Attack.java rename to src/effectivejava/chapter8/item50/Attacks.java index ddc16eda..2acf8c41 100644 --- a/src/effectivejava/chapter8/item50/Attack.java +++ b/src/effectivejava/chapter8/item50/Attacks.java @@ -1,17 +1,17 @@ package effectivejava.chapter8.item50; import java.util.*; -// Two attacks on the internals of an "immutable" period -public class Attack { +// Two attacks on the internals of an "immutable" period (232-3) +public class Attacks { public static void main(String[] args) { - // Attack the internals of a Period instance (page 232) + // Attack the internals of a Period instance (Page 232) Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78); // Modifies internals of p! System.out.println(p); - // Second attack on the internals of a Period instance + // Second attack on the internals of a Period instance (Page 233) start = new Date(); end = new Date(); p = new Period(start, end); diff --git a/src/effectivejava/chapter8/item50/Period.java b/src/effectivejava/chapter8/item50/Period.java index a02240d8..d3181d9f 100644 --- a/src/effectivejava/chapter8/item50/Period.java +++ b/src/effectivejava/chapter8/item50/Period.java @@ -1,7 +1,7 @@ package effectivejava.chapter8.item50; import java.util.*; -// Broken "immutable" time period class - Page 231-3 +// Broken "immutable" time period class (Pages 231-3) public final class Period { private final Date start; private final Date end; @@ -31,7 +31,7 @@ public String toString() { return start + " - " + end; } -// // Repaired constructor - makes defensive copies of parameters +// // Repaired constructor - makes defensive copies of parameters (Page 232) // public Period(Date start, Date end) { // this.start = new Date(start.getTime()); // this.end = new Date(end.getTime()); @@ -40,7 +40,7 @@ public String toString() { // throw new IllegalArgumentException( // this.start + " after " + this.end); // } - +// // // Repaired accessors - make defensive copies of internal fields (Page 233) // public Date start() { // return new Date(start.getTime()); diff --git a/src/effectivejava/chapter8/item52/Champagne.java b/src/effectivejava/chapter8/item52/Champagne.java index fd1c81ee..552a47e4 100644 --- a/src/effectivejava/chapter8/item52/Champagne.java +++ b/src/effectivejava/chapter8/item52/Champagne.java @@ -1,5 +1,6 @@ package effectivejava.chapter8.item52; +// Classification using method overrriding (Page 239) class Champagne extends SparklingWine { @Override String name() { return "champagne"; } } diff --git a/src/effectivejava/chapter8/item52/CollectionClassifier.java b/src/effectivejava/chapter8/item52/CollectionClassifier.java index fc92d2aa..64ec4182 100644 --- a/src/effectivejava/chapter8/item52/CollectionClassifier.java +++ b/src/effectivejava/chapter8/item52/CollectionClassifier.java @@ -26,9 +26,4 @@ public static void main(String[] args) { for (Collection c : collections) System.out.println(classify(c)); } - // Repaired static classifier method. (Page 240) -// public static String classify(Collection c) { -// return c instanceof Set ? "Set" : -// c instanceof List ? "List" : "Unknown Collection"; -// } } diff --git a/src/effectivejava/chapter8/item52/FixedCollectionClassifier.java b/src/effectivejava/chapter8/item52/FixedCollectionClassifier.java new file mode 100644 index 00000000..99b9c02c --- /dev/null +++ b/src/effectivejava/chapter8/item52/FixedCollectionClassifier.java @@ -0,0 +1,23 @@ +package effectivejava.chapter8.item52; + +import java.math.BigInteger; +import java.util.*; + +// Repaired static classifier method. (Page 240) +public class FixedCollectionClassifier { + public static String classify(Collection c) { + return c instanceof Set ? "Set" : + c instanceof List ? "List" : "Unknown Collection"; + } + + public static void main(String[] args) { + Collection[] collections = { + new HashSet(), + new ArrayList(), + new HashMap().values() + }; + + for (Collection c : collections) + System.out.println(classify(c)); + } +} diff --git a/src/effectivejava/chapter8/item52/Overriding.java b/src/effectivejava/chapter8/item52/Overriding.java index 7f5b2e69..9c670a46 100644 --- a/src/effectivejava/chapter8/item52/Overriding.java +++ b/src/effectivejava/chapter8/item52/Overriding.java @@ -2,6 +2,7 @@ import java.util.List; +// Classification using method overrriding (Page 239) public class Overriding { public static void main(String[] args) { List wineList = List.of( diff --git a/src/effectivejava/chapter8/item52/SetList.java b/src/effectivejava/chapter8/item52/SetList.java index 2a848f37..f54125d3 100644 --- a/src/effectivejava/chapter8/item52/SetList.java +++ b/src/effectivejava/chapter8/item52/SetList.java @@ -1,7 +1,7 @@ package effectivejava.chapter8.item52; import java.util.*; -// What does this program print? - Page 241 +// What does this program print? (Page 241) public class SetList { public static void main(String[] args) { Set set = new TreeSet<>(); diff --git a/src/effectivejava/chapter8/item52/SparklingWine.java b/src/effectivejava/chapter8/item52/SparklingWine.java index 6602b1dc..19fd9614 100644 --- a/src/effectivejava/chapter8/item52/SparklingWine.java +++ b/src/effectivejava/chapter8/item52/SparklingWine.java @@ -1,5 +1,6 @@ package effectivejava.chapter8.item52; +// Classification using method overrriding (Page 239) class SparklingWine extends Wine { @Override String name() { return "sparkling wine"; } } diff --git a/src/effectivejava/chapter8/item52/Wine.java b/src/effectivejava/chapter8/item52/Wine.java index 7e1506c0..8754d3ae 100644 --- a/src/effectivejava/chapter8/item52/Wine.java +++ b/src/effectivejava/chapter8/item52/Wine.java @@ -1,5 +1,6 @@ package effectivejava.chapter8.item52; +// Classification using method overrriding (Page 239) class Wine { String name() { return "wine"; } } diff --git a/src/effectivejava/chapter8/item53/Varargs.java b/src/effectivejava/chapter8/item53/Varargs.java index d427213d..89ea94f0 100644 --- a/src/effectivejava/chapter8/item53/Varargs.java +++ b/src/effectivejava/chapter8/item53/Varargs.java @@ -2,10 +2,9 @@ import java.util.stream.IntStream; -// Sample uses of varargs +// Sample uses of varargs (Pages 245-6) public class Varargs { - - // Simple use of varargs - Page 245 + // Simple use of varargs (Page 245) static int sum(int... args) { int sum = 0; for (int arg : args) @@ -13,12 +12,7 @@ static int sum(int... args) { return sum; } - // Simple use of varargs - Page 197 - static int sum2(int... args) { - return IntStream.of(args).sum(); - } - -// // The WRONG way to use varargs to pass one or more arguments! - Page 245 +// // The WRONG way to use varargs to pass one or more arguments! (Page 245) // static int min(int... args) { // if (args.length == 0) // throw new IllegalArgumentException("Too few arguments"); @@ -29,7 +23,7 @@ static int sum2(int... args) { // return min; // } - // The right way to use varargs to pass one or more arguments - Page 246 + // The right way to use varargs to pass one or more arguments (Page 246) static int min(int firstArg, int... remainingArgs) { int min = firstArg; for (int arg : remainingArgs) @@ -38,7 +32,6 @@ static int min(int firstArg, int... remainingArgs) { return min; } - public static void main(String[] args) { System.out.println(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); System.out.println(min(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); diff --git a/src/effectivejava/chapter8/item55/Max.java b/src/effectivejava/chapter8/item55/Max.java index 42f6181e..1455532f 100644 --- a/src/effectivejava/chapter8/item55/Max.java +++ b/src/effectivejava/chapter8/item55/Max.java @@ -2,9 +2,9 @@ import java.util.*; -// Optionals (P. 248) OK (Checked) +// Using Optional as a return type (Pages 249-251) public class Max { -// // Returns maximum value in collection - throws exception if empty +// // Returns maximum value in collection - throws exception if empty (Page 249) // public static > E max(Collection c) { // if (c.isEmpty()) // throw new IllegalArgumentException("Empty collection"); @@ -17,7 +17,7 @@ public class Max { // return result; // } -// // Returns maximum value in collection as an Optional +// // Returns maximum value in collection as an Optional (Page 250) // public static > // Optional max(Collection c) { // if (c.isEmpty()) @@ -31,7 +31,7 @@ public class Max { // return Optional.of(result); // } - // Returns max val in collection as Optional - uses stream + // Returns max val in collection as Optional - uses stream (Page 250) public static > Optional max(Collection c) { return c.stream().max(Comparator.naturalOrder()); @@ -40,7 +40,9 @@ Optional max(Collection c) { public static void main(String[] args) { List words = Arrays.asList(args); - // Using an optional to provide a chosen default value + System.out.println(max(words)); + + // Using an optional to provide a chosen default value (Page 251) String lastWordInLexicon = max(words).orElse("No words..."); System.out.println(lastWordInLexicon); } diff --git a/src/effectivejava/chapter8/item55/ParentPid.java b/src/effectivejava/chapter8/item55/ParentPid.java index f2c9054a..8b59519f 100644 --- a/src/effectivejava/chapter8/item55/ParentPid.java +++ b/src/effectivejava/chapter8/item55/ParentPid.java @@ -2,15 +2,17 @@ import java.util.Optional; -// Page 250 +// Avoiding unnecessary use of Optional's isPresent method (Page 252) public class ParentPid { public static void main(String[] args) { ProcessHandle ph = ProcessHandle.current(); + // Inappropriate use of isPresent Optional parentProcess = ph.parent(); System.out.println("Parent PID: " + (parentProcess.isPresent() ? String.valueOf(parentProcess.get().pid()) : "N/A")); + // Equivalent (and superior) code using orElse System.out.println("Parent PID: " + ph.parent().map(h -> String.valueOf(h.pid())).orElse("N/A")); } diff --git a/src/effectivejava/chapter8/item56/DocExamples.java b/src/effectivejava/chapter8/item56/DocExamples.java new file mode 100644 index 00000000..24452f54 --- /dev/null +++ b/src/effectivejava/chapter8/item56/DocExamples.java @@ -0,0 +1,97 @@ +package effectivejava.chapter8.item56; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +// Documentation comment examples (Pages 255-9) +public class DocExamples { + // Method comment (Page 255) + /** + * Returns the element at the specified position in this list. + * + *

This method is not guaranteed to run in constant + * time. In some implementations it may run in time proportional + * to the element position. + * + * @param index index of element to return; must be + * non-negative and less than the size of this list + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= this.size()}) + */ + E get(int index) { + return null; + } + + // Use of @implSpec to describe self-use patterns & other visible implementation details. (Page 256) + /** + * Returns true if this collection is empty. + * + * @implSpec This implementation returns {@code this.size() == 0}. + * + * @return true if this collection is empty + */ + public boolean isEmpty() { + return false; + } + + // Use of the @literal tag to include HTML and javadoc metacharacters in javadoc comments. (Page 256) + /** + * A geometric series converges if {@literal |r| < 1}. + */ + public void fragment() { + } + + // Controlling summary description when there is a period in the first "sentence" of doc comment. (Page 257) + /** + * A suspect, such as Colonel Mustard or {@literal Mrs. Peacock}. + */ + public enum FixedSuspect { + MISS_SCARLETT, PROFESSOR_PLUM, MRS_PEACOCK, MR_GREEN, COLONEL_MUSTARD, MRS_WHITE + } + + + // Generating a javadoc index entry in Java 9 and later releases. (Page 258) + /** + * This method complies with the {@index IEEE 754} standard. + */ + public void fragment2() { + } + + // Documenting enum constants (Page 258) + /** + * An instrument section of a symphony orchestra. + */ + public enum OrchestraSection { + /** Woodwinds, such as flute, clarinet, and oboe. */ + WOODWIND, + + /** Brass instruments, such as french horn and trumpet. */ + BRASS, + + /** Percussion instruments, such as timpani and cymbals. */ + PERCUSSION, + + /** Stringed instruments, such as violin and cello. */ + STRING; + } + + // Documenting an annotation type (Page 259) + /** + * Indicates that the annotated method is a test method that + * must throw the designated exception to pass. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface ExceptionTest { + /** + * The exception that the annotated test method must throw + * in order to pass. (The test is permitted to throw any + * subtype of the type described by this class object.) + */ + Class value(); + } +} + From 9e91e8a65c1847975dc81ef653f00c3b5c749c99 Mon Sep 17 00:00:00 2001 From: Joshua Bloch Date: Mon, 13 Aug 2018 14:16:01 -0700 Subject: [PATCH 2/3] Modified PayrollDay example from Item 34 (p. 166) to eliminate parameterless constructore, which ran counter to the purpose of the strategy enum pattern. This change corresponds to the forthcoming fourth printing. --- src/effectivejava/chapter6/item34/PayrollDay.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/effectivejava/chapter6/item34/PayrollDay.java b/src/effectivejava/chapter6/item34/PayrollDay.java index d282e3ed..54a89c12 100644 --- a/src/effectivejava/chapter6/item34/PayrollDay.java +++ b/src/effectivejava/chapter6/item34/PayrollDay.java @@ -1,21 +1,23 @@ package effectivejava.chapter6.item34; +import static effectivejava.chapter6.item34.PayrollDay.PayType.*; + // The strategy enum pattern (Page 166) enum PayrollDay { - MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, - SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND); + MONDAY(WEEKDAY), TUESDAY(WEEKDAY), WEDNESDAY(WEEKDAY), + THURSDAY(WEEKDAY), FRIDAY(WEEKDAY), + SATURDAY(WEEKEND), SUNDAY(WEEKEND); private final PayType payType; PayrollDay(PayType payType) { this.payType = payType; } - PayrollDay() { this(PayType.WEEKDAY); } // Default int pay(int minutesWorked, int payRate) { return payType.pay(minutesWorked, payRate); } // The strategy enum type - private enum PayType { + enum PayType { WEEKDAY { int overtimePay(int minsWorked, int payRate) { return minsWorked <= MINS_PER_SHIFT ? 0 : @@ -36,4 +38,9 @@ int pay(int minsWorked, int payRate) { return basePay + overtimePay(minsWorked, payRate); } } + + public static void main(String[] args) { + for (PayrollDay day : values()) + System.out.printf("%-10s%d%n", day, day.pay(8 * 60, 1)); + } } From bdc828a7af2bdfac28e3c38bd7d1a2ae05736ccc Mon Sep 17 00:00:00 2001 From: Joshua Bloch Date: Wed, 11 Sep 2019 21:29:41 -0700 Subject: [PATCH 3/3] Fixed double check idiom example from page 334 as was done in the third printing. See errata page for details: https://docs.google.com/document/d/1mAeEgQu4H4ADxa03k7YaVDjIP5vJBvjVIjg3DIvoc8E/ --- src/effectivejava/chapter11/item83/Initialization.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/effectivejava/chapter11/item83/Initialization.java b/src/effectivejava/chapter11/item83/Initialization.java index 15f40bff..83f05e0c 100644 --- a/src/effectivejava/chapter11/item83/Initialization.java +++ b/src/effectivejava/chapter11/item83/Initialization.java @@ -25,6 +25,7 @@ private static class FieldHolder { // Double-check idiom for lazy initialization of instance fields - Page 334 private volatile FieldType field4; + // NOTE: The code for this method in the first printing had a serious error (see errata for details)! private FieldType getField4() { FieldType result = field4; if (result != null) // First check (no locking) @@ -38,6 +39,7 @@ private FieldType getField4() { } + // Single-check idiom - can cause repeated initialization! - Page 334 private volatile FieldType field5;