Contents:
- Part 1: Choose a Development Environment
- Part 2: Obtaining files from GitLab
- Part 3: Fix Buggy Code—HolaWorld
- Part 4: A new Java class—RandomHello
- Part 5: Testing Java Code with JUnit
- Part 6: Answering Questions About the Code
- Part 7: More Java—Balls and Boxes
- How to Turn In Your Homework
- Optional: Debugger Tutorial
The purpose of this homework is to help you set up your development environment, get reacquainted with Java, and start getting familiar with tools you will use for the rest of the course. Although the homework description is long, we expect the step-by-step instructions will make doing the homework less overwhelming than reading it may be.
This homework links to (the middle of) some other handouts at many points. For convenience, here is the list of these handouts:
- Project Software Setup
- Editing, Compiling, Running, and Testing Java Programs
- Version Control Reference
- Assignment Submission
- Google Java Style Guide
(It also links to various Java files and API documentation files that are not listed above.)
You are free to get help from anyone on any portion of this homework. That is, the standard collaboration policy does not apply to this homework. You will need to know this material for the remainder of the quarter, however, so make sure you understand thoroughly the tools and concepts in this homework.
Part 1: Choose a Development Environment
Read the Project Software Setup document and decide where you will do your work.
Also choose what Java development tools you will use. We recommend using the IntelliJ IDE in CSE 331, but the choice is up to you. We also provide instructions for working from the Linux command line. If you choose a different IDE, you will need to figure out how to do the operations from it, or use the command line. IntelliJ is the most popular Java IDE. If IntelliJ is new to you, allocate some time to learn its features. That time will pay off mdash; probably this quarter, and definitely in the future.
Once you have chosen and prepared a development environment, you should set it up for CSE 331 use. See Configuring IntelliJ for 331 and follow any instructions that are applicable. (Note: if you switch development environments later in the quarter, revisit these instructions.)
Part 2: Obtaining files from GitLab
Your homework for this quarter will all be stored and tracked in a Git repository. Since this is the first time you're using this repository, you'll need to "clone" it to get a copy on your local computer. Follow the cloning instructions in the Project Software Setup handout. Also familiarize yourself with the other tools and commands listed in that document, and the concepts of Git in general. Note that, if you attended section, you've already done this with the group.
Part 3: Fix Buggy Code—HolaWorld
For this problem, you will fix some buggy code we provide.
Editing and Compiling Source Files
See Editing and Compiling Source Files to help learn how to perform the following basic tasks: adding new files to your directory structure, compiling Java code, and reading the Java compiler's output (which may indicate errors).
Also read the Google Java Style Guide which describes some standards for Java style used by Google. We expect you to have consistent, readable style throughout the quarter. This style guide may be a reasonable place to start if you're not sure how to achieve that, but we're not requiring that you exactly follow any specific part of that guide. The most important thing to have is consistent style, including style that is consistent with the starter code you may be modifying.
Fixing HolaWorld
Try to compile the provided code in HolaWorld.java
. You should see
compilation errors for this file. (And possibly in RandomHelloTest.java
too; if so, ignore these for now since we will fix them in the next part.) In
particular, the lines:
System.out.println(world.);
and:
return SPANISH_GREE;
are problematic. (You might see the second error only after you've fixed the first
one.) If you are using IntelliJ, these errors will be marked with red squiggly
lines in HolaWorld.java
.
Fix these errors and run HolaWorld
by running the
setup:runHolaWorld
gradle target. See the
Editing and Compiling Source Files
handout for more information on running gradle targets.
After you've fixed the errors and run the code, it would be a good time to commit your changes to your repository and push them to GitLab.
Part 4: A new Java class—RandomHello
Create a Java class with a main
method that will randomly choose, and
then print to the console, one of five possible greetings.
Create the file
RandomHello.java
, which will define a Java class
RandomHello
that will reside in the Java package setup
;
that is, its file name is
.../cse331-QUARTER-YourCSENetID/hw-setup/src/main/java/setup
/RandomHello.java
. Choose "Add" on the popup that asks "Do you want to add
the following file to Git?" This will include your newly created file to your repo
on git so it can be saved, accessed, and graded.
Java requires every runnable class to contain a main
method whose
signature is public static void main(String[] args)
(for example, see
the main
methods in HelloWorld
and in
HolaWorld
).
A code skeleton for the RandomHello
class is shown below. IntelliJ will
generate an empty class skeleton for you if you use it to create the new
RandomHello
class. You're welcome to use that and fill in the entire
class yourself, or just copy/paste the skeleton we provide below and work from
there.
RandomHello.java
:
package setup; /** RandomHello selects and prints a random greeting. */ public class RandomHello { /** * Prints a random greeting to the console. * * @param args command-line arguments (ignored) */ public static void main(String[] args) { RandomHello randomHello = new RandomHello(); System.out.println(randomHello.getGreeting()); } /** @return a greeting, randomly chosen from five possibilities */ public String getGreeting() { // YOUR CODE GOES HERE } }
This skeleton is meant only to serve as a starting point; you are free to organize your code as you see fit.
No Need to Reinvent the Wheel
Don't write your own random number generator to decide which greeting to select. Instead, take advantage of Java's Random class. (This is a good example of the adage “Know and Use the Libraries ” as described in Chapter 9 of Joshua Bloch's Effective Java, 3rd edition. Learning the libraries will take some time but it's worth it!)
Add the following to the body of your getGreeting()
method:
Random randomGenerator = new Random();
This line creates a random number generator (not a random number, but a
Java object that can generate random numbers). In IntelliJ, your code may
be marked as an error by a red underline. This is because the Random
class is defined in a setup that has not yet been imported (java.lang
and setup
are the only packages that are implicitly imported). Java
libraries are organized as packages and you can only access Java classes in
packages that are imported. To import java.util.Random
, add the
following line under the package setup;
declaration):
import java.util.Random;
This will import the class Random into your file.
Using java.util.Random
Read the documentation for Random
's nextInt(int n)
method
by going to the
Java API
and searching for Random
using the search bar in the top right-hand
corner. In IntelliJ, you can hover over the class or method name and press a hotkey
to view documentation (check your IntelliJ settings/preferences, under "Keymap" to
find your specific hotkey for "Quick Documentation").
Use the nextInt(int n)
method to choose your greeting. You don't have
to understand all the details of its behavior specification, only that it returns a
random number from 0 to n-1.
One way to choose a random greeting is using an array. This approach might look something like:
String[] greetings = new String[5]; greetings[0] = "Hello World"; greetings[1] = "Hola Mundo"; greetings[2] = "Bonjour Monde"; greetings[3] = "Hallo Welt"; greetings[4] = "Ciao Mondo";
The main
method in the skeleton code above prints the value returned by
getGreeting
. So after you complete getGreeting
, when the
class is run the main
method will print that greeting.
When you are finished writing your code and it compiles, run it using the
runRandomHello
target several times to ensure that all five greetings
can be displayed.
Again, now would be a good idea to add your new class to version control, commit it, and push it.
Part 5: Testing Java Code with JUnit
Testing is essential for writing quality software. JUnit is a framework for creating unit tests in Java. A unit test checks that one specific, small piece (a "unit") of functionality is working correctly. (JUnit can be used to create other types of tests, too.) This problem provides a quick overview and simple example of how JUnit works. (Testing will be more significant in later assignments.)
Open both hw-setup/src/main/java/setup/Fibonacci.java
and
hw-setup/src/test/java/setup/FibonacciTest.java
.
From the comments, you can see that FibonacciTest
is a test of the
Fibonacci class.
Now run the JUnit test
setup.FibonacciTest
. You'll notice that it's failing its tests! Examine
the test code and gradle's test output to understand why the tests are failing, then
use that information to find and fix the bugs in Fibonacci so it's passing all the
tests. The tests are correct, so you shouldn't modify FibonacciTest at all,
but it's probably useful to look at the code and see what it's doing.
Now look at the JUnit tests that we wrote for HolaWorld
and
RandomHello
. They are called HolaWorldTest
and
RandomHelloTest
, respectively. Ensure that your modified code passes
these tests before you turn in your homework.
Part 6: Answering Questions About the Code
Locate the answers.txt
file in your repo; you'll find it in
hw-setup/src/main/java/setup/
. In that file, write your answers to the
following questions using a few sentences max for each question.
- Why did Fibonacci fail the testThrowsIllegalArgumentException test? What (if anything) did you have to do to fix it? If you did not have to change anything to fix this issue, explain why.
- Why did Fibonacci fail the testBaseCase test? What (if anything) did you have to do to fix it? If you did not have to change anything to fix this issue, explain why.
- Why did Fibonacci fail the testInductiveCase test? What (if anything) did you have to do to fix it? If you did not have to change anything to fix this issue, explain why.
Part 7: More Java—Balls and Boxes
Until now, we have only been introducing tools. This -part delves into a more interesting programming exercise. This part will likely be somewhat challenging for most of you. Don't be discouraged. We're here to help, and we expect that time spent now will pay off significantly during the rest of the course.
-
Warm-Up: Creating a Ball:
Look at
Ball.java
. A Ball is a simple object that has a volume-
Determine what is wrong with
Ball.java
and fix all the problems with it.
We have included a JUnit test BallTest to help you. Moreover, one of IntelliJ's warnings should help you find at least one of the bugs without even referring to the JUnit results.
-
Determine what is wrong with
-
Using Pre-Defined Data Structures:
Next, you'll be implementing the majority of a class called BallContainer. As before, skeleton code is provided (see
BallContainer.java
). A BallContainer is a container for Balls. BallContainer should support the following methods: your task is to fill in the code to implement these methods correctly:- add(Ball)
- remove(Ball)
- getVolume()
- size()
- clear()
- contains(Ball)
The specifications for these methods are found in the API documentation (javadoc comments) for BallContainer.
BallContainer uses a
java.util.Set
to keep track of the balls. Using a predefined Java data structure saves significant work. Before implementing each method, read the documentation forSet
. Some of your methods will be as simple as calling the appropriate methods for Set .To help you out, we have included a JUnit test called
BallContainerTest
.Before you start coding, please take time to think about the following question.
There are two obvious approaches for implementing getVolume():
-
Every time
getVolume()
is called, go through all the Balls in theSet
and add up the volumes. (Hint: one solution might use a for-each loop to extractBall
s from theSet
.) -
Keep track of the total volume of the Balls in
BallContainer
whenever Balls are added and removed. This eliminates the need to perform any computations whengetVolume
is called. (Hint: you are welcome to add new fields to the BallContainer class as necessary.)
Which approach do you think is the better one? Once you've determined which you think is better, or come up with another solution that you think is better than either of the above, implement
getVolume()
alongside the rest ofBallContainer
-
Implementing a Box:
In this problem, you will do a little more designing and thinking and a little less coding. You will implement the Box class. A Box is also a container for Balls. The key difference between a
Box
and aBallContainer
is that a Box has only finite volume. Once a box is full, we cannot put in more Balls. The size (volume) of a Box is defined when the constructor is called:public Box(double maxVolume);
Since a
Box
is in many ways similar to aBallContainer
,Box
's implementation delegates to a privateBallContainer
. This code reuse simplifiesBox
, many of whose methods can simply “delegate” to the corresponding method inBallContainer
. This design of having one class contain an object of another class and reusing many of the methods is called composition or delegation.You are welcome to add new fields to the BallContainer class as necessary.
(Optional Note: You may wonder why we did not make
Box
extendBallContainer
via inheritance. That is, why did we not makeBox
a subclass ofBallContainer
? The reason is thatBox
is not a true subtype ofBallContainer
because it is in fact more limited thanBallContainer
(aBox
can only hold a limited amount). A user who uses aBallContainer
in their code cannot simply substitute thatBallContainer
with aBox
and assume the same behavior. (The code may cause the Box to fill up, but he did not have this concern when using aBallContainer
). For this reason, it is unwise to makeBox
extendBallContainer
. We will discuss subtyping much more deeply later in the course.)In addition to the constructor described above, you will need to implement the following new methods in
Box
:- add(Ball)
- getBallsFromSmallest()
The specifications for these methods can be found in the API documentation (javadoc comments) for
Box
.You shouldn't need to change your implementation of
BallContainer
orBall
for this problem. In particular, you should not implement the Comparable interface. Instead, you will want to use a Comparator object to handle comparisons between elements in the Box. See the Java API documentation for Comparator and java.util.Collections for details.Comparator
, though not seen in CSE 143, is a companion interface toComparable
and is used throughout the Java libraries --take the sort methods described in the Collections documentation as an example. In general, you are often presented with a choice between- making an object "Comparable" where the object itself "knows" how it is supposed to be compared, or
- implementing a separate "Comparator" object where the comparison logic is separated from the object being compared.
Comparator
allows you to specify the comparison logic for a certain object. For example, you may want to sort a collection of Strings by length while the natural order of Strings is alphabetic. You can create aComparator<String>
object with logic to compare by length and pass that object intoCollections.sort
. We'll study the merits of either choice in greater detail later in this class. For now, consider the following when trying to make a decision between the two: is the object you're working with likely to always be compared in the same way, or are there multiple "comparisons" that make sense? (For example, comparing Strings by length or alphabetically). This question will come up in later assignments (and beyond!), so make sure you understand how to create a Comparator and remember to consider the question when you're working with comparisons of objects in the future.A few hints to consider before you start writing code:
- You shouldn't need to implement your own sorting algorithm. Instead, take advantage of the Java API (remember: "Know and Use the Libraries").
-
If you make any changes to
BallContainer
orBall
for this problem, then explicitly document what changes you made and why inanswers.txt
. -
Be cautious if you use Java's
TreeSet
. Remember thatTreeSet
does not store duplicates, and if you provide aTreeSet
with aComparator
, it will use thatComparator
to determine duplication. See the TreeSet API documentation for more details. (TreeSet
is much used in CSE 142/143, but more rarely used in practice.) -
Before you start working on
getBallsFromSmallest()
, we recommend strongly that you consider using Iterator . - The JUnit test BoxTest.java should help you. However, we do not guarantee that the tests we provide will catch all bugs.
- Don't forget to commit and push your code occasionally. You can commit and push changes as many times as you want while working on the assignment. That is a good way to store backup copies of your work in GitLab.
How to Turn In Your Homework
At the end of each assignment, you must refer to the Assignment Submission Handout and closely follow the steps listed to submit your assignment. Do not forget to double check your submission as described in that handout - you are responsible for any issues if your code does not run when we try to grade it.
Use the tag name hw3-final for this assignment. To verify your
assignment on attu, you can use any of the gradle tasks described elsewhere in this
assignment: hw-setup:runHelloWorld
, hw-setup:runHolaWorld
,
and hw-setup:runRandomHello
.
Don't forget to write your answers to part 6 in the provided answers.txt file in your repo.
We should be able to find the following in the hw-setup/src
directory
of your repository:
-
HolaWorld.java
that works as described in Part 5 with no compilation errors -
RandomHello.java
that prints out one of five random messages when itsmain
method is executed -
Fibonacci.java
that passes the three tests inFibonacciTest java
(Note that you should not editFibonacciTest.java
to accomplish this task.) -
Ball.java
,BallContainer.java
andBox.java
that pass their respective JUnit tests (Again, you should not modify the JUnit tests, though you are encouraged to read the code to understand what they test.)
Optional: Debugger Tutorial
In this part, you will learn about IntelliJ's built-in debugger. A debugger can help you debug your program by allowing you to "watch" your program as it executes, one line at a time, and inspect the state of variables along the way. Using a debugger can be much more powerful and convenient than littering your program with statements that print output.
One of the most useful features of a debugger is the ability to set breakpoints in your program. When you run your program through the debugger, it will pause when it reaches a line with a breakpoint. You can inspect the current state of variables, then continue running your program normally or step through one line at a time to watch the variables change.
You should reference the IntelliJ Debugger Documentation throughout this section to learn how to use the debugging tools in IntelliJ. We won't be explaining every step of using the debugger in this section, so this is a good opportunity to practice reading documentation!
-
Open
Adder.java
. This simple program is supposed to print the sum of two user-provided integers. Try running it a few times (or reading the source code) and you'll see that it doesn't behave as expected. -
Click in the left-hand margin of the class next to the line
return x - y;
A red circle should appear, indicating a breakpoint. (Clicking again removes the breakpoint.) -
Run the program in debug mode by right-clicking on
Adder.java
and clicking “Debug...”. As before, enter two ints (say, 3 and 4) in the console when prompted. When your program hits the breakpoint, IntellIJ will pause the program and a debugging window will show up at the bottom of your screen. -
The debug perspective looks overwhelming at first, but don't worry! Look at the
ldquo;Variables” pane and you'll see the names and values of all
variables in the current context (which, at this breakpoint, is the
computeSum
method). The left panel in the frames tab shows where the program is currently paused, where the current method was called, and so on. (This is called the stack trace). Double-click on a method name to see the corresponding line in your source code. Finally, the console tab (next to “Debugger,” right above the “frames” area of the debugger) shows the console window.What are all the names and values listed in the “Variables” panel? What does the “frames” tab list as the current method and line number? (Write down the text that was highlighted when the Debug perspective first opened.)
-
Immediately above the “Debugger” panel, and on the left are several
groups of buttons for running your program. Mouse over each one for a
description. “Resume” (green arrow) causes your program to continue
executing normally until it finishes or hits another breakpoint. If you want to
monitor what happens shortly after the breakpoint, use “Step Into”
and “Step Over” (blue arrows near the top of the debugger).
“Step Over” executes the current line and pauses on the next line.
“Step Into” enters any method called on the current line so you can
execute that method line-by-line. (To finish the current method and jump back to
the caller, use “Step Out.”) Hit “Step Over” once to
execute the
return
statement and exitcomputeSum
. Hit “Step Over” again to progress to the next line.What are all the names and values listed in the “Variables” panel after each of the two step overs?
- Hit “Resume” to allow the program to finish executing.
Being able to step through each piece of code and examine what's going on is a very powerful way to understand what your code is doing any why things may be going wrong. The IntelliJ debugger can to so much more than just what we've discussed here, so make sure to read the documentation linked above to learn more about what it can do to help you fix any problems you might have in your code.