Educational outcomes
Student:
- explains how to implement constructs such as class extents, extent persistence, complex attribute, optional attribute, mult-valued attribute, class attribute, derived attribute in natural language (understanding);
- identifies and corrects errors in the source code, which is an implementation of the above structure;
- lists constructs implemented in the source code (understanding);
- implements the above structures using the selected programming language (application);
- indicates functional differences between two selected programs based on code snippets source (analysis);
- creates a program that implements specific business requirements using the constructs learned from the lectures (synthesis);
Justification
Modern programming languages are mostly based on the object paradigm. Therefore, its practical understanding is a necessary basis for working in this profession. Moreover, programmers often make systems based on analytical materials. And these largely use object-oriented graphical notations (such as UML). During this and the next lessons, you will deepen your theoretical bond, but first of all you will learn how to implement different concepts from object-orientness. This will allow you to design and implement modern business applications.
Extent Management - ObjectPlus class
The presented methods of implementing extent management will be (almost) the same for every business class in the system. Assuming an implementation within the same business class, we have to put almost the same elements in each of them:
- container that holds references to their instances,
- Methods to manage (add, delete, etc.).
Is it possible to unify it so that we do not have to write many times (almost) the same code? Fortunately, the answer to the above question is yes. We will use inheritance existing in Java. Although we have not discussed it yet, but for the
time being, we will have enough basic information from the programming lessons. We will create a base class from which will inherit all business classes in our application. Let us name it, for example, ObjectPlus, and equip
it with:
- persistance,
- extent management,
- maybe some other elements.
We will use the first approache to implement extent, ie. within the same class.
How to store many extents?
Referring to previous lessons, we know that we need to create a class attribute container (so that all objects in a class can access it). So at first glance it looks like an identical solution for a single class. Are you sure? Let's take a
look: For example, we create a Movie object inherited from ObjectPlus class and, thanks to a special reference in the constructor, we add it to the extent (which is stored in the class attribute of the ObjectPlus class). Then we create an Actor object, which also inherits from the ObjectPlus class. Also, thanks to a special constructor, the object is added to the extent stored in the ObjectPlus class. As a
result, we have two objects, Actor and Movie, which are in the same extent (in the ObjectPlus class). We did not want it. How to fix this?
Because all business classes inherit from one superclass (ObjectPlus), we can not use a simple container. We will use a mapping container that stores keys and values (dictionary):
- The key will be the name of the specific business class, such as
ActororMovie, - The value will be a collection containing the references to its instances (the real extent).
In other words, this new container (dictionary) will contain many extents rather than a sinle one. The figure below shows schematically the content of the container. We see that there are currently two entries: films and actors. The extent of actors put in a dedicated collection stores references to three objects. Extent of movies saved in another collection store references to four movies.

The code declaring the class and the corresponding container (in this case it is a Map parametized with appropriate types) is shown below. Let's also take a look at the implementation of the Serializable interface
- which will give us persistency of all the extents for free.
public abstract class ObjectPlus implements Serializable {
private static Map<Class<? extends ObjectPlus>, List<ObjectPlus>> allExtents = new HashMap<>();
// ...
}
The role of the constructor
In previous lessons, objects were added automatically to the extent, using the method call in the constructor. We will use this solution now, but due to the generality of our design, it will be a little more complicated.
Adding to an extent
Let's analyze the following code:
public abstract class ObjectPlus implements Serializable {
private static Map<Class<? extends ObjectPlus>, List<ObjectPlus>> allExtents = new HashMap<>();
public ObjectPlus() {
List extent = null;
Class theClass = this.getClass();
if(allExtents.containsKey(theClass)) {
// An extent of this class already exist
extent = allExtents.get(theClass);
}
else {
// An extent does not exist - create a new one
extent = new ArrayList();
allExtents.put(theClass, extent);
}
extent.add(this);
}
}
- (2). A private class attribute that is one of the Java mapping containers. As mentioned above, it stores keys (class names for specific extents) and values containing collections with references to instances of that class.
- (4). Constructor of the
ObjectPlusclass. Contains code that adds the object to a specific extents. - (6). Thanks to a technique called reflection we get information about the membership of the object that is constructed. The returned value, which is an instance of class
Class, will be used as a key in ourHashtable. We could use one of its methods to determine the class name, but it would be more efficient. - (8). We are checking if the mapping container already contains a key describing our class.
- (10). If so, then the value of the key is recovered from the key, that is, the collection containing the extent.
- (14, 15). If not, then we create a new empty collection that will contain instances and add it to the main mapping container.
- (18). We add information about the new instance that is being created. It is worth noting that here we always have the correct extent - either because we retrieved it on the basis of a key, or created a new one.
Ensure persistency
Thanks to serialization we do not have much to do. As we remember, a whole graph of objects is saved, so just send the Hashtable to our file .
public abstract class ObjectPlus implements Serializable {
private static Map<Class<? extends ObjectPlus>, List<ObjectPlus>> allExtents = new HashMap<>();
// [...]
public static void writeExtents(ObjectOutputStream stream) throws IOException {
stream.writeObject(allExtents);
}
public static void readExtents(ObjectInputStream stream) throws IOException, ClassNotFoundException {
allExtents = (HashMap) stream.readObject();
}
}
Actually, because all extents are in one map, we could even immediately create a stream inside the method and pass it the file name.
Getting an extent
Of course, sooner or later we will want to retrieve a certain extent and do something about its objects. The following method will do this:
public abstract class ObjectPlus implements Serializable {
private static Map<Class<? extends ObjectPlus>, List<ObjectPlus>> allExtents = new HashMap<>();
// [...]
public static <T> Iterable<T> getExtent(Class<T> type) throws ClassNotFoundException {
if(allExtents.containsKey(type)) {
return (Iterable<T>) allExtents.get(type);
}
throw new ClassNotFoundException(String.format("%s. Stored extents: %s", type.toString(), allExtents.keySet()));
}
}
Please note that this is a generic method, parameterized with a type. It returns the Iterable<T> type which gives two benefits:
- we can only iterate over it and it is not possible to add new objects,
- the result contains objects of the specific class, which means strong typing with all the benefits.
Sample usage (we iterate over the objects of the Movie2 class, which makes possible, e.g. direct calls of its method, e.g. getTitle()):
var movieExtent = ObjectPlus.getExtent(Movie2.class);
for (Movie2 movie : movieExtent) {
System.out.println(movie.getTitle());
}
Helper methods
Our small library would not be useful without auxiliary methods, such as displaying extent:
public class ObjectPlus implements Serializable {
private static Map<Class<? extends ObjectPlus>, List<ObjectPlus>> allExtents = new HashMap<>();
// [...]
public static void showExtent(Class theClass) throws Exception {
List extent = null;
if(allExtents.containsKey(theClass)) {
// Extent of this class already exist
extent = allExtents.get(theClass);
}
else {
throw new Exception("Unknown class " + theClass);
}
System.out.println("Extent of the class: " + theClass.getSimpleName());
for(Object obj : extent) {
System.out.println(obj);
}
}
}
Important parts for this implementation and example of use in the business class:
- (6). As a method parameter, we provide an instance of the Class that we use to identify class membership.
- (9). We are checking to see if there is a key - that is, information about extent.
- (11). If so, then we return the value for this key, that is, the container containing the extent of that particular class.
- (14). If the key does not exist, then we do not have information about the extent. In this situation we throw an exception. This can be solved differently, eg. display a message, (silently) quit the method.
- (17). Display name of the class.
- (19). Then we iterate through the collection containing the extent and display information about each object. In fact, in this situation, it is implicitly called
toSring()from a particular object.
public class Movie2 extends ObjectPlus implements Serializable {
// ...
public static void showExtent() throws Exception {
ObjectPlus.showExtent(Movie2.class);
}
}
Examples of use
Now there will be a nice part - we'll show you how easy it is to use it. We just need to inherit from the ObjectPlus class (line 1) and call the constructor from the superclass (line 11). As you can see in the business class itself,
we do not have much work involved in extent or persistency. We can focus on implementing business functionality.
public class Movie2 extends ObjectPlus implements Serializable {
private String title;
private float price;
private Date additionDate;
public Movie2(String title, Date additionDate, float price) {
// Call the constructor from the super class
super();
this.title = title;
this.additionDate = additionDate;
this.price = price;
}
// Business implementation
}
Potential improvements
The ObjectPlus class is only a simple prototype and therefore can be improved in many ways, for example:
- use Java Generics. This gives us better typological control. However, due to the generality of the solution, we will have to go to some compromises anyway;
- persistency for class attributes (class members marked as
staticare not saved during serialization); - better management of adding to extent for inherited classes. We will return to this issue in the next lessons.
Source codes

Generic solution is one that can be applied to many cases. This is a fairly common trend in computer science: instead of creating a piece of the program just for a particular case, with a bit more work, we are able to write a library that can be used in many projects. This will save us work and reduce the number of errors. Unfortunately, this usually means (somewhat) more complicated implementation.