1. Reading

We assume that PRC1 covers the content of chapters 1 through 4 sufficiently. It does not hurt to skim through those first.

  • Horstmann Core Java, Ed 11, Vol 1 Fundamentals Chapter 5 sections 5.1-5.5

2. Testing

The benefits of testing Throughout the exercises of PRC1, your have become acquainted with the value of tests: You have a way of checking if your code is any good without having to test each and every part manually. You clicked on the nice TMC button in NetBeans and then you could see which parts of your code worked, and which didn’t. There is a catch, though: Out there in the real world, there won’t be any NetBeans button doing that magic for you, nor will some teacher or school provide the tests for you, so you are on your own. But fret not! Writing tests is typically much simpler than writing the actual code. At least, it is when you follow a basic set of steps:

  1. Prepare: set up the thing you want to test (as in, set up the experiment)

  2. Interact: execute the experiment

  3. Verify, Assert or Ensure that the observable result(s) is/are as expected.

  4. If it says boom, then at least you learned something …​.

Topics week 1

  • Test Driven

  • AssertJ as assertion library.

  • JUnit (5) as test framework.

3. Testing / Test Driven Development

3.1. What are tests and why do we need them?

The way that you have worked with Java so far is that you had to write some code to implement something. For example you had to implement a bird watcher system or a telephone book, in which you could save people with their telephone numbers and look them up, too. You probably created classes called PhoneBook or BookEntry, and you had variables such as String personName, int phoneNumber and maybe a list or a map which contained all the people and their phone numbers. You had to think about how the phonebook works, and then you added classes, fields (variables) and methods. Slowly but surely, your phonebook started to look more and more like a phonebook.

But how did you know that your phonebook was actually working? You could have implemented a method that didn’t work, or maybe you forgot to add people to the phonebook, so the data wasn’t actually stored. What you did was you ran the TMC tests. They kept telling you whether the phonebook was working as intended. The test said "when I look for Alice in your phonebook, I will receive Alice’s phone number 1234-567.". And if the test didn’t receive Alice’s number, it would turn red and say something like "I expected 1234-567. But I received 0042-987." Therefore, the test fails.

The tests were a really useful way for you to know that you were indeed implementing a proper phonebook. You could have just written empty classes and said "here is the phonebook!" and there would have been no way to verify that the phonebook works. The test made sure that this doesn’t happen. They also give you confidence as you code. Because every test that turns green gives you that little "yes!" feeling that you wrote something that works.

Thus, tests fulfill two functions at once: they constantly probe your implementation and check whether it works, and they make you feel more confident about your programming skills. That’s really neat!

When you were at school, you probably had to write an English exam at some point. You wrote your answers, and then the teacher graded your exam. The teacher is just like a test: she reads your exam, expecting the correct answer. For example, you wrote "Alice and Bob goes to school.". Your teacher would get the red pen and highlight the word "goes". The teacher says "I expected: Alice and Bob go to school. Instead, I received: Alice and Bob goes to school." The teachers expectation, or assertion, was not met. Therefore, you get an error. As you grow older and you become more proficient at English, you write Emails in English, perhaps for work or at university. When you make a mistake, you spot your own errors: "oh hang on, I have to use 'go' instead of 'goes' here." You have internalized a test that checks for the correct conjugation of the word. You know what to expect, and when you deviate from the expectation, you spot the error.

Many people work the same way when they write code. They say "I know what I am doing because I have experience and I know the rules." But of course, we always make mistakes. Our brains are really bad testers. We stop seeing mistakes because we feel so familiar with our code. Have you ever handed in a report for university, only to find that the lecturer finds spelling errors you swear you didn’t do? That’s what happens in programming, too.

That is why we write our own little annoying English teachers that constantly check: is this correct? Even though we know how to program, we also acknowledge that our brains are terrible testers for things that you write yourself, so we give our brains a break and write a test instead.

3.2. Test Driven Development (TDD)

In Week 9 we wrote a little phonebook and then we ran the test to check that the phonebook was working. That worked well, because we no longer have to rely on our brains to spot the errors on-the-fly. But here is another bad message: not only does our brain stop recognizing errors in our own code, but it is also automatically biased to want our own tests to pass. You spent all this time writing your phonebook, you know how it works, so you know what test to write for it.

But your brain secretly deceives you: you have implemented a phonebook, and this is the only phonebook that you know. What if you removed your code, but leave in the tests- and ask a friend to write a phonebook? They start complaining that your tests are unfair. "Why do I have to use a List? I use Maps!" your friend says. But your tests insists that the phonebook has a List. So what have you done? You have written a test that proves YOUR implementation is correct.

When you work test-driven, you don’t implement the phonebook and then test it. You first write the test for a phonebook, and then you implement it. That sounds a bit weird at first. Why write tests for stuff that’s not there yet? We know it’s going to fail! But this is what we want. We need to keep telling our brains: this does not work. Figure out a way to make it work. This way, it is much harder to get married to your own code and to stop seeing problems with your test. You have found a way to deal with the human brain. Congratulations!

3.2.1. So how do I know what to test?

There is a little (actually, it’s big) catch with TDD. When you write a test for a thing that doesn’t exist yet, how do you know what to test for? If I write a test for a phonebook that is not implemented yet, what does my test expect?

The truth is, you never get around having to make implementation decisions. We just try to minimize our margin for error. So when you start writing your first test for your (non-existing) phonebook, you HAVE to have an idea of what the phonebook does. So perhaps you start with "I expect that the phonebook let’s me look up a person. When I look up Alice, I expect Alice’s phone number.". What could such a test look like?

3.2.2. Test selection strategies

There is no golden rule that says "always start with text X, and then test Y, and finish with test Z.". Instead, we rely on heuristics: rules of thumb that guide us through the TDD process. There are several decisions that you can make when writing tests.

For instance, you can decide to first write tests for all elements of the system under test (SUT), but not go into detail yet what each SUT needs to have tested. This would mean focusing on width first, and depth later. For instance, you could write tests for the entirety of the phonebook, but you don’t, for instance, test each valid or invalid phonebook entry. The other approach would be to start with the details on one particular part of the test class, and moving on to other parts only when one section of the SUT is covered completely. In this version, you go depth-first instead of width-first. Both approaches have their merit and you need to decide which strategy is best in which situation.

Another strategy is to weigh the difficulty of each test. When you look at a list of tests to write, do you start with the easy ones that are implemented quickly, or do you start with the more difficult tests that require more implementation thinking? When you are stuck on a particular test, it might be useful to first implement a number of tests that are easier to write, so that you make progress. It might be that whatever was causing you to be stuck on that test is now easier to solve now that you have a list of other tests that you have implemented.


4. Code quality, not just in testing

One easily forgets that one of the most important qualities of good source code is readability. If the code is incomprehensible for the reader, and that might be you in 6 weeks time, than that code is not maintainable. It’s fait will be: run it as long as it takes to rewrite it, than put it in the bin.

Readability is a feature that is influenced by code style, proper naming,[1] and documentation.

In all cases you may assume a versed reader of java code. You are about to be one.

If you write code and than have to explain how it works, rewrite it. Typically you selected the wrong names. Code should be readable as a story.

4.1. Checkstyle and friends

Apart from testing, the java world has a plethora of quality verification tools. Many of these can be configured as maven plugins and indeed, the sebi pom has a few.

checkstyle Does a source code analysis and comments on the way your code is formatted, placement of your curly braces, white space, naming conventions, javadoc etc.
pmd Analyses the source code but now scans for coding defects.
spotbugs The successor of findbugs does code analysis on the byte code level.

You can easily run all these plugins by calling maven as mvn site. It will, on success produce a website inside your target folder, aptly calls target/site. Use you browser to open it or open the target/site folder in the files tab of NetBeans-IDE and right-click view in index.html.

You can find the sebivenlo_checks.xml [2] check-style rules on this server by following the link 9 word to the left.

The source code repository has been configured to check your code with check-style in a pre-commit hook. We insist that you format your code properly and add documentation where required. We use the google java style, with a few tweaks.

The bottom line is: Apply the proper Java code style, or your code will not be accepted by the versioning system.

The tools above are complementary and help to improve your code quality. Since you are supposed to be a learner, accept the given advise, even if it is from a program, and use it to improve your coding style.

If you are about to commit your solution to the PRC2 repo, and all tests pass, run mvn site in the root of your project or run maven from netbeans with target site:site. If all is well, you have a nice website with a lot of reports, including dependency info, javadoc of both business code and test code, and checkstyle, pmd, and spotbugs reports.

Note that some plugins will refuse to work when the project does not pass the tests.

If your commit is not accepted due to checkstyle issues, look at the checkstyle report. You can also run checkstyle from NetBeans/maven : run maven goal checkstyle:checkstyle. If it is not okay, you will see the problems in the output window.

4.2. Where is javadoc required?

The short list is: Javadoc is required for all public or protected methods and constructors.

  • On the class (or interface) declaration.

  • all methods (or constructors) with more than 2 lines of code.

No javadoc is required for methods annotated with @Test or @ParameterizedTest or methods that overwrite a definition in a super type (class or interface) which is already documented.

Of course you can teach NetBeans to have all your code comply with a code style that is acceptable by checkstyle. Here you can find my editor settings. Import them into NetBeans and after that, you can make your code look really good (to my eyes and to checkstyle), by pressing ALt+Shift+F.
On a Mac that is different. But you knew that.

5. Slides

Walking-through exercise: Phonebook

In this exercise, we will write our own phonebook. As this is the first exercise, we will work through the solution and have some accompanying text for you to read. Please read the text and don’t just copy+paste the code, especially when you are struggling with something.

In week 9 of PRC1 you were asked to implement a more advanced version of the phonebook of week 6. Let’s have a look at the exercise again. The exercise is nice, but we had to make sure that the User Interface is an exact copy, so that the tests run properly. For PRC1 this was fine because it was good for teaching you how to program. But when we write our own tests, we want the tests to be less brittle.

Let’s write our own tests and work test-driven to implement our phonebook again.

Turning requirements into testable statements
Let’s get started with writing our phonebook, TDD style. How do we proceed? In the beginning it’s best to write down the requirements for the system as a list of requirements that is testable. Instead of writing "The system should add numbers together", write "2 and 2 should equal 4.". The first statement is not testable, the second statement is much easier: assertThat(2+2).isEqualTo(4).

The list of requirements reads:

  1. adding a phone number to the relative person

  2. phone number search by person

  3. name search by phone number

  4. adding an address to the relative person

  5. personal information search (search for a person’s address and phone number)

  6. removing a person’s information

Let’s turn each requirement into testable statements and how we are going to verify them.

  1. Lookup a person by name:

    1. Input "Jukka" should yield Null (because we haven’t added any entries yet)

  2. Add phone number:

    1. input "Pekka", "040-123456" should store those values

    2. lookup: "Pekka" should yield "040-123456"

  3. Lookup a person by number:

    1. input "02-444123" should yield Null

    2. input "040-123456" should yield "Pekka"

  4. Add address:

    1. input "Pekka" and "High Street 15, Venlo"

  5. Lookup personal information by name:

    1. lookup "Pekka" should yield address "High Street 15, Venlo" and phone number "040-123456"

  6. Deleting personal information by name:

    1. Input "Pekka", should delete "Pekka" and number and address

Before beginning with the list, this is what our test class looks like. To avoid creating a new phonebook object in every test, we use the @BeforeEach annotation to set up our tests.

public class PhonebookTest {

    Phonebook phonebook;

    @BeforeEach
    public void setup() {
        phonebook = new Phonebook();
    }
  }

Let’s work our way through that list. In the beginning, we want to have a phonebook that actually looks up people. Because we haven’t added anything to the book yet, we’re starting with the test case that the person is not found. We could have written the test differently; instead of testing isNull, we could have asked for isEqualTo("person not found"). The reason we are not doing that is that we are making the test brittle (prone to break): by expecting a very specific String output, we essentially force our phonebook implementation to output "person not found". This is not the point of a test. You want to make sure that you test that functionality "if person is not found, the method should not return a person". It’s intuitive to use the isNull comparison here.

    @Test
    public void phonebookSearchByNameNotFound() {
      assertThat(phonebook.searchByName("Jukka")).isNull();
    }

Next, we add a person and use the searchByName function to see whether the adding was successful. To check whether we have successfully added an entry to the phonebook, we make use of the (already tested) search function. Because the search function was already successfully tested, we can include it here. Can you figure out what weakness our test has? We are using the contains comparison to see whether our phonebook contains the data we ask for. The phonebook could essentially be a long String containing all the data you add, but it has no phonebook structure that, for example, a Map or a Linked List would provide. At this point we have to decide how strict we make the tests. We could add another assert statement that checks the length of the String. If it’s greater than two (name and number), the test fails. This would be a more stringent test, but it also enforces a certain type of implementation. Essentially, when writing our tests we constantly make judgment calls of how lax or stringent our tests should be. Let’s leave our test like this for now.

@Test
public void phonebookAddsEntry() {
    phonebook.add("Pekka","040-123456");
    assertThat(phonebook.searchByName("Pekka")).contains("040-123456");
}

We have successfully tested the searchByName method and the add method. Moving down our list of requirements, next is the searchByNumber functionality. Let’s write a test that will first add an entry to the phonebook, and then asks to lookup a person by providing their number. In addition, we write another test that checks that looking up a number which is not in the phonebook returns a null result.

@Test
public void phonebookSearchByNumber() {
    phonebook.add("Pekka", "040-123456");
    assertThat(phonebook.searchByNumber("040-123456")).contains("Pekka");
}

@Test
public void phonebookSearchByNumberNotFound() {
    assertThat(phonebook.searchByNumber("02-444123")).isNull();
}

Are you getting the hang of it? Remember that our tests don’t prescribe one implementation- each of you can write a different phonebook implementation that all pass these tests. The more tests we write however, the more we have to optimize our implementation. We will see that with our next set of tests, the address entry. According to our requirements, the phonebook should also contain the address of the persons added, and naturally, it should be possible to look up addresses when specifying a person’s name. Again, our test forces us to make an implementation decision. In the code below, addAddress is a separate method and the searchByName method is now expected to yield the address as well.

@Test
public void phonebookSearchAddress(){
    phonebook.addEntry("Pekka", "040-123456");
    phonebook.addAddress("Pekka", "High Street 15, Helsinki");
    assertThat(phonebook.searchByName("Pekka")).contains("High Street 15");
}

Here is what an alternative test could look like:

@Test
public void phonebookSearchAddress(){
    phonebook.addEntry("Pekka", "040-123456", "High Street 15, Helsinki");
    assertThat(phonebook.searchAddress("Pekka")).contains("High Street 15");
}

In this version, the addEntry method is required to take a third argument, the address; and there is a specific search function that looks up addresses only. Which of these two versions (or another version entirely) you choose is down to the business logic and best coding practices. If the business logic requires a separate address search for example, the second test is the way to go. But the way we have phrased our requirement, the first version is correct.

New tests can have an impact on our implementation. Perhaps up until now you have used a HashMap<String, ArrayList<String>> to store a persons name and their phone numbers. Now that people also need addresses, our simple HashMap implementation reaches its limits. Sure, we could store numbers and addresses in the same ArrayList, but that would be messy. Instead, we could use Object-oriented principles and create a Person class that holds name, numbers and addresses and the HashMap<String,Person> binds people’s name and person Objects. How you implement your phonebook is up to you, but we can see here that as testing progresses, we are forced to refactor our code and to optimize it so that it passes the tests, yet its easy to read and implements good design choices. The key point here is that our test should not worry about whether you implement a Person class, but is concerned solely with the outcome. That way, different implementations can achieve the same end result.

Lets return to our list of requirements. We have one requirement left, the deletion of an entry. In our requirement we specified that we lookup a person by name before deleting. Let our test reflect that:

@Test
public void phonebookDeletesEntry()
{
    phonebook.addEntry("Pekka", "040-123456");
    phonebook.deleteEntry("Pekka");
    assertThat(phonebook.searchByName("Pekka")).isNull();
}

Again, we are using the searchByName method to verify that deleting the entry actually removes the data selected. And this is it! Our very first test-driven phonebook!

6. Exercises

Check out this tutorial for a great reference for AssertJ!

Exercise 1: In the pub (without exceptions)

In the pub

For this exercise, you are modeling and implementing the process of drinking beer in a pub. Develop your own pub simulation. Write unit tests to test if your implementation works and if it conforms to the specification and if exceptions are properly handled. Work test driven!

You’ll find the project template in your repository.

Here are the specifications of this exercise. Turn them into testable requirements before you start your TDD process.

  1. Initially the pub has 100L of beer available.

  2. A barkeeper and beer drinkers belong to a Pub.

  3. Beer drinkers can ask the barkeeper for beer.

  4. Beer is handed out either in small format (0.2L) or pint format (0.57L).

  5. Beer drinkers can only drink a limited amount of beer, between 2L and 5L.

Here is how you could start the BarkeeperTest class. First you create a barkeeper that you use in your tests.

    private Barkeeper barkeeper;

    @BeforeEach
    public void setUp() {
        barkeeper = new Barkeeper();
    }

In your first test method, you want to test whether the tapping of beer works as intended. Let the test fail first, then implement the logic of that test. When you have finished,continue to the next part.

The exercise is finished when you have tested and implemented each requirement.

Exercise 2: Fraction

Fraction TDD

Test driven development of a fraction class.

Here is a task description. We will insert a rather empty project into your repository, which in not only quite empty but also broken.

fraction
Figure 7. fraction class

Test Driven

Test driven development does not mean that you write all tests before you can write any implementation code. On the contrary, you write a tiny test for one of the functionalities, implement that functionality to make it past the test and then go to the next. All in tiny steps.

Choosing is always hard, including choosing where to start. The typical order for test driven development is:

  1. A Constructor. You typically need instances and this is how your can construct some.

  2. Getters for the fields that are defined in the constructor. If such getters are not part of the public API of the class, make the package private, to hide them from client classes outside of the package.

  3. The public String toString() method, which can show the result of the constructor.

  4. Setters, if needed. (They are NOT in this exercise).

  5. The business methods, one by one. In case of Fraction start at multiplication, because that is easiest.

  6. Further refinements, like in case of the fraction, that the fraction should always be in normalised form.

Now to the meet (requirements) of this task: The project contains the classes Fraction and Main.

Because this may be your first venture into test driven territory, we have added @Order annotations in the test class, which will help you finding your way. Normally one would and should not have a fixed order of test executions, but in this case it is there to help you. Enable the tests and work your way through completing the test methods and implement the Fraction business code.

You do not have to implement all aspects of a requirement in your first test. Take it a small step at a time.

The source code is in English, using the common names fraction, numerator and denominator.
The mathematical representation typically is: \(\frac{\text{numerator}}{\text{denominator}}\), example: \(\frac{1}{2}\).

Fraction Constructor

  1. Write a test that calls the fraction constructor with two distinct ints, say 4 and 5, and then invokes the getters int getNumerator() (should produce 4) and int getDenominator() (should produce 5) to assure that the fraction instance is properly constructed.

  2. Implement the constructor of Fraction with parameters n and d for numerator and denominator respectively.

  3. Run the tests.

In a similar fashion, develop the remaining requirements of the Fraction class.

  • Make sure the (negative) sign is attached to the numerator.

  • Ensure that the resulting Fraction is normalised. Fractions like \(\frac{2}{4}\) should automatically be transformed into \(\frac{1}{2}\). By dividing numerator \(a\) and denominator \(b\) before construction of a fraction by \(g = gcd(a,b)\) you produce a fraction with the same mathematical value, called a normalised fraction.

  • Have the constructor throw an IllegalArgumentException if the denominator is 0. This ensures that further calculations can be done without having to consider a zero value here. You could say that it provides a safety net for the code following the non-0 check. NOTE: You do not have to test this exception throwing yet, we show the techniques for that in week 3.

Implement toString
Implement the method toString(). As example for the expected output of toString()) for the fraction \(f=\frac{3}{4}\) is (3/4).
Implement times Implement the helper method Fraction.times( int otherN, int otherD ) to multiply this fraction with numerator otherN and denominator otherD of another fraction. The result is a new Fraction-object.

To multiply two fractions \(\frac{a}{b}\) and \(\frac{c}{d}\) consider the property \(\frac{a}{b}\times\frac{c}{d} = \frac{a\times{}c}{b\times{}d}\) Example: \(\frac{1}{2}\times\frac{3}{4}=\frac{1\times{}3}{2\times{4}}=\frac{3}{8}\).

If this method works, it should be easy to implement Fraction times(Fraction other). We do not mean copy and waste, but invoke the already available method cleverly.

Code re-use, one of the promises of Object Oriented Programming, is NEVER copy code, but instead call on already available and tested functionality.

  • Test plus Write the method FractionTest.testPlus(). Make up some example values, or use the one given below.

  • Plus Implement the helper method Fraction.plus( int otherN, int otherD ) to create a new fraction that is the sum of this fraction and another fraction with numerator otherN and denominator otherD. The result is a new Fraction-object. Formula: \(\frac{a}{b}+\frac{c}{d} = \frac{a\times{d}+c\times{b}}{b\times{d}}\)

  • Same for minus and divideBy. Test and implement minus and divideBy operation. You can re-use the work you did in times and plus, as in invoke the provided helper methods by adapting the parameters, such as changing sign or swapping them.

  • Add a constructor that takes one int as numerator and an implied denominator of 1, so that you can do
    Fraction three = new Fraction(3);

  • Test and implement implement the required int compareTo(Fraction other) method.
    The trick here is to multiply numerator of on fraction by the denominator of the other and then compare the resulting products. You are actually computing the parts of numerator of a fraction that would result from this minus the other without creating a new fraction (which would be wasteful).

  • Add static helpers frac(int, int) and frac(int), so you can use shorter notations such as Fraction twoThirds= frac(1).subtract(frac(1,3));

Now you have developed the Fraction class, the Main program, is able to use it with the code given. Note that we do not yet address the problem of how to test the proper use of standard input or output. That may be be part of a later exercise.

A NetBeans starter project will be in your respository.

The properties of fractions

The mathematical properties of fractions in a typical Java implementation.

Let \(f_1\) be a fraction with numerator \(a\) and denominator \(b\) thus: \(f_1=\frac{a}{b}\). And \(f_2\) a fraction with numerator \(c\) and denominator \(d\), thus \(f_2=\frac{c}{d}\).

Greatest Common Divisor

The function \(\gcd(p,q)\) is called Greatest Common Divisor. With parameters \(p\) and \(q\) it produces integer value \(g\), which is the greatest integer value that divides both \(p\) and \(q\) evenly (divide with zero remainder).

Fraction Properties

With the above definitions these rules apply:

  • Minimum integral denominator With fractions that have integral (whole) numerator and denominator, the denominator is always positive and unequal to 0. Therefore the minimal denominator is +1. \( \forall f, f =\frac{a}{b}, b \ge +1\)

  • Unity value When denominator and numerator are equal, say \(a\), then the value of the fraction is \(1\). \(\frac{a}{a} = 1\)

  • Integral value A fraction with numerator \(a\) and denominator \(1\) has the same value as \(a\). \( f = \frac{a}{1} = a\)

  • Idempodence Dividing or multiplying numerator and denominator of \(f\) by the same number \(n\) produces a fraction with the same value. Follows from unity: Multiplying \(f\) by \(1\) or \(n/n\) produces a fraction with the same value. \( f = \frac{a}{b} = \frac{a}{b}\times 1 =\frac{a}{b}\times\frac{n}{n}= \frac{a\times n}{b \times n} = \frac{a/m}{b/m}\)

  • Normalisation By applying idempodence, dividing numerator \(a\) and denominator \(b\) before construction of a fraction by \(g = gcd(a,b)\) produces a fraction of same value, called a normalised fraction. With \(g =gcd(a,b)\) \(f_1 = \frac{a}{b} = \frac{a/g}{b/g}\)

  • Negation The negative of a fraction is the same as a fraction with the numerator negated. \( -f_1 = -\frac{a}{b} = \frac{-a}{b} \)

  • Move sign to numerator When at construction time of a fraction the denominator is negative, negate both numerator and denominator. \( f_1=\frac{a}{-b} = \frac{-a}{b}\)

  • inverse The inverse of a fraction is the same as the swapping places of numerator and denominator. Note that multiplying a fraction with its inverse is \(1\), by applying the rules idempodence and unity. \(\frac{1}{f_1} = \frac{1}{(\frac{a}{b})} = \frac{b}{a}\)

  • Addition Add two fractions. \(f_1+f_2 = \frac{a}{b}+\frac{c}{d}=\frac{a\times d + c\times b}{b\times{}d}\)

  • Subtraction Subtraction of two fractions \(f_1\) and \(f_2\) is the same as addition of \(f_1\) and the negation of \(f_2\). \(f_1-f_2 = \frac{a}{b}-\frac{c}{d}=\frac{a}{b}+\frac{-c}{d}\)

  • Multiplication \( f_1\times{}f_2 = \frac{a}{b}\times\frac{c}{d}=\frac{a\times{}c}{b\times{}d}\)

  • Division Dividing fraction \(f_1\) by \(f_2\) is the same as multiplication \(f_1\) with the inverse of \(f_2\).
    \( f_1/f_2 = \frac{a}{b}/\frac{ c }{ d }=\frac{a}{b}\times \frac{ d }{ c } \)

The fraction class implemented with the operations above can and should have final fields. It will only need two fields. Such a class has the important property of being immutable, which is a very nice property to have for a class. It makes it easy to reason about the state of the instances after construction: They will always be the same. This means that they are cache-friendly, which is important if you have a multi-core processor. This and other properties will be addressed in later weeks or courses.

Compute gcd in Java
  static int gcd( int a, int  b ) {
    // ensure loop uses positive values
    a = Math.abs( a );
    b = Math.abs( b );
    while ( b != 0 ) {
      int t = b;
      b = a % b;
      a = t;
    }
    return a;
  }

Note that inside a Fraction class, \(b\) is the denominator with a minimum value of +1. Also, if \(a\) or \(b\) equal 0, the method returns the correct value \(b\) or \(a\), which is easily shown.

6.1. Test run reports Week 1

By following the links below you can find how well everyone did on the exercises.



1. One of the really difficult problems in informatics
2. will be maintaned at the same place where you find sebipom.