Chapter Four - Work with Other Classes

In this chapter you are going to learn how to use other classes in your @Test method code. Eventually these will be classes that you write, but for the moment we will use other classes that are built in to Java.

You have already done this in the previous chapter. Because you used the JUnit Assert class to check conditions, but we imported it statically, so you might not have noticed. (I’ll explain what static import means in the next chapter).

But first, some guidance on how to learn Java.

Use @Test methods to understand Java

When I work with people learning Java, I encourage them to write methods and assertions which help them understand the Java libraries they are using. And that is what we will do in this chapter.

For example, you have already seen a primitive type called an int.

Java also provides a class called Integer.

Because Integer is a class, it has methods that we can call, and we can instantiate an object variable as an Integer.

When I create an int variable, all I can do with it, is store a number in the variable, and retrieve the number.

If I create an Integer variable, I gain access to a lot of methods on the integer e.g.

  • compareTo - compare it to another integer
  • intValue - return an int primitive
  • longValue - return a long primitive
  • shortValue - return a short primitive

Explore the Integer class with @Test methods

In fact you can see for yourself the methods available to an integer.

  • Create a new package:
    • com.javafortesters.chap004testswithotherclasses.examples
  • Create a new class IntegerExamplesTest
  • Create a method integerExploration
  • Annotate the method with @Test so you can run it with JUnit

You should end up with something like the following:

package com.javafortesters.chap004testswithotherclasses.examples;
import org.junit.Test;

public class IntegerExamplesTest {

    @Test
    public void integerExploration(){
    }
}

We can use the integerExploration method to experiment with the Integer class.

Instantiate an Integer Class

The first thing we need to do is create a variable of type Integer.

        Integer four = new Integer(4);

Because Integer is a class, this is called instantiating a class and the variable is an object variable.

  • int was a primitive type.
  • Integer is a class.
  • To use a class we instantiate it with the new keyword
  • The new keyword creates a new instance of a class
  • The new instance is referred to as an object or an instance of a class

You can also see that I passed in the literal 4 as a parameter. I did this because the Integer class has a constructor method which takes an int as a parameter so the object has a value of 4.

The Integer class actually has more than one constructor. You can see this for yourself.

  • Type in the statement to instantiate a new Integer object with the value 4
  • Click inside the parentheses where the 4 is, as if you were about to type a new parameter,
  • press the keys ctrl + p (cmd + p on a Mac)

You should see a pop-up showing you all the forms the constructor can take. In the case of an Integer it can accept an int or a String.

Check that intValue returns the correct int

We know that the Integer class has a method intValue which returns an int, so we can create an assertion to check the returned value.

After the statement which instantiates the Integer.

Add a new statement which asserts that intValue returns an int with the value 4.

        assertEquals("intValue returns int 4",
                     4, four.intValue());

When you run this method it should pass.

Instantiate an Integer with a String

We saw that one of the constructors for Integer can take a String, so lets write some code to experiment with that.

  • Instantiate a new Integer variable, calling the Integer constructor with the String "5",
  • Assert that intValue returns the Integer 5
        Integer five = new Integer("5");
        assertEquals("intValue returns int 5",
                     5, five.intValue());
Quick Summary
package com.javafortesters.chap004testswithotherclasses.examples;

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class IntegerExamplesTest {

    @Test
    public void integerExploration(){
        Integer four = new Integer(4);
        assertEquals("intValue returns int 4",
                     4, four.intValue());
        Integer five = new Integer("5");
        assertEquals("intValue returns int 5",
                     5, five.intValue());
        Integer six = 6;
        assertEquals("autoboxing assignment for 6",
                      6, six.intValue());
    }
}

It might not seem like it but we just covered some important things there.

  • Did you notice that you didn’t have to import the Integer class?
    • Because the Integer class is built in to the language, we can just use it. There are a few classes like that, String is another one. The classes do exist in a package structure, they are in java.lang, but you don’t have to import them to use them.
  • We just learned that to use an object of a class, that someone else has provided, or that we write, we have to instantiate the object variables using the new keyword.
  • Use ctrl + p to have the IDE show you what parameters a method can take (cmd + p on a Mac).
  • When we instantiate a class with the new keyword, a constructor method on the class is called automatically.
AutoBoxing

In the versions of Java that we will be using, we don’t actually need to instantiate the Integer class with the new keyword.

We can take advantage of a Java feature called ‘autoboxing’ which was introduced in Java version 1.5. Autoboxing will automatically convert from a primitive type to the associated class automatically.

So we can instead simply assign an int to an Integer and autoboxing will take care of the conversion for us e.g.

        Integer six = 6;
        assertEquals("autoboxing assignment for 6",
                      6, six.intValue());
Static methods on the Integer class

Another feature that classes provide are static methods.

You already used static methods on the Assert class from JUnit. i.e. assertEquals

A static method operates at the class level, rather than the instance or object level. Which means that we don’t have to instantiate the class into a variable in order to call a static method.

e.g. Integer provides static methods like:

  • Integer.valueOf(String s) - returns an Integer initialized with the value of the String
  • Integer.parseInt(String s) - returns an int initialized with the value of the String

You can see all the static methods by looking at the documentation for Integer, or in your code write Integer. then immediately after typing the . the IDE should show you the code completion for all the static methods.

For each of these methods, if you press ctrl + q (ctrl + j on a Mac) you should see the help file information for that method.

Public Constants on the Integer class

It is possible to create variables at a class level (these are called fields) which are also static. These field variables are available without instantiating the class. The Integer class exposes a few of these but the most important ones are MIN_VALUE and MAX_VALUE.

In addition to being static fields, these are also constants, in that you can’t change them. (We’ll cover how to do this in a later chapter). The naming convention for constants is to use only uppercase, with _ as the word delimiter.

MIN_VALUE and MAX_VALUE contain the minimum and maximum values that an int can support. It is worth using these values instead of -2147483648 and 2147483647 to ensure future compatibility and cross platform compatibility.

To access a constant, you don’t need to add parenthesis because you are accessing a variable, and not calling a method.

i.e. you write “Integer.MAX_VALUE” and not “Integer.MAX_VALUE()”.

Do this regularly

I encourage you to do the following regularly.

When you encounter:

  • any Java library that you don’t know how to use
  • parts of Java that you are unsure of
  • code on your team that you didn’t write and don’t understand

Then you can:

  • read the documentation - ctrl + q (ctrl + j on Mac) or on-line web docs
  • read the source - ctrl and click on the method, to see the source
  • write some @Test annotated methods, with assertions, to help you explore the functionality of the library

When writing the @Test methods you need to keep the following in mind:

  • write just enough code to trigger the functionality
  • ensure you write assertion statements that cover the functionality well and are readable
  • experiment with ‘odd’ circumstances

This will help you when you come to write assertions against your own code as well.

Warnings about Integer

I used Integer in this chapter because we used the int primitive in an earlier chapter and Integer is the related follow on class.

But… experienced developers will now be worried that you will start using Integer in your code, and worse, instantiating new integers in your code e.g. new Integer(0)

They worry because while an int equals an int, an Integer does not always equal an Integer.

I’m less worried because:

  • I trust you,
  • Automation code has slightly different usages than production code and you’ll more than likely use the Integer static methods
  • I’m using this as an example of instantiating a class and using static methods,
  • This is only “Chapter 4” and we still have a way to go

I’ll illustrate with a code example, why the experienced developers are concerned. You might not understand the next few paragraphs yet, but I just want to give you a little detail as to why one Integer, or one Object, does not always equal another Object.

e.g. if the following assertions were in an @Test method then they would pass:

        assertEquals(4,4);
        assertTrue(4==4);

Note that “==” is the Java operator for checking if one thing equals another.

If the following code was in an @Test method, then the second assertion would fail:

        Integer firstFour = new Integer(4);
        Integer secondFour = new Integer(4);

        assertEquals(firstFour, secondFour);
        assertTrue(firstFour==secondFour);

Specifically, the following assertion would fail:

        assertTrue(firstFour==secondFour);

Why is this?

Well, primitives are simple and there is no difference between value and identity for primitives. Every 4 in the code refers to the same 4.

Objects are different, we instantiate them, so the two Integer variables (firstFour and secondFour) both refer to different objects. Even though they have the same ‘value’, they are different objects.

When I do an assertEquals, JUnit uses the equals method on the object to compare the ‘value’ or the object (i.e. 4 in this case). But when I use the "==" operator, Java is checking if the two object variables refer to the same instantiation, and they don’t, they refer to two independently instantiated objects.

So the assertEquals is actually equivalent to:

        assertTrue(firstFour.equals(secondFour));

Don’t worry if you don’t understand this yet. It will make sense later.

For now, just recognize that:

  • you can create object instances of a class with the new keyword, and use the non-static methods on the class e.g. anInteger.intValue()
  • you can access the static methods on the class without instantiating the class as an object e.g. Integer.equals(..).

Summary

You learned that in IntelliJ you can press ctrl and then the left mouse button to click on a method name and IntelliJ will jump to the source of that method.

You learned the following shortcut keys:

Function Windows Mac
Show Parameters ctrl + p cmd + p
Show JavaDoc ctrl + q ctrl + j

You also learned about static methods and the difference between object value and object identity.

Whatever you learn in this book, make sure you continue to experiment with writing assertions around code that you use or want to understand.

You also learned how to instantiate a new object and what a constructor does.