Lab 8

Due: 05/12 (bonus)

Reflection and Generics

Objectives

  • 10 (bonus) Points - Implemented a generic Stringifier using reflection, annotations, and recursion.
  • 10 (bonus) Points - Use generics to implement a binary tree that can store any kind of object.

Description

Implemented a generic Stringifier using reflection, annotations, and recursion.

Writing a toString method for data objects can be a highly repetitive task since basically most of the time the only thing this method will do is to iterate over the fields of the class and use an appropriate method to output the values of these fields as strings. We will use reflection to write a generic Stringifier class in package lecture.lab4 which implements a static method

public static String stringify(Object o);

which takes an object of any class and serializes that all fields. For fields of a primitive type, wrapper type (e.g., Integer) or String you should use the toString methods of the corresponding wrapper class, e.g., Integer for int. For reference types, you need to recurse to use stringify to serialize the object. The output format should be as follows:

{ CLASS@id
  field1: value1
  field2: value2
  ...
  field3: valueN
}

Here CLASS is the name of the class, id is the result of calling System.idenityHashCode(o) for an object o. A null value should be encoded as NULL. Note that if a field is of a reference type then the value created should be indented by an appropriate amount of tabs corresponding to the recursion depth of stringify. For instance, for the list datastructure below implemented using the class ListCell shown below you should return (assuming the two list cells have ids XXXXX and YYYYY):

{ ListCell@XXXXX
  value: 2
  next:
	{ ListCell@YYYYY
	value: 5
	next: NULL
	}
}
class ListCell {
	int value;
	ListCell next;
}
%3 n1 2 n2 5 n1->n2

Note that some datastructures may be have cicular references, e.g., in a doubly linked list a list cell has a reference to the following and the previous cell (as illustrated below). This would lead to endless recursion unless you keep track of which objects have already been serialized. To solve this problem you need to keep track of every Object that stringify has seen already using an appropriate collection datastructure. Then for each object that should be processed you need to first check whether the object has been processed before already. If so, just output @id where id is the result of System.identifyHashCode(o) for the object instead of recursing.

Note that the getFields method of Class does not return private fields of a class. You have to call getDeclaredFields. Furthermore, per default, you will not use a Field object to access a private field unless you call setAccessible(true) on this object.

%3 n1 head n2 tail n1->n2 n2->n1

Assuming that the two list cells have ids XXXXX and YYYYY and that the links of the list cells are stored in two fields next and previous this would produce the following output:

{ ListCell@XXXXX
  value: 2
  next:
  previous: NULL
	{ ListCell@YYYYY
	value: 5
	next: NULL
	previous: @XXXXX
	}
}

Furthermore, implement two annotations StringifiyIgnore and StringifyMethodName.

  • StringifyIgnore can be placed on a field. Any field with this annotation should be ignored by Stringify
  • StringifyToStringMethod(String methodname) can be placed on a class. If Stringify has to serialize an instance of this class, then it does so by calling methodname instead of recursing. methodname is expected to have no parameters and return a String.

Testcase

Write a testclass ListCell as explained above and write a JUnit test that tests the serialization of several lists build using this class.

Use generics to implement a binary tree that can store any kind of object.

In this task you will implement a binary tree datastructure as a class Tree in package lecture.lab8 whose nodes can store elements of any class E. Use generics to ensure that a tree to store instances of a class E can only be used to store instances of this class. A class TreeNode should be used to store a value of type E (accessible through a method getValue() that returns an object of type E) and a left and right reference to the children of the node. The children should be accessible through getLeft() and getRight() methods of the class TreeNode. The Tree class should expose a method iterator that returns an Iterator<E> (see Java documentation for Iterator) that traverses all the elements in the tree.

To iterate over all the elements in the tree use one of the traversal methods discussed in class.

Testcase

As a class Person that stores a name (String) and then use this class to write a JUnit test case that create the following tree of Person objects, searches through the elements of the tree using the iterator() method of the Tree class and creates a list of the name of every Person whose name starts with a B and compares this result against an expected list. Also write a test case in this JUnit test that serializes the tree using the Stringifier class implemented in the previous task and compares against the expected outcome.

%3 n1 Bob n2 Alice n1->n2 n3 Barbara n1->n3 n4 Bert n2->n4 n5 Adam n2->n5 n6 Peter n3->n6

Updated: