Review

We have learned a lot this week about object-oriented programming! Some important points:

These are all concepts that can be pretty tricky to wrap your head around the first time you see them since they require a pretty different way to think about the programs than what you are used to.

Class Constants

As discussed in class, programs are meant to be read by humans, not by computers. This means it is very important we focus on the readability of our code to some other reader; this reader could be a co-worker or partner but could also be you looking back at code you wrote when trying to solve a critical bug. This means we want our code to convey the semantic meaning of what it is trying to accomplish as clearly as possible.

Take our version of ArrayIntList for example:

public class ArrayIntList {
    private int[] elementData;
    private int size;

    public ArrayIntList() {
        this(10);
    }

    public ArrayIntList(int capacity) {
        ...
    }
}

If someone else were to read our code, they might ask “Why is the number 10 there? Is there any reason it’s not 9?”. In this program, the number 10 is what we call a magic value or a value in a program that has no inherent semantic meaning. Now we, as the author of this code, know we wanted that number 10 to be the default capacity of the internal array. However, by just hard-coding the number 10 we are not conveying that semantic meaning of “default capacity”.

One solution is to just write a comment indicating the meaning of the value 10. That’s not a bad idea but it doesn’t exactly solve the problem if we had the number 10 appearing more often in our code and we wanted to have the flexibility of changing it if necessary without having to change every instance it appears in the class. A much better solution is to store this magic value in a class constant with a descriptive name that we use in our code instead of that magic value. For example, our ArrayIntList would look like this if we added a constant.

public class ArrayIntList {
    public static final int DEFAULT_CAPACITY = 10;

    private int[] elementData;
    private int size;

    public ArrayIntList() {
        this(DEFAULT_CAPACITY);
    }

    public ArrayIntList(int capacity) {
        ...
    }
}

Now it is much clearer that we are constructing something of the default capacity!

A few things to mention about class constants:

Notice, this program is much more readable even though we only use that constant once! Class constants are even more powerful when you use one in multiple places. If you were to want to change the default capacity, you would only have to change it once at the constant declaration since no other places have the value hard-coded!

Double Vision

What if we wanted to write methods for ArrayIntList that took other ArrayIntList instances as parameters? This sounds kind of weird at first but can be really helpful! For example, the ArrayList class has a method addAll that takes another ArrayList and adds all the values in that list to this one. We want to emulate this behavior for ArrayIntList.

We would want to write the client code so it would look something like the following

public class Client {
    public static void main(String[] args) {
        ArrayIntList list1 = new ArrayIntList();
        list1.add(1);
        list1.add(2);
        list1.add(3);

        ArrayIntList list2 = new ArrayIntList();
        list2.add(4);
        list2.add(5);

        list1.addAll(list2);

        System.out.println(list1); // Should print [1, 2, 3, 4, 5]
    }
}

We would start by writing a method with the correct header

public class ArrayIntList {
    private int[] elementData;
    private int size;

    // constructors and other methods

    // post: adds all the values from other to the end of this list.
    // the values will appear at the end in the same order they appear in other.
    public void addAll(ArrayIntList other) {
        // TODO implement this method
    }
}

In the addAll method, we would have two variables that refer to different instances of the ArrayIntList class; this would refer to the same list as list1 while other would refer to the same list as list2.

Now we could loop over all the values in other and add them to the contents of our list. A simple way to do this would be

public void addAll(ArrayIntList other) {
    for (int i = 0; i < other.size(); i++) {
        this.add(other.get(i));
    }
}

What this is doing is using the standard traversal over the other ArrayIntList, getting the value at each index, and then calling the add method (that appends at the end) on this ArrayIntList. You are allowed to prepend method calls on yourself with this. to make it clear which ArrayIntList you are calling the method on; this is not required because Java will go in and insert the this. itself (just like it did for accessing fields), but I added it for clarity.

Check your understanding

Try solving this problem on Practice-It!

Some lazy pun about “private”

The addAll code above is actually a working implementation and is totally fine. One other thing I want to mention is an extra thing you can do when you take another instance of your type as a parameter: you are allowed to access its private fields!

Here is the same method implemented by accessing the fields of other directly.

public void addAll(ArrayIntList other) {
    for (int i = 0; i < other.size; i++) {
        this.add(other.elementData[i]);
    }
}

This sounds weird at first since I first said that private means no one outside can access your fields, but Java has a slight technicality to that statement. To Java, private doesn’t mean private to things outside this instance, it means private to things outside of this class. While that looks like a tiny wording difference, what it implies is that any instance of ArrayIntList has the ability to access fields of any other ArrayIntList it has a reference to because they are of the same class.

This is definitely different to our human notion of the word “private”! We would think of any private state you have as only accessible only to you, but Java would allow anyone of type Human to access your private fields!

It’s rare that you are required to access private fields of other instances of your type, but it’s good to know that it is a possibility. As we mentioned in lecture, there is always a trade-off between directly accessing fields and calling methods (fields are generally faster, but accessing methods instead leads to better abstractions and can make your code more flexible) so it’s really up to you to figure out what you think is the most readable.