I am completely replacing the Java4Python section.
There is a section index at the end of this document.
Here is a Python class and a rather direct Java translation.
Python in point2.py:
class Point: '''elaborated Point to compare to Java''' def __init__(self, x, y): self.x = x self.y = y def getX(self): return self.x def getY(self): return self.y def distanceFromOrigin(self): return (self.x ** 2 + self.y ** 2) ** .5 def diff(self, pt): '''return vector difference. self - ptf''' return Point(self.x - pt.x, self.y - pt.y) def distance(self, pt): '''return distance between self and pt''' return self.diff(pt).distanceFromOrigin() def halfway(self, target): '''return Point halfway between self and target''' mx = (self.x + target.x)/2 my = (self.y + target.y)/2 return Point(mx, my) def __str__(self): return "x = {}, y = {}".format(self.x, self.y)
and compare Java in Point.java:
/** translation of point2.py */ import java.lang.Math; /** translation of point2.py with verbose comments */ public class Point // public so available to all other classes { private double x, y; // instance variable, private, NOT in a method public Point(double x, double y) // constructor: method with class name, no return value { this.x = x; // this in place of Python self this.y = y; } public double getX() { return x; // no this: no need to disambiguate x } // If a variable is not local, next check instance variables public double getY() { return y; } public double distanceFromOrigin() { return Math.sqrt(x*x + y*y); // no ** operator } /** return vector difference. this - pt */ public Point diff(Point pt) // cannot overload "-" operator { return new Point(x - pt.x, y - pt.y); // note required "new" // for other Point, pt, must use dot notation } /** return distance between this and pt */ public double distance(Point pt) { return diff(pt).distanceFromOrigin(); // not need this. with diff } /** return Point halfway between this Point and target */ public Point halfway(Point target) { double mx = (x + target.x)/2; // target needs dot notation while double my = (y + target.y)/2; // instance variables for this do not return new Point(mx, my); // again note "new" } public String toString() { return String.format("x = %f, y = %f", x, y); } }
Java requires all variables to be declared, including in a class. In Python we tend to just create instance variables in __init__, and we know they are instance variables because we always use object oriented dot notation, like self.x.
In Python we can always distinguish a local variable like x from an instance variable like self.x, because we always use dot notation for the instance variable.
Instance variables, like all variables for Java are declared. They are declared inside the class and outside any method:
private double x, y; // instance variables, private, not in a method
I put this declaration first inside the class, but it could go anywhere outside of method definitions. Like all declarations, a type must be specified.
In Python you can access any variable from anywhere: often convenient, but lacking in safety. Inside a class Java allows a variable to be declared private, so it is only accessible directly from inside the class.
Python uses the fixed name __init__ for its constructor. Java has different notation: A method with the same name as the class, and no return value (not even void) is a constructor.
Java constructors and instance methods headings do not include self like Python always has. This current object does have a name, but it is this instead of self and the name is implicit, and is not a part of explicit method parameter lists.
The heading of the constructor has (typed) parameters only:
public Point(double x, double y) // constructor: method with class name, no return value { this.x = x; // this in place of self this.y = y; }
Then other than the replacement of self by this, the body looks the same as in Python.
Unlike in Python where object attributes are always referred to with dot notation, the separate declaration of instance variables allows for some syntax shortening.
In the first regular method
public double getX() { return x; // no this: no need to disambiguate x } // If a variable is not local, next check instance variables
note no self or this anywhere! Java allows the this. to be omitted from this.x. This works in Java because of these two conditions:
If there are both a local variable and and instance variable with the same name, Java first looks to match a plain local variable. If there is not a local variable by that name, it next looks for an instance variable for the current object (this object).
In getX there is no x local variable declared, so the compiler understands that x is the same as this.x.
In the earlier constructor, there were both local and instance x, and the instance one needed to be disambiguated from the local one by using this..
In getY and distanceFromOrigin there are no competing local variables, so no need for this. with the instance variables.
We have a parameter of the same type as the class in method
/** return vector difference. this - pt */ public Point diff(Point pt) // cannot overload "-" operator { return new Point(x - pt.x, y - pt.y); // note required "new" // for other Point, pt, must use dot notation }
Now there are x and y instance variable usages for both objects. Still there is no local variable x or y, so we can use x in place of this.x. Of course to refer to the state of a different Point, pt, we must use the regular dot notation, pt.x
Recall that instance variable x in the Point class was declared private. Although pt is probably a different Point than the current object (this), it is of the same class, so private components can be accessed inside the definition of the class.
Again: Not only can private instance variables of this object be accessed, but also private instance variables of a parameter object of the same class. Dot notation must be used with the parameter object.
In code in a different class than Point, if we want to access x in p of class Point, then we need a public way, and the method getX allows the outside access to p.x via p.getX(). This allows reading the value, but not changing the value of p.x.
The Javadoc utility program (and various IDE's) will extract a comment /** ... */ right before a class or method heading and use it in documentation for the whole class. Note the two asterisks in the beginning instead of one. To make the documentation look good in HTML, there is lots of special syntax inside these comments that I will not use or discuss.
We see another shortening compared with Python in
/** return distance between this and pt */ public double distance(Point pt) { return diff(pt).distanceFromOrigin(); // not need "this." with diff }
The Python version included self.diff to access the instance method diff.
The Java compiler can see that diff is a Point method, and allows the coder to use diff in place of this.diff.
A user-defined object class can be used as a return type, as in the halfway method. The Python version also returns a Point, but in Python return types need not be specified.
This is a pretty direct analog of Python __str__. Be sure to match the Java heading exactly (including public).
In the formatting for a Point, notice two places for substituting a double value, and hence two further parameters.
That should pretty well cover the ideas in this Java class!
For completeness we will also cover corresponding programs that use the Point class.
First testpoint2.py:
'''Code to test Point''' from point2 import Point def main(): pt = Point(3, 4) print('pt coords:', pt) print('x alone:', pt.getX()) print('y alone:', pt.getY()) print('pt dist for origin:', pt.distanceFromOrigin()) pt2 = Point(-9, 9) print('pt2 coords:', pt2) print('pt1 - pt2 coords:', pt.diff(pt2)) print('pt to pt2 distance:', pt.distance(pt2)) print('halfway from pt to pt2:', pt.halfway(pt2)) main()
and Java TestPoint.java:
// Code to test Point like Point in point2.py // no import if Point in same folder public class TestPoint { public static void main(String[] args) { Point pt = new Point(3, 4); // declaration and new System.out.println("pt coords: " + pt); //need + to make one expression System.out.println("x alone: " + pt.getX()); System.out.println("y alone: " + pt.getY()); System.out.println("pt dist for origin: " + pt.distanceFromOrigin()); Point pt2 = new Point(-9, 9); System.out.println("pt2 coords: " + pt2); System.out.println("pt1 - pt2 coords: " + pt.diff(pt2)); System.out.println("pt to pt2 distance: " + pt.distance(pt2)); System.out.println("halfway from pt to pt2: " + pt.halfway(pt2)); // System.out.println("other class private value: " + pt.x); //ERROR! } }
There is little new to say about the translation. For Java println we need a single string parameter. A Point is created with new included.
Do look at the final Java line that is commented out. Uncommenting it would cause an error: While TestPoint could use pt.getX(), it cannot use Point's private pt.x!
Suppose that you wanted to write a Student class so that the class could keep track of the number of students it had created. Although you could do this with a global counter variable, that is an ugly solution. The right way to do it is to use a static variable. In Python we could do this as follows (where numStudents might be class a class variable):
class Student: numStudents = 0 # inside class, NOT inside a method def def __init__(self, id, name): self.id = id self.name = name Student.numStudents = Student.numStudents + 1 # note dot notation with the class name def main(): for i in range(10): s = Student(i,"Student-"+str(i)) print 'The number of students is: ', Student.numStudents main()
In Java we would write this same example using a static declaration.
public class Student { public static int numStudents = 0; private int id; private String name; public Student(int id, String name) { this.id = id; this.name = name; numStudents = numStudents + 1; } public static void main(String[] args) { for(int i = 0; i < 10; i++) { Student s = new Student(i,"Student" + i); } System.out.println( "The number of students: " + Student.numStudents); } }
In this example notice that we create a static member variable by using the static modifier on the variable declaration. Once a variable has been declared static in Java it can be accessed from inside the class without prefixing the name of the class as we had to do in Python.
Exercise: Modify the Python or Java Point code to keep a class/static variable with a list of all Points created. Then write and test a static method to find the minimum distance between Points created.
While basic class features largely translate from Python to Java, as discussed in the last section for Point, there are bigger differences in some more advanced features, and some things just are not translated for Rational.
To compare the whole classes see rational.py and Rational.java.
We will go through some individual features.
We will come back to the extra part in the class heading, implements Comparable<Rational>.
Java variables cannot start with '_', and we can protect num and den by the usual Java technique of making them private.
Note that by using Java int variables, we are restricting the range of values, unlike with Python. We could replace the int type with long to get more flexible, or we could use the java.math.BigInteger class for arbitrarily long integers, but with no arithmetic operand overloading, it would be verbose, with no real new ideas, so we will stick to type int for num and den.
In Python there can be just one __init__ method for a class, but it allows multiple type and default variables.
The most basic Java constructor directly supplies int values:
/** * Create a rational given numerator and denominator. * @param num numerator * @param den denominator */ public Rational(int num, int den) { if (den == 0) { // We really should force an Exception, but we won't. System.out.println("Zero denominator changed to 1!"); den = 1; } // enforces den > 0, rational reduced to lowest terms int n = gcd(num, den); if (den/n < 0) n = -n; this.num = num / n; this.den = den / n; }
There is not as neat a confirmation of bad data as the Python assert statement, so I make an really kludgey choice of just printing a warning and changing 0 denominators.
The gcd method at the end of the class is only used by this constructor, so it can be private. Like the Python version, it is static.
With just this original constructor, the compiler would find an error if we used new Rational(2) to convert an int to a Rational, because there is just one parameter.
In Java we need a different approach, overloading of the constructor name, with different versions with different types and numbers of parameters. This is legal and common in Java, while there is no analog in Python. Python offsets it by not being statically typed, and allowing optional and keyword parameters.
This idea of overloading raises a very important difference between Python and Java. In Python a method is known by its name only. In Java a method is known by its signature. The signature of a method includes its name, and the sequence of types of all of its parameters (but not the formal parameter names). The method name and actual parameter types are enough information for the Java compiler to decide which method to call at runtime, even when several methods have the same name.
In particular we can create a constructor to convert an integer to a Rational. We overload the constructor so that if it only receives a single int it will construct a Rational with heading public Rational(int wholeNumber).
There are several ways to do this, but what seems like an obvious way to use what we already have written, is to just have this constructor call the original constructor, supplying 1 for the denominator, new Rational(wholeNumber, 1);, but with Java constructors not returning anything we cannot do that is any obvious way.
Instead Java has a strange special construction to handle this:
/** * Create a rational for an integer value. * @param wholeNumber is the rational number */ public Rational(int wholeNumber) { this(wholeNumber, 1); // new syntax -- first line (only) can call } // another constructor using 'this' as the method name.
On the first line only of a constructor, we can use this in place of new Rational, and do the initialization that the other constructor would have done. It is possible to have more lines after this first line to further alter the state, but that is not necessary here.
Variations on this syntax are important with inheritance, too, but that is not for this course.
With different overloaded versions of methods, the Java compiler looks at the names of the method/constructor and the sequence of types as parameters, to figure out which overloaded version to call.
Overloading gets a big more complicated with this obvious implementation of equals:
/** * Test two fractions for equality. * @param r the rational to compare to the receiver. * @return true if the receiver and r are equal numbers, false otherwise. */ public boolean equals(Rational r) { return num == r.num && den == r.den; }
Note that the ancestor class of Rational, Object, also has a version of equals:
public boolean equals(Object obj).
A Rational is also a kind of Object, so this Object version could be used to test a Rational parameter:
Rational r = new Rational(2, 3), r2 = new Rational(-4, -6); boolean match = r.equals(r2);
but the Java compiler is more sophisticated: In the usage r.equals(r2), the compiler sees that the equals defined in the Rational class has an exact parameter type match, and prefers that version.
The existence of the Object version means that it is legal to have a Rational r in r.equals("someString"). The compiler would choose the Object version of the method, and the result would be false, not an error. (The Object version is only true if the parameters are the same object - the same place in memory.) This is the same as == for Java objects.
I included the obvious version of an add method that is a direct translation of my earliest Python version, before we considered multiple types, or brought in dunder methods.
An exercise below is to write an overloading of add that takes an int parameter.
You could go on to do a bunch more if you wanted routine practice: binary operations subtract, multiply, divide, with assorted overloadings, and unary operations (no extra parameter): negate, inverse (1/this), ....
A very important method is:
/** * Compare two fractions. * @param r that is compared to this rational. * @return a number that is positive, zero or negative, respectively, if * the value of this is bigger than r, * the values of this and r are equal or * the value of this is smaller than r. */ public int compareTo(Rational r) { return num*r.den - den*r.num; }
This is related to the discussion of the Python helping method _ineq. A boolean comparison between a/b and c/d (smaller, bigger or equal) is the same as the comparison between a/b - c/d and 0, and with positive denominators, this is the same as the comparison between ad - bc and 0. Hence the sign of int expression ad - bc is all we need to understand the ordering of a/b and c/d!
So Rational r1 is greater than Rational r2 if r1.compareTo(r2) > 0. All the other comparison symbols, <, <=, >=, ==, != can be used similarly!
This idea is useful for any kind of object you would like to do comparisons for!
Much like the Python dunder comparison method __le__ gives us a "natural ordering" of a type that we can use with the standard list sort method, we can use the compareTo method in Java.
Not quite so fast: Python assumes if __le__ is defined, then previously defined methods, like for sorting a list, can just use it. Python is duck typed: if you see a method with a name you were expecting, just use it. This name comes from: "If it quacks like a duck, it's a duck!" With a weird name like __le__ that may not sound so unreasonable! Java, however, is more conservative: just because a class has a method name (and signature) that you were expecting, it may be a coincidence, and not really useful for the purpose you want: Java has a way to confirm the intention with an interface.
The word interface is used several ways in programming:
Look back now at the class heading of the Java Rational class:
``class Rational implements Comparable<Rational> ``
The implements Comparable<Rational> refers to the confirmation we need, for instance, to be able to use the standard Java sorting method Collections.sort for Rationals.
An interface is a specification or contract, that says any object that claims it implements this interface must provide some list of method signatures. Many objects implement the Comparable interface.
The Comparable interface is generic, so we specify the type to compare as in the heading for Rational: Comparable<Rational>.
It specifies the existence of a method with exactly the same signature as our Rational method compareTo. It also specifies exactly what is explained in the documentation string that I included: exactly when the value returned should be positive, negative, or zero.
It is the same idea for other classes: The String class implements Comparable<String>, so it requires the method heading
public int compareTo(String s)
and the same documentation except with String replacing Rational.
Since the String class does implement Comparable<String>, Collections.sort works on String lists.
I am not showing you the exact generic Comparable interface code, because generics complicate it with syntax we have not had. Later we will look at interfaces you might write.
Because of the implements Comparable<Rational> in the class heading, when the compiler executes, it confirms that the Rational class does in fact have the method with the signature required, and that it behaves in a way consistent with the intention of the interface. And then this class can be used with any method that requires a Comparable parameter.
We can test the sorting with Rationals. Here is SortRational.java:
// Illustrating that the Comparable interface // allows sort to work.''' import java.util.Collections; import java.util.ArrayList; public class SortRational { public static void printRationals(ArrayList<Rational> rList) { for (Rational r : rList) System.out.print(" " + r); System.out.println(); } public static void main(String[] args) { ArrayList<Rational> rList = new ArrayList<Rational>(); Rational[] rArray = {new Rational(5, 3), new Rational(34, 14), new Rational(-7, 4), new Rational(4, 6)}; for (Rational r : rArray) rList.add(r); System.out.print("Original list: "); printRationals(rList); Collections.sort(rList); System.out.print("Sorted list: "); printRationals(rList); } }
Write an overloaded version of add that takes an int as parameter.
We have seen other classes convert a string with a static method ( like Double.parseDouble). We could do the same thing in the Rational class, starting from the heading
public static Rational parseRational(String s)
Note that you can take the basic logic from the Python constructor.
Use the parsing method to modify SortRational.java to take user input for the list, something like the Python version, sortrationals.py.
Get a sequence of Rationals from the user like in the previous exercise. Print their sum.
If you modify`Rational.java <../examples/Rational.java>`_, eliminating implements Comparable<Rational>, what happens when you compile Rational.java and SortRational.java?
Write a program to compare == equality with Rationals and also the equals method.
The previous section described how to use the existing Comparable interface. You can also create them, with what looks like a stripped down class definition: interface replacing class in the heading, no instance variables, method headings for public methods followed by a semicolon and no body. Example:
public interface Response { boolean execute(Command cmd); // methods assumed public String getCommandName(); String help(); }
This comes from the large example, http://anh.cs.luc.edu/170/examples/zuul/src/.
In my refinement of Zuul we will see multiple kinds of objects that all satisfy the Response interface. Response is an interface type. Java allows declarations using an interface as the type being declared. It is not a class type, but for safe compilation and execution it does make sense: We can have an array of different kinds of objects, each satisfying the Response interface, declared with type Response[], like:
Response[] responses = { ... }; // details omitted: See Zuul for (Response r : responses) { String s = r.getCommandName(); // legal by the contract r.show(); // COMPILER ERROR: show() isn't in the interface ... }
The compiler will know that elements of such an array satisfy a method that is listed in the interface contract, but no others, so they are the only ones the compiler will allow for an object declared explicitly by its interface type.
We will see a more sophisticated use of a user defined interface in the game of Zuul discussion.
In scientific computing you often want a function passed as a parameter. With Python we can easily pass a function as a parameter. There is involved documentation relating Java 8+ lambda expressions to single method interfaces, but we will stick to an approach where just use an interface makes sense, with two methods: We will assume that you will want not only a function, f, but also be able to generate a description string, with a toString. We can package both in an interface that we will name FunctionDoc for the expected signatures in such a class:
public interface FunctionDoc // interface, not class { // NO constructor double f(double x); //semicolon, not body String toString(); // also assumes instance } // methods are public
This is in file FunctionDoc.java The comments indicate most of the restrictions needed for an interface file:
If FD is declared as an instance of a FunctionDoc, then we can make calls like FD.f(2.5) and FD.toString(), but NO other methods.
Here are two possible classes implementing the FunctionDoc interface:. You can skip the logical details if you like: The essential things about these example interfaces are that they both have the two required method signatures, though implemented in very different ways, and that implementing classes like Polynomial can have many further components (instance variables, constructor, other methods):
class TrigSum implements FunctionDoc { public double f(double x) { return Math.sin(x) - Math.cos(x); } public String toString() { return "f(x) = sin x - cos x"; } } class Polynomial implements FunctionDoc { private double[] coef; // decreasing powers /** Polynomial given decreasing coefficient powers */ public Polynomial(double[] coefficients) { coef = shortenArray(coefficients); } /** return array without leading 0's */ public static double[] shortenArray(double[] coefficients) { // not in interface - OK int len = coefficients.length, deg = len - 1; while (deg > 0 && coefficients[deg] == 0) deg--; // leading 0's are redundant double[] nums = new double[deg+1]; for (int i = 0; i <= deg; i++) nums[deg - i] = coefficients[len - 1 - i]; return nums; } public double f(double x) { double sum = 0; for (double c : coef) sum = sum * x + c; return sum; } public String toString() { String s = "f(x) = "; int deg = coef.length - 1; for (int i = 0; i < deg; i++) if (coef[i] != 0) s += String.format("%fx^%s + ", coef[i], deg - i); return s + coef[deg]; } }
The first example in my Java Companion section Notes on While Loops was on the bisection method for approximating roots of a real function.
The use was rather limited, since the specific function being checked was hard-coded into the algorithm.
With FunctionDoc interface, we can have a much more generally useful method:
As examples of interface use, the important thing in the code the use of a parameter of the FunctionDoc type, that can be passed on to a further method, and also that these methods call only the two expected methods for the FunctionDoc interface:
import java.lang.Math; public class ZeroFind { public static void main(String[] args) { FunctionDoc p = new Polynomial(new double[] {1, 0, -2}); // This should have solution x = +/- square root of 2 showBisection(p, 0, 2); // sqrt(2) showBisection(p, 0, 1); // no root p = new Polynomial(new double[] {1, -3, -3, 1}); // This should have solution x = -1. -1 - 3 + 3 + 1 = 0 showBisection(p, -10.0, 10.0); showBisection(new TrigSum(), 0, 1); // pi / 4 } /** Shows parameters and results of bisection function. */ static void showBisection(FunctionDoc FD, double a, double b) { System.out.println("\nLet " + FD); // use toString implicitly System.out.format("Looking for a root in [%f, %f].%n", a, b); double root = bisection(FD, a, b, true); if (!Double.isNaN(root)) // Nan not equal to itself! System.out.println ("An approximate root is " + root); else System.out.println ("Could not find the root: Endpoints same sign."); } /** This bisection method returns the best double approximation to a root of FD.f. Returns double.NaN if FD.f(a) and FD.f(b) have the same sign. Does not require a < b. Shows steps if showSteps. */ public static double bisection(FunctionDoc FD, double a, double b, boolean showSteps) { if (FD.f(a) == 0) return a; if (FD.f(b) == 0) return b; if (Math.signum(FD.f(a)) == Math.signum(FD.f(b))) return Double.NaN; // can't do bisection double c = (a + b) / 2; // If no f(c) is exactly 0, iterate until the smallest possible // double interval, when there is no distinct double midpoint. while (c != a && c != b) { if (showSteps) System.out.format ("a = %.17g b= %.17g diff = %.17g%n", a, b, b-a); if (FD.f(c) == 0) { return c; } if (Math.signum(FD.f(c)) == Math.signum(FD.f(a))) a = c; else b = c; c = (a + b) / 2; } return c; // double value matches an endpoint here } }
This is in file ZeroFind.java, along with the definitions of TrigSum and Polynomial.
Note again that the implements FunctionDoc at the end of the headings of the Polynomial and TrigSum classes are essential. You could try temporarily removing an implements clause, and see the compilation fail.
In order to keep all three classes in the same file, the latter two cannot be declared public. If I wanted TrigSum and Polynomial to be available to other programs, I would need each to be public and in its own file.
This has been an introduction to interface basics. We are not covering a number of more advanced features in Java 8+:
Suppose you want to have a program that lets users select games to play, where each game is in a class that has a standard toString method describing itself, and a play method, with no parameters, that returns an int final score for the game. Write an interface called Game.java for this.
Below is a simple program that could use Game1, Game2 and Game3 classes satisfying the interface, and using my UI class. For the program below to be consistent with the next problem, we assume Game1 has a constructor with one int parameter:
public class PlayGames { public static void main(String[] args) { Game[] games = {new Game1(3), new Game2(), new Game3()}; int nGames = games.length; int cumulativeScore = 0; String[] prompts = new String[nGames+1]; prompts[0] = "0: Quit"; for (int i = 0; i < nGames; i++) { // use toString methods prompts[i+1] = String.format("%s: %s", i+1, games[i]); } int n; do { for (String prompt: prompts) { System.out.println(prompt); } n = UI.promptInt("Game number: "); if (n != 0) { cumulativeScore += games[n-1].play(); System.out.println("Your cumulative score is now: " + cumulativeScore); } } while (n != 0); System.out.println("Thanks for playing!"); } }
What is wrong with this attempt at a Game1 class for part a, that has to do with interfaces (not just being boring)?
import java.util.Random; public class Game1 { private Random rand; private int nTry; public Game1(int tries) { nTry = tries; rand = new Random(); } public int play() { int score = 0; for (int i = 0; i < nTry; i++) { int x = rand.nextInt(100); int y = rand.nextInt(100); int ans = UI.promptInt(x + " + " + y + "= "); if (ans == x + y) { score++; System.out.println("Correct!"); } else { System.out.println("No, the answer is " + (x+y)); } } return score; } public String toString() { return "Do " + nTry + " small arithmetic sums."; } }
If you want to test all of this together, without creating Game2 and Game3, you could replace new Game2(), new Game3() in the initialization of games by new Game1(2), new Game1(4). This just makes even more boring games. A further extension (not required) would be to make more quite different games to include in the initialization of games.
Download the three assignment files in: Booklist assignment stub files. Note that the stub files should all compile as is (they just are not be useful to run). To make that happen for stubs of methods that return values, it is essential to have a return statement. So the method stubs that are not void have a dummy return statement, returning something legal but not useful. Such return statements are labeled "so the stub compiles". Be sure to change those lines!
Book.java completed: A simple class creating instances with author, title, and year.
BookList.java completed: A class creating instances storing a collection of Books and selecting books from the list
TestBookList.java completed: A program with a main method that creates a BookList, Books, adds Books to the BookList, and tests BookList methods clearly and completely.
If you have a partner, have each team member, separately, submit a text file named "log.txt" to Sakai also. The log should indicate
Though only one copy of the Java files should be submitted by one partner, both should separatately write and submit their own log.txt file.
If you do not have a partner, you may just put parts 1-2 in comments at the top of Book.java.
Make development and debugging easier for yourself! Complete the Book class first, and then BookList. As you finish individual classes or even methods, one at a time, test them (either in jShell or with temporary short versions of TestBookList).
See the stub file provided. It has instance fields for the author, title, and year (published). It needs a body for its constructor:
public Book(String author, String title, int year)
It needs the bodies for three standard (one line) accessor methods
public String getAuthor() public String getTitle() public int getYear()
and one method to format all the information as a String
public String toString()
The toString method should return a (single) String on three lines. For example if the Book fields were "David Barnes", "Objects First", and 2008, the String would appear as
Like other toString methods this method does NOT print to System.out. It RETURNS a (single) String.
It has only one instance variable, already declared by:
private ArrayList<Book> list;
It already has a constructor written, creating an empty list, with heading
public BookList()
Code the bodies of the public methods with these headings provided:
/** Add a book to the list. */ public void addBook(Book book) /** List the full descriptions of each book, with each book * separated by a blank line. */ public void printList() /** List the titles (only) of each book by the specified author, * one per line. */ public void printBooksByAuthor(String author) /** List the full descriptions of each book printed * in the range of years specified. */ public void printBooksInYears(int firstYear, int lastYear)
For instance if the list included books published in 1807, 1983, 2004, 1948, 1990, and 2001, the statement
printBooksYears(1940, 1990);
would list the full book description for the books from 1983, 1948, and 1990. Hint: Remember you should have already written the Book class's toString method.
It should have a main program that creates a BookList, adds some books to it (more than in the skeleton!), and convincingly displays tests of each of BookList's methods that exercise all paths through your code. Check for one-off errors in printBookYears. With all the methods that print something, make the results easy to understand. Do print a label, as in the skeleton, before printing output from each method test, so that the correctness of the test can be seen without any knowledge of the source code.
Write a toString method for the BookList class that returns (not prints) the content described by the printList method above as a single String (including a final newline). Also change the printList method body to the one line:
System.out.print(this);
The print and println methods print objects by using their toString methods. In your testing class, test the BookList toString method by converting the resulting Booklist description string to upper case before printing it (which should produce a different result than the regular mixed case of the printList method test).
Here are the main topic headings: