Concepts / Arrays

Arrays

An interface can be the base type of an array, as in Strokeable[] listOfFigures, and its elements can refer to any objects whose classes implement the interface.

  • Programming

A Drawing Program With Mixed Figures

Imagine a drawing program that keeps a list of figures on screen. Some of the figures are lines and some are circles, and the program needs to draw all of them. One approach is to create one array for lines and another array for circles. That works, but every drawing step then has to know which kind of figure it is touching, and the code grows with every new kind of figure.

This article shows a different approach. An interface can serve as the base type of an array, so a single array can hold many different kinds of objects. Throughout the article the interface is Strokeable, and the array variable is named listOfFigures. The type of that variable is Strokeable[].

The plan for this article is to trace what happens to the array step by step: first the declaration, then the creation, then the filling of the slots, and finally the call that draws each figure. At each stage you will be asked what the variable refers to, so you can check your understanding against the rules before the explanation follows.

Predicting the Result of a Declaration

What do you think happens?

After the line Strokeable[] listOfFigures; runs, and before any new operator is used, does an array exist?

  • Yes, an array of Strokeable with ten slots now exists

  • No, the variable has a type but no array has been created

Reveal answer
Answer:

No, the variable has a type but no array has been created

Declaring a variable gives it a type so that it can refer to an array, but the declaration does not supply that array. The array has to be created separately, using the new operator.

Forming an Array Type

Definition

An array type is built from two parts: the base type of the array, followed by a pair of empty square brackets. The base type is the type of each element the array will hold. The brackets mark the type as an array type rather than a single value of the base type.

Applying this pattern to the familiar examples gives String[] for an array of Strings and int[] for an array of ints. For the interface in this article, the base type is Strokeable, so the array type is Strokeable[]. The base type is never changed by the brackets; the brackets only say that the type describes a whole array of that base type.

java
Notes on the code
  • The base type String, followed by []: an array of Strings
  • The base type int, followed by []: an array of ints
  • The base type Strokeable, an interface, followed by []: an array whose elements are Strokeable references

Declaring the Array Variable

Once you have the type, a variable is declared by writing the type, a space, and the variable name, followed by a semicolon. The line Strokeable[] listOfFigures; is an example. The variable name is what you will later use to reach the array, and the type is what tells the compiler that the variable is allowed to refer to an array of Strokeable.

java
Notes on the code
  • Strokeable[] is the type, the base type followed by brackets
  • listOfFigures is the name the program will use to reach the array
  • The semicolon ends the declaration; no array has been created yet

The source makes the key point directly: declaring a variable does not make the actual array. A variable of an array type still needs a value before it can be used, and that value has to be an array. The diagram below compares the variable just after the declaration with the variable after an array has been created and assigned to it.

listOfFigurestyped Strokeable[], noarray yetlistOfFiguresrefers to an arrayStrokeable[10] arrayten slotsrefers to
After the declaration line but before new runs, what does listOfFigures refer to, and how does that differ once the array has been created?

The two steps are easy to confuse because they often appear on neighboring lines and both mention the same variable name. The declaration gives the variable a type. Creation makes the thing the variable will refer to. Keeping those two steps apart is the main idea of this topic.

Creating the Array With new

Creating an array is the step that produces the actual array. In Java this is done with an operator named new. The source notes that arrays in Java are actually objects, and that the special syntax is related to this, although the details of object creation are not needed at this stage. What matters here is the pattern you write.

java
Notes on the code
  • A is the array variable on the left, so it receives the new array as its value
  • new is the operator that creates the array
  • int is the base type, the type of each element
  • 5 is the length, the number of elements the array will have

Read the pattern from left to right. The array variable is on the left of the assignment, so it receives the new array. On the right, new is followed by the base type and then a length in square brackets. The length is required, because creating an array means deciding how many elements it has. An array variable must be assigned an array before it is used, so creation must come before any work on the array's elements.

index 0emptyindex 1emptyindex 2empty
Right after new Strokeable[3] runs, how many slots exist, what are their indices, and what does each slot contain?

The diagram shows the slots created by new Strokeable[3]. Creating the array allocates its slots, but it does not put any objects into them. A slot refers to an object only after an assignment has stored one there. Until that happens, the slot holds no object. The diagram is a logical picture of the indexed set of slots; the source does not describe how the slots are laid out in memory.

Worked example

Creating a String Array of Length Four

Declare a variable named names that can refer to an array of Strings, then create an array of four Strings and assign it to that variable.

  1. 1
    Write the type

    The base type is String, so the array type is String followed by empty brackets: String[].

  2. 2
    Declare the variable

    Write the type, a space, and the name, then a semicolon: String[] names;. At this point names can refer to an array, but it does not refer to one yet.

  3. 3
    Create the array

    Use new with the base type and a length of 4: new String[4]. This allocates four slots, indexed 0 through 3.

  4. 4
    Assign the array

    Assign the new array to the variable: names = new String[4];. The variable now refers to the array.

Result

The variable names refers to an array with four slots. Each slot is empty until a String is assigned to it.

Filling Slots With Different Classes

After the array is created, each slot is assigned separately. In the source's example, a Line is assigned to slot 0, a Circle to slot 1, and another Line to slot 2, and the same pattern continues for the rest of the array. Each assignment is legal because Line and Circle both implement Strokeable. The array does not record a single class for all of its elements. Each element can refer to a different class, as long as that class implements the interface.

java
Notes on the code
  • Declaration and creation together: a Strokeable[] variable refers to a new array of ten empty slots
  • Slot 0 receives a Line object; Line implements Strokeable, so the assignment is legal
  • Slot 1 receives a Circle object; a different class can sit in the same array
  • Slot 2 receives another Line; the array does not need every slot to hold the same class
listOfFiguresStrokeable[] arrayslot 0Line objectslot 1Circle objectslot 2Line objectindex 0index 1index 2
How can one array of type Strokeable[] hold a Line and a Circle at the same time, and how do the slots relate to those separate objects?

The diagram shows the relationship the statements set up. The variable names one array, and each slot in that array refers to an object. The objects in slot 0 and slot 2 are both Line objects, while slot 1 refers to a Circle. Every slot is allowed to refer to any class that implements Strokeable, and the base type is what limits the choices to that interface.

Why the Same Call Works on Every Element

The reason for using an interface as the base type is the methods it guarantees. Every element of the array has a stroke() method, because every element refers to an object whose class implements Strokeable. This is what lets a single statement such as listOfFigures[i].stroke(g) work for any element, whether that element is a Line or a Circle. The compiler checks the call against the base type, Strokeable, which declares stroke().

This behavior is polymorphism. The same call produces behavior that depends on the actual class of the object in the slot. The code that makes the call does not need to know which class it is dealing with. It only needs to know that the element has the methods declared by the interface, so a loop can draw every figure in the array without a separate branch for each kind.

java
Notes on the code
  • The loop visits every slot by index
  • Each element is called through the interface; the loop does not check the concrete class
  • Each call runs the stroke() method of whatever object the slot refers to

Mistakes Beginners Make With Array Declarations

  • Common mistake

    Treating the declaration as if it created the array

    What it looks like
    Strokeable[] listOfFigures; followed by listOfFigures[0] = new Line();
    Why it fails

    The declaration only gives the variable a type. No array exists until new creates one, so there are no slots to assign to.

    Do this instead

    Create the array first: listOfFigures = new Strokeable[10]; and only then assign to its slots.

  • Common mistake

    Assuming the base type records the class of each element

    What it looks like

    Believing that because the array is Strokeable[], every slot must hold a Line.

    Why it fails

    The base type constrains the interface, not the concrete class. Each slot can hold a different class that implements Strokeable.

    Do this instead

    Read Strokeable[] as an array of references to any object whose class implements Strokeable.

  • Common mistake

    Calling a method on a slot that was never assigned

    What it looks like

    Creating new Strokeable[10] and immediately calling listOfFigures[0].stroke(g) before any assignment.

    Why it fails

    A slot refers to an object only after an assignment has stored one there. An empty slot has no object for the call to run on.

    Do this instead

    Assign an object to the slot before calling its methods, or check that the slot is filled first.

  • Common mistake

    Leaving out the length when creating an array

    What it looks like

    Writing new Strokeable; instead of new Strokeable[10];

    Why it fails

    Creating an array means deciding how many elements it has, and the length is required.

    Do this instead

    Give the base type followed by the length in square brackets: new Strokeable[10].

  • Common mistake

    Using the array variable in code that runs before the creation statement

Best practice

When you read a line that declares an array variable, look at the lines that follow for the creation statement. If the variable is used before that statement, the program is working with a variable that has no array yet.

Practice Tracing a Declaration

MEDIUM

Trace the following three lines. After each line, state whether listOfFigures refers to an array, how many slots the array has, and whether any slot holds an object. Then say which line would make a call on listOfFigures[0] fail if it were made right after that line.

Strokeable[] listOfFigures;
listOfFigures = new Strokeable[3];
listOfFigures[0] = new Line();
Hints
  • After the first line, the variable has a type but no array has been created.
  • The second line creates an array with three slots, and a new array's slots start out with no object in them.
  • A call on listOfFigures[0] needs an object in slot 0, so check the state after the second line, before the third line assigns it.

Key Takeaways

  • An array type is the base type followed by a pair of empty square brackets, so the array type for the interface Strokeable is Strokeable[].
  • Declaring an array variable gives it a type so that it can refer to an array, but it does not create the array; the new operator, with a base type and a length, creates it.
  • Creating an array allocates its slots, but a slot refers to an object only after an assignment stores one there, so unassigned slots hold no object.
  • Because the base type is an interface, one array can hold objects of different classes that implement it, and every element has the methods the interface declares.
  • A loop can call listOfFigures[i].stroke(g) on every element without knowing its concrete class; this is polymorphism, and a failing call usually traces back to a slot that was never assigned.