class Book { String title; String author; int numPages; boolean isCopyrighted; Book( String _title, String _author, int _numPages, boolean _isCopyrighted ) { this.title = _title; this.author = _author; this.numPages = _numPages; this.isCopyrighted = _isCopyrighted; } /* ( Template for book-handling functions: ) ... funcForBook( /* Book this * / ) { ... this.title ... this.author ... this.numPages ... this.isCopyrighted } */ /** @return The amount of time it takes the average reader to read a book (in minutes) */ double readingTime( /* Book this */ ) { return 2 * this.numPages; } @Override public String toString( /* Book this */ ) { return this.title + ", by " + this.author + " (" + this.numPages + "pp)" + (this.isCopyrighted ? " ©" : "" ); } /** @return a book just like `a-book`, but with bigger text (hence, more pages). */ Book enbiggen( /* Book this */ ) { return new Book( this.title, this.author, 2*this.numPages, this.isCopyrighted ); } /** @return a brand new book, based on an original. * The result's authoor will be the original author & the `newAuthor`, * and the result has `numNewPages` pages added to the original. */ Book deriveFrom( /* Book this */ String newAuthor, int numNewPages ) { return new Book( "Son of " + this.title, this.author + " & " + newAuthor, this.numPages + numNewPages, false ); } /* All code has to be inside a method in Java, so we'll stick our unit-tests in here. */ static void test() { Book b0, b1; b1 = new Book( "The Cat in the Hat", "Seuss", 37, true ); b0 = new Book( "The Soul of Wit", "Barland", 0, false ); assert (new Book( "The Cat in the Hat", "Seuss", 37, true )).readingTime() == 74.0; assert b0.readingTime() == 0; assert b1.enbiggen().equals(new Book( "The Cat in the Hat", "Seuss", 74, true )); assert (new Book( "The Bat in the Mat", "Streuss", 1, true )).enbiggen() .equals(new Book( "The Bat in the Mat", "Streuss", 2, true )); assert b0.enbiggen().equals(b0); assert b1.deriveFrom("Barland", 10) .equals( new Book( "Son of The Cat in the Hat" , "Seuss & Barland" , 47 , false ) ); assert b0.deriveFrom("Banksie", 99) .equals( new Book( "Son of The Soul of Wit" , "Barland & Banksie" , 99 , false ) ); assert b0.equals(b0); assert !b0.equals(b1); assert b1.equals( new Book( "The Cat in the Hat", "Seuss", 37, true ) ); assert b0.hashCode() == b0.hashCode(); assert b1.hashCode() == new Book( "The Cat in the Hat", "Seuss", 37, true ).hashCode(); assert b1.toStringDebug().equals( "new Book( \"The Cat in the Hat\", \"Seuss\", 37, true )" ); } /** Just run our unit tests. Be sure to run `java` with `--assertionsEnabled` (or, `-ea`). */ public static void main( String[] __ ) { test(); // Alternately, we could just stick the tests directly into `main` here, that's fine too. } // OOPS -- this does not override Object#equals(Object) public boolean equals( /* Book this, */ Book that ) { return this.title.equals( that.title ) && this.numPages == that.numPages; } @Override public boolean equals( /* Book this, */ @Nullable Object that ) { if (that==null) { return false; } else if (this==that) { return true; } else if (this.getClass() != that.getClass()) { // Many say to use `instanceof` ^^here^^; I disagree. --ibarland return false; } else { Book thatt = (Book)that; return this.author.equals(thatt.author) && this.title.equals(thatt.title) && this.numPages == thatt.numPages && this.isCopyrighted == thatt.isCopyrighted; } } // as a Java/O.O. programmer, memorize: // NEVER OVERRIDE EQUALS WITHOUT OVERRIDING HASHCODE // (Built-in libraries -- especially Collections -- rely on the // property that if a.hashCode()!=b.hashCode(), then !a.equals(b) // and so they won't actually call `equals` if the hashcodes differ. // ...Java's default hashCode, like default `equals`/`==`/`toString` // returns the memory address. // (So the default equals + hashCode DOES have the desired property, happily; // it's up to you to remember to [repeat with me] Never override // `equals` without also overriding `hashCode`.) private static Integer theHash = null; private static int SHUFFLE_BITS = 0b11111; //31; @Override public int hashCode( /* Book this */ ) { if (theHash == null) { // We only reach this once per object. // (relies on immutability). int h = 0; h += title.hashCode(); h *= SHUFFLE_BITS; h += author.hashCode(); h *= SHUFFLE_BITS; h += (new Integer(numPages)).hashCode(); h *= SHUFFLE_BITS; h += (new Boolean(isCopyrighted)).hashCode(); theHash = new Integer(h); } return theHash.intValue(); } /** @return a string suitable for a programmer to read. * It can be copied/pasted into Java source code. */ public String toStringDebug( /* Book this */ ) { String separator = ", "; return "new Book( " + "\"" + this.title + "\"" + separator + "\"" + this.author + "\"" + separator + this.numPages + separator + this.isCopyrighted + " )"; } }