Contents:

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:

(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.

We encourage you to finish this homework well before its due date.

Problem 1: Choose a Development Environment

Read the Working At Home 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 — 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 Starting IntelliJ and follow any instructions that are applicable. (Note: if you switch development environments later in the quarter, revisit these instructions.)

Problem 2: Obtaining files from GitLab

Follow the setup instructions in the Version Control handout. Also familiarize yourself with the commands listed in that document.

Problem 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 CSE 331 Java Style Guide, which describes some standards for Java style that we will expect you to follow.

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, and HolaWorld.java itself will be marked with a red crossmark in the Package Explorer.

Fix these errors and run HolaWorld.

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.

Problem 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 hw1; that is, its file name is .../cse331-18au-$USER/src/main/java/hw1/RandomHello.java.

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 some of this skeleton for you if you use IntelliJ to create the new RandomHello class.

RandomHello.java:

package hw1;

/** 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 7 of Joshua Bloch's Effective Java. 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 package that has not yet been imported (java.lang and hw1 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 hw1; declaration):

import java.util.Random;

This will import the class Random into your file. In IntelliJ, to automatically add all necessary imports and remove unused imports, either choose "Code | Optimize Imports" on the main menu, or type Ctrl+Alt+O to optimize your imports (documentation for optimizing imports). Because there is only one class named Random, IntelliJ will figure out that you mean to import java.util.Random and will add the above line of code automatically. (If the name of the class that needs to be imported is ambiguous — for example, there is a java.util.List as well as a java.awt.List — then IntelliJ will prompt you to choose the one to import.)

Using java.util.Random

Read the documentation for Random's nextInt(int n) method by going to the Java API and selecting Random from the list of classes in the left-hand frame. In IntelliJ, you can hover over the class or method name and press CTRL-Q on Linux/Windows or CTRL-J on Mac.

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 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.

Problem 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 given method. (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 src/main/java/hw1/Fibonacci.java and src/test/java/hw1/FibonacciTest.java. From the comments, you can see that FibonacciTest is a test of the Fibonacci class.

Now run the JUnit test hw1.FibonacciTest.

If you are using a textual version of JUnit, it will produce output to the console indicating failed tests. If you are using a GUI version of JUnit, a window or panel with a menacing red bar will appear, indicating that some of the tests in FibonacciTest did not complete successfully. The top pane displays the list of tests that failed, while the bottom pane shows the failure trace for the highlighted test. The first line in the failure trace contains an error message that explains why the test failed. (The author of the test code creates this error message.)

If you click on the failure testThrowsIllegalArgumentException, the bottom pane will switch to the appropriate error message. In this example, the first line of the failure trace shows that Fibonacci.java improperly threw an IllegalArgumentException when tested with zero as its argument. (You may have to scroll to the right to see this.) If you double-click on the name of a test in the top pane, IntelliJ will jump to the line where the failure occurred in the editor pane. Figure out the problem in Fibonacci.java, fix it, and rerun the JUnit test. IntelliJ will automatically rebuild when you make changes.

Use the information in the failure trace pane to help you continue debugging Fibonacci. Keep a record of what you did to debug Fibonacci as you will have to answer questions about your debugging experience in the next problem. After you have fixed all the problems in Fibonacci, you should see a bright green bar instead of a red one when you run FibonacciTest.

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.

Problem 6: Answering Questions About the Code

Find the file named hw1/answers.txt in your hw1 folder. Open this file and add answers to the following questions under Problem 6:

  1. Why did Fibonacci fail the testThrowsIllegalArgumentException test? What (if anything) did you have to do to fix it?
  2. Why did Fibonacci fail the testBaseCase test? What (if anything) did you have to do to fix it?
  3. Why did Fibonacci fail the testInductiveCase test? What (if anything) did you have to do to fix it?

Problem 7: More Java—Balls and Boxes

Until now, we have only been introducing tools. This problem delves into a more interesting programming exercise. This problem 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.

As you work on this problem, record your answers to the questions in the answers.txt file.

  1. Warm-Up: Creating a Ball:

    Look at Ball.java. A Ball is a simple object that has a volume.

    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.

  2. Using Pre-Defined Data Structures:

    Next, create a class 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:

    1. add(Ball)
    2. remove(Ball)
    3. getVolume()
    4. size()
    5. clear()
    6. contains(Ball)
    The specifications for these methods are found in the API documentation 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 for Set. 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.java.

    Before you start coding, please take time to think about the following question (which you need to answer in the text file):

    There are two obvious approaches for implementing getVolume():
    1. Every time getVolume() is called, go through all the Balls in the Set and add up the volumes. (Hint: one solution might use a for-each loop to extract Balls from the Set.)
    2. 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 when getVolume is called.

    Which approach do you think is the better one? Why?

  3. 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 a BallContainer 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 a BallContainer, Box's implementation delegates to a private BallContainer. This code reuse simplifies Box, many of whose methods can simply “delegate” to the corresponding method in BallContainer. This design of having one class contain an object of another class and reusing many of the methods is called composition or delegation.

    (Optional Note: You may wonder why we did not make Box extend BallContainer via inheritance. That is, why did we not make Box a subclass of BallContainer? The reason is that Box is not a true subtype of BallContainer because it is in fact more limited than BallContainer (a Box can only hold a limited amount). A user who uses a BallContainer in their code cannot simply substitute that BallContainer with a Box and assume the same behavior. (The code may cause the Box to fill up, but he did not have this concern when using a BallContainer). For this reason, it is unwise to make Box extend BallContainer. 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:

    1. add(Ball)
    2. getBallsFromSmallest()

    The specifications for these methods can be found in the API documentation for Box.

    A few hints to consider before you start writing code:

    Also, answer the following questions in your answers.txt file:

    1. There are many ways to implement getBallsFromSmallest(). Briefly describe at least two different ways. Your answers should differ in the implementation of Box, not in lower-level implementation (for example, using an insertion sort instead of a selection sort is a lower-level implementation because it does not affect how Box is implemented). Hint: think about different places in the Box class where you could add code to achieve the desired functionality.
    2. Which of the above ways do you think is the best? Why?

    There is no one correct answer. Our intent is to help you fight that urge to code up the first thing that comes to mind. Remember: More thinking, less coding.

Problem 8: Turning In Your Homework

Each homework indicates exactly what to turn in a section titled “What to Turn In”. This typically includes Java source files (that you change or create) and text files.

For instructions for turning in your homework, see the document Assignment Submission.

What to Turn In

Your TA should be able to find the following in the src directory of GitLab:

Please include your first and last name in every text file you turn in (i.e., files ending with .txt). In particular, be sure to update answers.txt to include your name as well as the expected answers.

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.