Showing posts with label inheritance. Show all posts
Showing posts with label inheritance. Show all posts

Monday, December 15, 2014

Inheritance Part 2: That's Rather Abstract, Isn't it?

I hope you've been browsing through Java source code outside of your own and the small samples I've provided on this site.  Reading and trying to understand code can only help you in the long run.  You will encounter different code styles, you will see interesting data structures, API calls you didn't know existed, all sorts of stuff.

Today I'm going to talk about something you might have seen, but if you haven't, it's no big deal.  It is the keyword abstract.

We use abstract as part of the definition of a class, which indicates that while it is in fact a valid class file, nobody is allowed to create an object of that type.  That doesn't mean that it's useless though.  Remember, we have inheritance on our side.  You can create concrete classes (concrete is just the opposite of abstract, indicating a class from which you can create objects.  No special keyword is needed for that) that extend abstract classes all you want.

We also use abstract as part of method signatures when we want to create the idea of a particular method call but wish to defer the actual implementation of said method to our concrete classes.  This is similar in concept to interfaces, and sometimes it's more of a personal decision as to which you will use.

You see a lot of abstract classes in use when you look through frameworks, which are basically bodies of code designed to accomplish some function but with the details left up to the final developer.  There are business frameworks, logging frameworks, GUI frameworks, reporting frameworks, the list is very long.  Usually the trigger for creating a framework is when someone realizes they've just written the same thing for the fifth or tenth time, and that the differences between the various programs are relatively minor.  Maybe you query a database, format a report and write it to a file, and it's always the same except for the details of which fields you look for and print.

This is a great application for abstract classes.  Let's take a look at a simple one:

public abstract class Report {
}

Well, that's about as simple as it gets.  To be fair it's also about as useless as it gets but never fear, we'll fill in some details later.  Actually it's not entirely useless.  You might want to have a set of classes that all share a common base class so that you can collect them into a Collection of some sort, although you could also make this work with an interface.  Abstract classes really come into their own when you include some code within them.  So let's write something.

public abstract class Report {
    public abstract void GenerateOutputLine(String dataLine);
    public abstract String ReadData();

    public void runReport() {

        String dataLine = null;
        while( null != ( dataLine = ReadData() ) ) {
            GenerateOutputLine(dataLine);
        }
    }
    
}

OK, that was simple enough.  We have defined not only our abstract Report class, but made it do a little bit of work for us.  It calls ReadData until that method returns a null, and then takes the value returned from ReadData and passes it to GenerateOutputLine.  Of course, you can't actually run this report, you need to create at least one concrete class based on it.  That could be a simple test driver designed to make absolutely certain that your framework behaves properly with known data.  It could be something that reads from one file and writes to another, which would basically give you a 'copy file' command.  It could be something that reads one file, does some calculations or modifications, and then spits out the changed data.  The options are nearly endless, despite the fact that this is a highly simplified example.

Let's write a simple test driver:

public class ReportTester extends Report {

    public static void main(String[] args) {
        ReportTester rt = new ReportTester();
        rt.runReport();
    }

    String [] dataLines = {
        "one", "two", "three", "four", "five"
    };
    int lineNumber = 0;

    public void GenerateOutputLine(String dataLine) {
        System.out.println(dataLine);
    }

    public String ReadData() {
        String toReturn = null;
        if (lineNumber < dataLines.length) {
            toReturn = dataLines[ lineNumber++ ] ;
        }
        return toReturn;
    } 
}

Now if we were to run 'ReportTester' we'd get the output:

one
two
three
four
five

The main driver of the program is still in that 'Report' superclass, but the implementation details have been kept in ReportTester.  We run the code in Report, and it calls the methods it finds in ReportTester to do the work.  ReportTester itself is quite simple.  In addition to a simple main that does nothing more that create ReportTester and run it (actually running that method from the superclass Report), it defines the specific implementations for our two abstract methods.  One method does nothing more than write to the console and I hope I don't need to explain that.  The other goes through an array of Strings, returning the next one in the array and increasing the index value each time it's invoked until it reaches the end of the list, at which time it returns a null which triggers the end of the loop in 'runReport'.

This is of course tremendous overkill for a program of this size, but as your projects get more involved, the ratio of abstract code to concrete code is likely to change quite a bit.  System maintenance gets easier and faster, and your software gets more robust.  After all, a bug fix to a framework may take care of issues across several dozen different specific implementations of different reports.

I will of course revisit this topic later, because we've only scratched the surface of the possibilities brought on by object orientation.  With enough small components intelligently built, one can assemble new programs almost like putting together Tinker Toys or Lego.  One's systems can be defined in large part by configuration files, and changed easily at will, all without writing, testing or deploying new code.


Wednesday, December 3, 2014

Diamonds are not an Object's Best Friend

Once again, I turn to a post I made on reddit for inspiration.

Some people wonder why inheritance branch but never join.  Why not tie two different families of objects together and extend both of 'em?

I can see the point of that, one could conceivably build objects that join several disparate branches of inheritance into large and versatile classes.  Conceivably.  One could also get oneself into a world of trouble, though, and this is most easily explained via the so-called diamond inheritance problem.

Please note that you cannot do this and expect your code to compile, it's just a conceptual demonstration.
Diamond.java
public class Diamond extends Class1, Class2 {
}

Class1.java
public class Class1 {
    public void doSomething() {
         System.out.println("Class1.doSomething()");
    }
}

Class2.java
public class Class2 {
    public void doSomething() {
         System.out.println("Class2.doSomething()");
    }
}
Now you have a main that uses the above classes:
public static void main(String[] args) {
    Diamond diamond = new Diamond();
    diamond.doSomething();
}
So, when you run main, what happens? This is the essence of the diamond problem. You wind up having two lines of inheritance converging on one and run into a quite serious issue.

This basically just scratches the surface of the problem, of course.  We can also think about what happens when you try to invoke something in super.  The question would have to become 'which super'?  There are many potential negative ramifications from the interactions that could be created by allowing this kind of inheritance, and the creators of Java decided that the best way to avoid them was by preventing the issue altogether.
One can achieve most of the positive effects of multiple inheritance via Interfaces and a few helpful design patterns such as Adapter, Composite and Decorator.

Inheritance Part 1 - That Class is Super!

I would like to discuss the concept of inheritance, and no, I don't mean the vase that Aunt Gladys left you in her will.

When we talk about inheritance in object oriented programming languages, we are talking about how one object can be based on another object.  In Java, we use the keyword extends to do this.

However, in order to make this as simple as possible, let's ignore even that keyword for a moment.  EVERY single Java class automatically extends the class Object without any effort at all on your part.

Let's look at an example Person data object, which I will continue to use as an example for future posts.

public class Person {
    private String firstName;
    private String lastName;

    public Person(String newFirstName, String newLastName) {
        firstName = newFirstName;
        lastName = newLastName;
    }
    ...
}

As I mentioned above, Person automatically extends Object, so in reality the class definition is really more like this:
    public class Person extends Object {

But we just don't bother writing it.  We call Object the superclass of Person and Person is called a subclass of Object.

As far as what this brings to the table, let's use what is perhaps simultaneously one of the most common features and one of the most common issues people have with objects when they are starting out.  Printing them out.

If I create a new Person object and want to see what it looks like, I might be tempted to do this:

    Person joe = new Person("Joe", "Blow");
    System.out.println(joe);

If I do this, I will be sorely disappointed when I run my program, because while I might expect to see this:

    Joe Blow

I will instead see something like this:

    Person@4f1d0d

At this point, I begin to tremble and sweat.  In a panic I turn to stackoverflow.com or /r/javahelp and complain that my object is broken and my printouts are garbled.  The regulars there probably make fun of me for not actually posting my code correctly or something.  My dog bites me and my girlfriend refuses to go out with me.  OK, I may have exaggerated a bit.

But what's happening here is that when you call 'System.out.println(joe)' you are (again, invisibly) actually in effect calling 'System.out.println(joe.toString())'.  After all, you need to turn your object into something that can be displayed on the console.  

You might at this point be saying to yourself "But wait, I don't have a 'toString' in Person" and in a sense you're right.  You certainly didn't create such a thing.  But you did in fact inherit a toString method from Object.  Object is pretty ignorant about your code, though, and has no idea how you intend to format your printouts, so it just tells you the type of object and where in your JVMs memory that object is stored.  In general, if a superclass contains a method, a subclass can call it as though it were a part of the subclass itself.

This is all inheritance really is, in the end.  If we try to call a method or refer to a variable in an object, and it isn't mentioned in that object, the compiler tries to see if it's available via inheritance.  If so, you avoid compile errors and things 'work'.  They may not work the way you want, but at least something interesting happens.

Let's fix the problem now.  I could take the cheap and easy way out like this:

    System.out.println(joe.firstName + " " + joe.lastName);

And that would work.  But honestly, all we really need to do is create our own 'toString' method in Person that does things the way we want.  This will save us the effort of having to write the above code everywhere that wants to just display someone's name.  You'll really appreciate that when you decide that you'd rather display that guy as "Blow, Joe" instead and have to change code in 23 different places.

Add the following block of code to Person:

public String toString() {
    return firstName + " " + lastName;
}

And just like that, the original program will print the name out the way you wanted it to in the first place.

There, you've done it.  You've made use of an inherited method, and then you created an override for it.  An override is nothing more or less than specifying that instead of using the method found somewhere up the line of inheritance, you want to use this specific method instead.

When it comes down to it, overriding toString is just the tip of the iceberg, and I will of course be revisiting this later on.  There are some extremely interesting things that you can do with inheritance, and it forms the basis for many important language structures.  For now though, just remember that one class can extend another one, and that when it does so it has the option of replacing methods in its superclass with its own more specific version.  There are a few other rules you'll need to follow, but most of them are not too horrible to deal with.