The Relationship between C Structures and Java Classes @(http://www.cs.usfca.edu/~parrt, 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: o {void *} to {Object}, which refers to any Java object. (But cannot refer to a variable of a primitive type.) o {list_add()} to {add()} because moving it inside the class definition for {List} means that it is now really {List.add()}! Function names are more general in Java. o {list_init()} to {List()}, which is called the constructor for class {List}. We use the constructor to allocate the space also. o access specifiers have been added to prevent access to {List}'s private data. #### 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: o encapsulation: the methods that operate on an object are packaged up with that object. o data hiding: portions of the object's data may be hidden from the user, thus, allowing the implementation of that object to be isolated from the user. Changes to the implementation would not affect a user's code as all access to the data element can be controlled through a set of functions.