The Relationship between C Structures and Java Classes

Terence Parr

[This section is intended as review for C++ programmers and as a bridge from C to Java for C programmers.]

Consider how you would build a list manager for arbitrary elements in C. First, you would define operations on a list such as add and get:

void list_add(void *item) {..}
void *list_get(int i) {..}

Second, you might choose the implementation data structure such as an array of void *:

static void *data[MAX_ELEMENTS];
static int nelements = 0;

To ensure that users of the list manager do not access the data structure explicitly (isolating implementation from interface), we have made the data static, thus making it invisible outside of the file.

Naturally, the problem with this scheme is that only one list manager can be created. To solve this, you would put the data within a struct and then list manager users would each allocate one of the structs:

struct List {
  void *data[MAX_ELEMENTS];
  int nelements;
};

void list_add(struct List *list, void *item) {..}
void *list_get(struct List *list, int i) {..}
void list_init(struct List *) {nelements=0;}

Notice that now each list function receives the particular list to operate on as the first argument. Also note that an explicit initialization function is required to initially set the fields of a List structure.

Unfortunately, this struct-based scheme opens up all of the data elements to the users as they must see the definition of struct List to be able to use it.

A Java Implementation

A similar, but simpler and safer, solution to the struct-based scheme, is available to Java programmers: Change the name struct to class. Place the functions within the class definition. Finally, remove the first argument that indicates which list to manipulate.

class List {
  private  Object data[];
  private  int nelements=0;
  public   void add(Object item) {..}
  public   Object get(int i) {..}
  public   List() {
      data = new Object[MAX_ELEMENTS];
  }
}

Notice that we have also changed:

Using the List Managers

Now compare how you would use the C list manager versus the Java list manager:

/* C code */
#include "List.h"
foo() {
  struct List *mylist = (struct List *)calloc(1, sizeof(struct List));
  list_init(mylist);
  list_add(mylist, "element 1");
  list_add(mylist, "element 2");
  /* we can write to mylist->data[] directly! */
}

In Java, you would write:

// Java code
void foo() {
  List mylist = new List();
  mylist.add("element 1");
  mylist.add("element 2");
  // cannot write to mylist.data[] directly!
}

The crucial difference between C and Java is in the function call versus method invocation; i.e.,

list_add(mylist, "element 1");

versus

mylist.add("element 1");

In C you write function-centric code. In Java, you write data-centric code. That is, "execute this code using this data" versus "here's some data...operate on it". Object-oriented programmers typically go further than this to say "ask the list to add "element 1" to itself." Another common terminology (from Smalltalk) would describe this operation as "send the message add to the object mylist with an argument of "element 1"."

Are Classes Only For Object-Oriented Programming?

The Java version of the list manager described above can be viewed merely as a simpler description than the C version. In fact, classes that are not derived from another class (using inheritance) are really nothing more than fancy struct definitions. All the mumbo-jumbo surrounding object- oriented programming such as polymorphism can be ignored while studying the benefits of encapsulation and data hiding: