22
Sun, Dec
3 New Articles

Object-oriented Design for AS/400 Java Applications: Abstract Classes and Polymorphism

Java
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

This article, the fifth and final installment on object-oriented design with Java, introduces you to abstract base classes and polymorphism. Learn when and how to use abstract classes and how Java’s polymorphism kicks in to make it all work. This article explains how the combination of abstract base classes and polymorphism will provide you with coding strategies that the RPG programming language will probably never provide.

In the previous article in this series, I introduced you to the power of Java interfaces (see “Object-oriented Design for AS/400 Java Applications: Java Interfaces,” MC, November 1998). Java interfaces allow you to associate a set of function names that serve a similar or related purpose into a discretely named unit. The difference between a Java interface and a Java class is that the functions of an interface are without implementations whereas the functions of a class must have code implementations. Java interfaces work well when dissimilar entities exhibit a common behavior. You design a Java interface as a list of functions that represent common behaviors across dissimilar entities. You design Java classes to represent business entities. Each of these Java classes provides custom code implementation for the functions of its common interface. The idea is that classes of an application have a consistent interface—it’s just that the implementations of these interfaces vary from class to class.

But what do you do when you find yourself repeatedly coding the same exact implementation for the functions of an interface? You might consider using a base class instead. Perhaps you might want to define some nonconstant fields (which interfaces do not support), so, once again, you consider developing a base class. I covered base classes in the third article of this series (see “Object-oriented Design for AS/400 Java Applications: Inheritance,” MC, October 1998). A base class allows you to provide functions with code implementations for the general behaviors of a common group of entities. An example of a

base class might be a customer. That base class is then extended by a derived class to provide functions that are specific to the behaviors of a subset of that group of entities. Examples of classes derived from the customer class might be consumer and company class. Both of these classes inherit much of their attributes and behaviors from their common customer base class, but they both also have extended the base functionality of ancestry to provide behaviors specific to consumers or companies.

Here’s the rule of thumb for deciding when to use a base class or when to use an interface: Use a base class when the common functions can share the same implementation; use an interface when the common functions cannot share the same implementation. But what do you do when you have a mix (that is, a mix of functions, some of which should have base class code implementations, and some of which should be custom coded in implementations of the derived class)?

Abstract Classes

Java’s solution to this design dilemma is an abstract class. Java abstract classes are like Java interfaces in that they can define functions without providing implementations. An abstract class, like an interface, can never be instantiated. Unlike interfaces, however, abstract classes can optionally provide function implementations. It’s similar to your boss giving you a list of things for you to do: Some of them he wants done his way; the others, he doesn’t care how you do them—he just wants you to get them done. One other difference between abstract classes and interfaces is that an abstract class can have fields that are mutable (changeable).

A simple but very real example of an abstract class is Java’s own Number class (shown in Figure 1). I covered the Number class in my article on inheritance, but I neglected to mention that it was an abstract class. The Number class is a generalization of numbers, any number—be it integer, long, short, floating point, or fixed decimal. Some of the functions of the Number class have implementations and others do not, as they are abstract functions. The long and the short of the Number class is that many Java classes derive from the Number class. Each of these classes defines implementations of the abstract functions of the Number class that are appropriate for the manipulation of the characteristics of that derived class’s type of number. The BigDecimal class, for instance, is derived from the Number class to implement a fixed-point decimal class. Because the BigDecimal class extends the abstract Number class, it has to provide code implementations for the four abstract functions of the Number class (intValue, longValue, floatValue, and doubleValue). The two nonabstract functions of the Number class (byteValue and shortValue) do not have to be implemented in the BigDecimal class. If you create a BigDecimal object, that object has shortValue and byteValue functions; it’s just that the code implementation for these functions is provided by BigDecimal’s parent Number class.

The Transaction class shown in Figure 2 illustrates an abstract class. Transaction implements the ITransaction interface in the design of a base transaction class. Notice that the class definition begins with the Java keyword abstract. The abstract qualifier states that this class is not to be considered a concrete data type. You cannot instantiate an abstract class; it is only to be used as a base class. You couldn’t instantiate a Number object, for example, with new Number() because Number is an abstract class. When a class that is derived from an abstract class implements the null functions (functions that have no code implementations; i.e., they provide only their name, arguments, and return type) of its base, that class is then considered a concrete class. So you can create a BigDecimal object (which is a Number) with new BigDecimal() because BigDecimal is a concrete class.

The Transaction class defines the mutable fields of date, description, quantity, and cost. You may recall that Java interfaces only support constant fields, so the ITransaction interface could not declare the four fields that are obviously part of any transaction. The Transaction class implements the ITransaction interface, so it defines those four fields. The Transaction class, however, is an abstract class because it has no implementations for one of its functions: getDescription. The getDescription function has the obligatory abstract

keyword qualifier to tell the compiler that, “No, you did not want to provide code implementation for this function.” Abstract classes are handy when some of the functions of an abstract class have general implementations that are usable by its derived classes, but the other functions would best be implemented in the derived classes themselves.

Think about the Transaction class: The getDate function implementation will work fine for any class derived from the Transaction base class. Those derived classes, however, clearly would want to provide their own description for their transactions. Note that when one or more functions of a class are declared as abstract, the class definition also must be declared as abstract. The Java compiler then makes sure that any derived classes implement those abstract functions and that the abstract class is not to be instantiated as an object.

The Transaction class defined in Figure 2 is used as the base for both the WOTransaction and POTransaction classes as shown in Figure 3. Besides extending the characteristics and behaviors of its Transaction base class with the badge field and setBadge function, WOTransaction and POTransaction both provide the required implementations for the abstract getDescription function. The work order and purchase order transaction classes also implement functions for the IPrint interface. You may have noticed that the Transaction class implemented the IPrint interface. You may also be wondering, then, why the Transaction class didn’t provide its own implementation of the IPrint interface’s functions. After all, the Transaction class nicely provided implementations for all of the functions of the Itransaction interface! Abstract classes like WOTransaction are not required to provide code for any of the functions that have the abstract keyword specified. All the functions of an interface are implicitly tagged as abstract, and the responsibility of implementing the code for abstract functions is delegated to those classes that implement that interface. The Transaction class—because it is an abstract class—does not have to provide code for the functions of the IPrint interface; it effectively delegates the responsibility for implementing the IPrint interface to any classes that extend the Transaction class—like the WOTransaction class.

Polymorphism

The beauty of base classes, abstract base classes, and Java interfaces is that you can design other Java classes to use the general design of the functions of those base classes and interfaces. You wouldn’t, for instance, create separate print classes for WOTransaction and POTransaction. You would create a generic print class. The PrintTransactions class of Figure 4 can print information about any object whose class implemented the IPrint interface (which includes the WOActivity and POActivity classes from my last article). The TestAbstractTransaction class of Figure 5 creates (instantiates, in object-oriented parlance) POTransaction and WOTransaction objects in its constructor. Both of these classes extend the abstract base class Transaction. The printGroup array, which holds objects of the Transaction class, is initialized to contain references to the po and wo objects. Then the TestAbstractTransaction’s constructor instantiates a PrintTransaction object. Note that the constructor for PrintTransaction is passed a constant value—a static final field from the PrintTransaction class—that tells the print utility class to print to a file. PrintTransaction’s print function accepts an argument of the array of printGroup Transaction objects. The PrintTransaction print function, by the way, does not take a reference to Transactions, nor does it take a reference to POTransactions or WOTransactions. The print function was designed to take an object whose class implemented the IPrint interface; the print function was designed to an interface. At runtime, PrintTransaction’s print function invokes the function of the class (that the printGroup object was originally created as) by using an object-oriented feature called polymorphism. The listing in Figure 6 shows that the PrintTransaction print function magically uses the appropriate implementations of the IPrint interface. (The code for the Java classes used in this article can be found at MC’s Web site at www.midrangecomputing. com/mc/99/01.)

Don’t Do Today What You Can Put Off Until Tomorrow

You may be wondering how all this polymorphism stuff works. Let me give you the two-minute explanation. You probably understand that with legacy programming languages like RPG, function calls are tied to their code implementation at compile-time (either by reference or by copy). Consider for instance, an RPG IV program where a driver module invokes another module. The code for that other module is bound to the program at compile-time. When using the bind-by-copy method, all of the code for the invoked module becomes an integral part of the compiled program. You would get the same effect if you copied the entire source of that module to within the driver module. With Java, function calls are not bound at compile-time; they are bound at runtime. When an object invokes a function, the Java Virtual Machine (JVM) uses the object reference and calls the function that the object’s class implemented. The code for the function is dynamically bound at runtime.

When you design to an interface, you declare your variables to be of either an interface or a base class. Those variables are known as object references because they are handles to object instances of any object whose class had implemented the interface (if the variable is typed as an interface) or extended the base class (if the variable is typed as a base class). When you use an object reference to invoke a function, the code for that function is bound at runtime, rather than compile-time, to the function implementation of the class that was used to instantiate the referenced object. This is a difficult concept to grasp because it seems that if the function that uses that object knows it only as a base class or an interface, it would not know to invoke the function of a class. The secret is in the object reference. That reference is a handle to an object, and the object knows what class it was created as and, hence, the appropriate function to call. This runtime process does add overhead, which is one of the biggest reasons why Java (and other object-oriented languages) is slower than non-object-oriented languages such as RPG and COBOL. But the pluggability of Java interfaces, classes, and abstract classes provides additional benefits, the value of which go way beyond the cost of a few extra CPU cycles.

Twenty or 30 years ago, assembly language programmers were saying that third- generation languages like C, RPG, and COBOL consumed too many resources, but how many assembly language programmers do you know? We all do know, however, that the industry moved to third-generation languages because these languages made programmers more productive. The industry at large has already moved on again to object-oriented languages to raise programmers to yet a higher level of productivity. Now that Java provides us with a viable object-oriented business language, it’s time that AS/400 shops adopt the proven strategies of object-oriented design.

public abstract class java.lang.Number {

public abstract int intValue();

public abstract long longValue();

public abstract float floatValue();

public abstract double doubleValue();

public byte byteValue();

public short shortValue();

public java.lang.Number();
}

class java.math.BigDecimal

extends java.lang.Number {

// code omitted

public int intValue();

public long longValue();

public float floatValue();

public double doubleValue();

// code omitted
}

Figure 1: Java’s BigDecimal class provides code implementations for the abstract functions declared in its parent class, java.lang.Number

import java.math.BigDecimal;
import java.util.Date;

public interface IPrint {
public void printDetail();
public void printFooter();
public void printHeader();
}

public interface ITransaction {
public Date getDate();
public String getDescription();
public int getQuantity ();
public void setDate(Date date);
public void setDescription(String desc);
public void setQuantity (int qty);

}

public class RdbEntity {
// DB2/400 create, read, update, delete functions
}

abstract class Transaction extends RdbEntity

implements IPrint, ITransaction {
protected Date date;
protected String description;
protected int quantity;
protected BigDecimal cost;
// no implementation for IPrint interface

// no implementation for new function

abstract public String getDescription();

// implementations for ITransaction interface

public BigDecimal getCost () {return cost;}
public Date getDate() {return date;}
public int getQuantity () {return quantity;}
public void setCost(BigDecimal cost) {

this.cost = cost;}
public void setDate(Date date) {

this.date = date;}

public void setDescription(String desc) {

this.description = desc;}
public void setQuantity (int qty) {

this.quantity = qty;}
}

Figure 2: The Transaction class is an abstract class because it does not provide code implementation for the abstract getDescription function

public class WOTransaction extends Transaction{

private int badge;

// implementation of abstract function

public String getDescription() {

return date+" Badge: "+badge+" Desc: " +

description;}

// implementation for IPrint interface

public void printDetail() {

System.out.println(getDescription());}

public void printFooter() {

System.out.println("*** WO Footer *** ");}

public void printHeader() {

System.out.println("Work Order Trans:");}

public void setBadge(int badge) {

this.badge = badge;}
}

public class POTransaction extends Transaction{

private String shipMethod;

// implementation of abstract function

public String getDescription() {

return " Ship via: " + shipMethod +

" Desc: "+description;}

// implementation for IPrint interface

public void printDetail() {

System.out.println(getDescription());}

public void printFooter() {

System.out.println(

"*** POTransaction Footer ***");}

public void printHeader() {

System.out.println(

"Purchase Order Transaction:");}
}

Figure 3: The WOTransaction and POTransaction classes provide code implementations for the abstract getDescription function of their base Transaction class and the IPrint interface

public class PrintTransactions {

public static final String FILE = "FILE";

public static final String PRINTER="PRINTER";

PrintTransactions (String printer) {

// create and open file here if "FILE"

// passed else write to printer

}

public void finalize() throws Throwable {

// flush file or printer

}

public void print(IPrint[] printGroup) {

for (int i = 0; i < printGroup.length; i++)

{

printGroup[i].printHeader();

printGroup[i].printDetail();

printGroup[i].printFooter();

}

}

}

Figure 4: The PrintTransactions class can print information about any object whose class implemented the IPrint interface

public class TestAbstractTransaction {

public TestAbstractTransaction ( ) {

POTransaction po = new POTransaction();

po.setDescription("purchase order shipped");

WOTransaction wo = new WOTransaction();

wo.setDescription("W/O routing step 110 completed");

wo.setDate(new java.util.Date());

wo.setBadge(928);

// create an array and initialize it to the

// po and wo Activity objects

Transaction[] printGroup = {po, wo};

PrintTransactions print =

new PrintTransactions(PrintTransactions.FILE);

// print is expecting an array of IPrint but since

// the abstract Transaction class implements

// the IPrint interface, polymorphism handles it.

print.print(printGroup);
}

public static void main(java.lang.String[] args) {
new TestAbstractTransaction();

}

}

Figure 5: The TestAbstractClass was designed to use the common interface of the Transaction abstract base class and the IPrint interface

Purchase Order Transaction:

Ship via: null Desc: purchase order shipped
*** POTransaction Footer ***
Work Order Trans:
Tue Nov 03 14:15:10 EST 1998 Badge: 928 Desc: W/O routing step 110 completed
*** WO Footer ***
Purchase Order Transaction:

Ship via: null Desc: purchase order shipped
*** POTransaction Footer ***
Work Order Trans:
Tue Nov 03 14:15:38 EST 1998 Badge: 928 Desc: W/O routing step 110 completed
*** WO Footer ***

Figure 6: The output of the TestAbstractClass application demonstrates the results of designing to an interface using both abstract classes and interfaces

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: