Image of Define an abstract property in java

ADVERTISEMENT

Introduction

Abstract keyword is normally applied on classes and methods in order to delegate the implementation of some behavior to subclasses. Java doesn’t support abstract properties, if you try to mark a class property as abstract, you get a compilation error.

In this tutorial, we introduce 2 ways for defining abstract properties which are set by subclasses without using the abstract keyword.

1. Use case

Suppose we want to implement a transaction logging module which logs information about a particular transaction. We want our module to be abstract so that we’re able to support different ways of logging: file system logging, database logging … etc.

Our engine concatenates all the transaction information in one String using a predefined separator which depends on the logging mechanism, for example the comma character “,” is used for logging a comma-separated string in the file system.

So the separator looks as abstract to our engine and needs to be defined explicitly by each logging mechanism.

2. Define abstract property

In this section, we provide 2 ways for delegating the definition of the separator to subclasses.

2.1 Define a parameterized constructor in the abstract class

The first way of delegating the definition of a dynamic property in an abstract class is through defining a parameterized constructor.

So we create our engine as the following:

public abstract class TransactionManager {
    private String separator;
    
    public TransactionManager(String separator) {
        this.separator = separator;
    }
    
    public abstract void writeTransaction(String result);
    
    public Transaction startTransaction()
    {
        Transaction transaction = new Transaction(System.currentTimeMillis());
        return transaction;
    }
    
    public void endTransaction(Transaction t) {
        long processingTime = System.currentTimeMillis() - t.getStartTime();
 
        StringBuilder logBuilder = new StringBuilder();
        logBuilder.append(t.getStartTime());
        // Notice the use of this.separator
        logBuilder.append(this.separator);
        logBuilder.append(processingTime);
        logBuilder.append(this.separator);
        logBuilder.append(t.getData());
 
        String result = logBuilder.toString();
        writeTransaction(result);
    }
}

When defining a parametrized constructor in an abstract class, the subclasses are forced to define their own constructors and call the super constructor. Hence, we forced the separator attribute to be dependent on the used logging mechanism.

As noticed, our engine provides implementation for the static behaviors which are common between all logging mechanisms like: startTransaction(), endTransaction(), while it delegates the dynamic behavior writeTransaction() to children classes.

Now, if we want to create a transaction manager which logs to a file system, we would define it as the following:

public class TransactionManagerFS extends TransactionManager{
 
    // The IDE forces you to implement constructor.
    public TransactionManagerFS(String separator) {
        super(separator);
    }
       
    @Override
    public void writeTransaction(String result) {
        System.out.println("The following transaction has just finished: " );
        System.out.println(result);
    }
}

Let’s do some test to see how the above implementation works:

public static void main(String[] args) throws InterruptedException {
        // we pass the separator explicitly in the constructor
        TransactionManager transactionManager = new TransactionManagerFS(",");
        Transaction transaction = transactionManager.startTransaction();
        transaction.setData("This is a test transaction !!");
        Thread.sleep(1500);
        transactionManager.endTransaction(transaction);
    }

Output:

The following transaction has just finished: 
1502179140689,1501,This is a test transaction !!

3. Passing separator through getter method

Another way to delegate the definition of a dynamic property is through defining an abstract getter method, which retrieves the required separator based on the used logging mechanism. In our engine, we then use the getter method whenever we want to use the separator.

So we modify our engine to be as the following:

public abstract class TransactionManager {
 
    public abstract String getSeparator();
    public abstract void writeTransaction(String result);
    
    public Transaction startTransaction()
    {
        Transaction transaction = new Transaction(System.currentTimeMillis());
        return transaction;
    }
    
    public void endTransaction(Transaction t) {
        long processingTime = System.currentTimeMillis() - t.getStartTime();
 
        StringBuilder logBuilder = new StringBuilder();
        logBuilder.append(t.getStartTime());
        // Notice the use of getSeparator()
        logBuilder.append(getSeparator());
        logBuilder.append(processingTime);
        logBuilder.append(getSeparator());
        logBuilder.append(t.getData());
 
        String result = logBuilder.toString();
        writeTransaction(result);
    }
}

respectively TransactionManagerFS becomes as the following:

public class TransactionManagerFS extends TransactionManager{
 
    @Override
    public String getSeparator() {
        return ",";
    }
       
    @Override
    public void writeTransaction(String result) {
        System.out.println("The following transaction has just finished: " );
        System.out.println(result);
    }
}

We then update our main class to use the new implementation, and we make sure that the same result is obtained.

public static void main(String[] args) throws InterruptedException {
        // The separator is defined implicitly using getSeparator() method of the manager
        TransactionManager transactionManager = new TransactionManagerFS();
        Transaction transaction = transactionManager.startTransaction();
        transaction.setData("This is a test transaction !!");
        Thread.sleep(1500);
        transactionManager.endTransaction(transaction);
    }

Output:

The following transaction has just finished: 
1502179140689,1501,This is a test transaction !!

That’s it, if you have any other way just share it with me here in the comments section below.

Summary

Abstract keyword is normally applied on classes and methods in order to delegate the implementation of some behavior to subclasses. Java doesn’t support abstract properties, if you try to mark a class property as abstract, you get a compilation error.

Next Steps

If you're interested in learning more about the basics of Java, coding, and software development, check out our Coding Essentials Guidebook for Developers, where we cover the essential languages, concepts, and tools that you'll need to become a professional developer.

Thanks and happy coding! We hope you enjoyed this article. If you have any questions or comments, feel free to reach out to jacob@initialcommit.io.