Chapter 8. Inheritance

Inheritance is a form of software reusability in which programmers create classes that "inherit" an existing class's data and behaviors and enhance them with new capabilities. Software reusability saves time during application development. It also encourages the reuse of proven and debugged high-quality software, which increases the likelihood that a system will be implemented effectively. When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of the existing class. The existing class is called the base class, and the new class that is derived from the base class is called the derived class. In Java and the JavaFX scripting language, a base class is called a superclass and the derived class is called a subclass.

Inheritance is an integral part of the JavaFX scripting language. When you inherit, you say "This new class is like that old class with new data members and member functional that adds the additional capabilities." You state this in code by giving the name of the class as usual, but before the opening brace of the class body, put the keyword extends followed by the name of the base class or superclass. When you do this, you automatically provide all the fields and methods in the base class to the derived class or subclass, and you can define your own data members and member functions in the derived class.

Listing 8-1 shows a simple example of how we can inherit a class in JavaFX.

Example 8.1. A simple inheritance example

class Shape{
    var x : Number=10.0;
    var y : Number=10.0;
    function drawShape(){
        println("Draw the shape");
    }
}
class Circle extends Shape {
    var radius : Number;
    override function drawShape(){
        println("Draw circle at x={x} y={y} with radius={radius}");
    }
}
var circle : Circle = Circle{
    x : 40
    y : 50
    radius : 5
}
circle.drawShape();

Output

Draw circle at x=40.0 y=50.0 with radius=5.0

In Listing 8-1, the class shape represents any shape to draw on the computer. It's a base class or superclass, which has two data members, x and y, representing X and Y coordinates on the screen, and drawShape(), a method to draw the shape.

Listing 8-1 also shows a class Circle, which is extended from the Shape class. So class Circle is a derived class, since it inherits all the data members and the member functions of the Shape class and also defines its own data as well as member functions, thereby enhancing the functionality of the base class. Since a circle is also a shape, we are overriding member functions of the drawShape( ) method to draw the circle, and the overridden method is marked with the override keyword. (Method overriding is covered in Chapter 5, "Functions.")

Finally, when you create an instance of the class Circle, you have the data members x, y, and radius. When you call the circle.drawShape() method on an instance of the Circle class, the Circle.drawShape() method is invoked. If you need to invoke a member function of the Shape class, then you should invoke drawShape() on a Shape class instance.

The Order of Initialization of Data Members

Since inheritance involves two or more classes, in general there is a relationship established between the base and the derived class. When you create an instance of the derived class, it contains within it a base class member as a subobject. This subobject is the same as if you had created an instance of the base class by itself. It's just that from the outside, the subobject of the base class is wrapped within the derived class object. When we initialize an object of the derived class, first the base class data member is initialized and then the derived class data member. Let us see an example that demonstrates the order in which the data members are initialized across base and the derived classes, through the init block. (Refer to Chapter 6, "Class Definitions," for more information on the init block. Briefly, it is equivalent to the constructor in Java and is called automatically when a class is instantiated. It is used to do any initialization such as grouping different shapes to create a custom UI control.) Listing 8-2 shows the code.

Example 8.2. Data member initialization order

class Base {
        var a : Integer;
        var str:String;
        init{
            println("Base class init block.");
            a = 10;
            str="Base";
          }
    }

    class Derived extends Base {
        var k:Number;
        init {
println("Derived class init block.");
            k=34.34;
        }

        function printValues() : Void {
            println("a = { der.a }  str = {der.str}  k = {der.k}");
          }

    }

    var der:Derived = Derived{}
    der.printValues();    // a = 10  str = Base  k = 34.34

Output

Base class init block.
Derived class init block.
a = 10  str = Base  k = 34.34

Listing 8-2 has two classes, named Base and Derived. Each class has its own data members and its own init blocks to initialize the data members. When we create an object or instance of the class Derived, the Base class's init block is called first and then the init block of the derived class. Hence the base class's data member initialization is done prior to that of the derived class. Finally, the printValues() method is called to verify the initialized data member values.

Overriding Data Members

Similar to overriding a member function of the base class, you can override the data members or instance variables of the base class as well. Overriding a data member is straightforward and simple. In the derived class, you declare a data member or instance variable with the same name as the superclass data member, but with the override keyword as one of its modifiers, and without any type specifier. A data member can be initialized with a different value in the derived class when it is being overridden, and if the variable is left uninitialized in the derived class, the initialization specified in the base class would be considered.

Listing 8-3 shows an example.

Example 8.3. Overriding data members

class Game{
    var player:Integer = 2;
    var item : String="Some Game";
    var location:String="Out Door";

    function printValues() : Void {
        println("player = {player}  item = {item}  location={location}");
    }
    }
class Cricket extends Game{
    override var item="Bat,Ball and stumps";
    override var location;        // data member is not initialized.

    override function printValues() : Void {
        println("player = {player}  item = {item}  location={location}");
    }
}
var game:Game=Game{ }
    game.printValues();
    var cri:Cricket = Cricket{}
    cri.printValues();

Output

player = 2  item = Some Game  location=Out Door
player = 11  item = Bat,Ball and stumps  location=Out Door

In Listing 8-3, we are overriding three data members of the base class Game from within the derived class Cricket, and we are initializing two of the overridden members with new values in the derived class. We are leaving the variable location uninitialized. We are creating an instance of the Game class as well as the Cricket class and printing the values. In the output, notice that the value ofthe location variable in the cri instance is automatically initialized to Out Door, demonstrating that the initialization is inherited from the base class.

Note

Attempting to specify a type specifier in a statement overriding a data member or instance variable will cause a compiler error.

Use of the super Keyword

Like Java, JavaFX Script also supports the super keyword, which can be used to invoke a method available in the base class from within any member function of the derived class. Let us see a simple example of how it can be used in JavaFX, in Listing 8-4.

Example 8.4. Usage of the super keyword

class A{
   function fun1(){
       println("Base class Function-1");
   }
   function fun2(){
       println("Base class Function-2");
   }
}
class B extends A{
   function bFun1(){
       println("Derived class Function-1");
   }
   override function fun2(){
       super.fun2();
       println("Derived class Function-2");
   }
}
var obj:B=B{}
obj.fun2();

Output

Base class Function-2
Derived class Function-2

The code shown in Listing 8-4 is self-explanatory; we call the base class's fun2() function from within the overriding function of the derived class.

It is also possible to call a non-overridden function of the base class using the super keyword, but doing that generally does not make sense, since you can already invoke the non-overridden member function directly.

Mixin Classes

JavaFX Script has supported multiple inheritance right from its inception, but the concept of multiple inheritance has been radically changed as of JavaFX 1.2. Prior to 1.2, a normal JavaFX class could directly extend from any number of other classes, but as of version 1.2 this has been streamlined by enforcing certain restrictions on inheriting multiple classes through mixins. The mixin keyword in JavaFX Script refers to a new form of multiple inheritance with same features as before but with additional benefits such as much simpler and much faster code generation. The concept of a mixin is generic within OOP. Mixins are more or less like interfaces in Java except that they can have variables and implementations that can be inherited by all the classes that extend a mixin class.

The mixin keyword is applied to classes, and such a class is described as a mixin class. A mixin class is a like a regular JavaFX class; it contains data members, member functions, an init block anda postinit block. The difference is that the class cannot be instantiated directly; it is instead designed to be extended and used by subclasses.

Some basic points should be remembered while implementing inheritance in JavaFX:

  • JavaFX Script classes are allowed to extend at most one other JavaFX Script class as a superclass. This superclass can be either a Java class or a JavaFX Script class.

  • JavaFX Script classes are allowed to extend any number of JavaFX Script mixin classes.

  • JavaFX mixin classes are allowed to extend any number of other JavaFX mixin classes and can also extend any number of Java interfaces.

As mentioned earlier, a mixin class can extend one regular (non-mixin) class and any number of mixin classes.

Now let us see an example that demonstrates the difference between how Java and JavaFX achieve a similar functionality and how simple JavaFX is compared to Java (Listing 8-5).

Example 8.5. A comparison of mixins in Java and JavaFX

FileName: GreetWorld.java
public interface GreetWorld {
    public void printGreetings();
}
FileName: Greeting1.java
public class Greeting1 implements GreetWorld {
    String courtesy = "Hello";
    String name = "Praveen!";

    public void printGreetings() {
        System.out.println(courtesy + " " + name);
    }
    public static void main (String args[]) {
        Greeting1 g = new Greeting1();
        g.printGreetings();
    }
}
FileName: Greeting2.java
public class Greeting2 implements GreetWorld {
    String courtesy = "Hello";
    String name = "Lawrence!";

    public void printGreetings() {
        System.out.println(courtesy + " " + name);
    }
    public static void main (String args[]) {
        Greeting2 g = new Greeting2();
        g.printGreetings();
    }
}
FileName: Greetings.fx
public mixin class GreetWorld {
    var courtesy: String = "Hello";
    var name: String;

    public function printGreetings(): Void {
        println("{courtesy} {name}");
    }
}

class Greeting1 extends GreetWorld {
    override var name = "Praveen!";
}
class Greeting2 extends GreetWorld {
    override var name = "Lawrence!";
}

public function run() {
    var g1 = Greeting1 {}
    var g2 = Greeting2 {}
    g1.printGreetings();
    g2.printGreetings();
}

Output

(Executing Greeting1.java) Hello Praveen!
(Executing Greeting2java) Hello Lawrence!
(Executing Greetings.fx)
Hello Praveen!
Hello Lawrence!

As you see in Listing 8-5, the JavaFX code is much simpler than the Java code. You can easily see that there is an implementation for the method printGreetings() in the mixin class, and so the subclasses do not have to implement the method again. The implementations of printGreetings in Greeting1 and Greeting2 are identical, a duplication that is avoided in JavaFX. In addition to this, all the classes are maintained in a single file in JavaFX; in Java you need a separate file for each of the public classes and interfaces.

Note

A mixin class cannot be instantiated directly and can only be extended. However, you can instantiate a derived class that extends a mixin class, and it is legal to cast a derived class's object to a mixin reference. It is also legal to use a mixin class with the instanceof operator.

Listing 8-6 demonstrates how to create a subclass from a regular class and a mixin class.

Example 8.6. Deriving a mixin and a regular class together

class Base {
    var x : Integer;
    function showX() {
        println("x = {x}");
    }
}

mixin class MixBase  {
    var y : Integer;
    function showY( ){
        println("y = {y}");
    }
}
class SubClass extends MixBase , Base {
    var z : Integer;
    function showZ() {
        println("z = {z}");
    //super.showY(); - ILLEGAL: WILL NOT COMPILE
    //super.showX(); - LEGAL
    }
}

var obj = SubClass{
    x : 10;
    y : 20;
    z : 30;
}

obj.showX();  // x = 10
obj.showY();  // y = 20
obj.showZ();  // z = 30

Output

x = 10
y = 20
z = 30

Listing 8-6 shows how a regular class and a mixin class can be extended by a subclass. Notice that a mixin class can also have data members and functions just like a normal class, and those members can be accessed the same way as we do with a normal class.

Notice also that the contents of the mixin class, such as member functions and data members, are included in the derived class rather than inherited. They become part of the derived class during compilation. Hence, the super keyword can only refer to the extension of a non-mixin class and not a mixin class. That's why super.showY() will not compile if uncommented in this example, whereas super.showX() will compile correctly.

Creating a Subclass from Multiple Mixin Classes

When a class inherits from multiple other classes, the technique is called multiple inheritance. Java does not support multiple inheritance through classes, but it can be done using Java interfaces. By contrast, in the JavaFX scripting language, mixin inheritance allows you to extend multiple mixin classes, giving you the benefits of multiple inheritance. Now let us see how we can create a subclass extending from multiple mixin classes (Listing 8-7).

Example 8.7. Extending multiple mixin classes

public mixin class Mixin1 {
    var a : Integer;
    public function getA(){
        return a;
    }
}
public mixin class Mixin2 {
    var b : Integer;
    public function getB(){
        return b;
    }
}
class Mixee extends Mixin1, Mixin2 {
    var c : Integer;
    public function getC(){
        return c;
    }
}
function run () {
    var obj = Mixee{
        a : 10;b:20;c:30;
    }
    println("a = {obj.getA()}  , b = {obj.getB()}  , c= {obj.getC()}"); // a = 10  , b = 20
, c= 30
}

output :-
a = 10  , b = 20  , c= 30

In Listing 8-7, we have defined two classes, named Mixin1 and Mixin2. Each class contains a single Integer data member and a member function to return its data value. The class Mixee extends both the Mixin1 and Mixin2 classes, inheriting the data members and member functions of both parent mixin classes and thus enabling multiple inheritance. Now with an object of the Mixee class, you can access the data members and member functions of Mixee, Mixin1, or Mixin2.

The Order of Initialization in Multiple Inheritance

Recall from the beginning of this chapter that init blocks are always executed in order from parent class to child class. Now when we have multiple parent classes, the same order of parent-to-child is maintained, and the parents' init blocks would be called before the child's. But the order among multiple parents is chosen by the order in which the parent classes are mentioned after the extends keyword in the derived class. If class A extends from B, C, and D, the order of initialization would be B

The Order of Initialization in Multiple Inheritance

Example 8.8. Order of initialization with multiple inheritance

public mixin class Mixin1 {
    init{
        println("mixin1 init block");
    }
}

public mixin class Mixin2 {
    init{
        println("mixin2 init block");
}
}

class Mixee extends Mixin1, Mixin2 {
    init{
        println("mixee init block");
    }
  }

function run() {
    Mixee{}
xs}

Output

mixin1 init block
mixin2 init block
mixee init block

As you see in the output of Listing 8-8, the order is always from parent to child and the order among multiple parents is decided by the order in which the parent classes are extended by the derived class. In this case, Mixee extends Mixin1 first and then Mixin2, and so the init blocks are executed in the order Mixin1

Order of initialization with multiple inheritance

Abstract Classes

There are situations in which you would want to define a superclass that declares the structure of a given abstraction without providing a complete implementation of every method. That is, sometimes you will want to create a superclass that only defines a generalized form that will be shared by all of its subclasses, leaving it to each subclass to fill in the details. Such a class determines the nature of the methods that the subclasses must implement. One way this situation can occur is when a superclass is unable to create a meaningful implementation for a method.

In such a scenario, you can create a class and mark certain methods to be overridden by subclasses compulsorily, by specifying the abstract type modifier. The responsibility of implementing these methods is with the subclass, since the parent class will not have any implementation. Thus, a subclass must override them—it cannot simply use the version defined in the superclass.

Any class that contains one or more abstract methods must also be declared abstract. To declare a class abstract, you simply use the abstract keyword in front of the class keyword at the beginning of the class declaration. There can be no objects of an abstract class. That is, an abstract class cannot be directly instantiated, because an abstract class is not fully defined. But you can create a reference of the abstract class.

Note

A mixin class can have abstract functions if you want to leave the implementation to the derived classes. But the class need not be referred to as abstract, unlike a normal Java or JavaFX Script class.

Using a JavaFX Class to Extend a Java Abstract Class

Because the JavaFX Scripting language is built on top of Java, JavaFX classes can extend Java classes (including abstract classes) and Java interfaces. For example, suppose you have created an application in Java and now want to migrate it to JavaFX. You can just extend those Java classes or implement the Java interfaces in your JavaFX directly to import the same behavior (that is, the interface) that you have already built using Java. Listing 8-9 shows an example; we have a Java abstract class, which is in a separate .java file, and this abstract class is extended and implemented by JavaFX subclasses.

Example 8.9. A JavaFX class extending an abstract Java class

Filename : Figure.java
package inheritance;

abstract class Figure {
    public float dim1;
    public float dim2;
    // abstract area class
    abstract float area();
}

FileName : AbstractImplementation.fx
package inheritance;
class Rectangle extends Figure {
    override function area() : Number  {
        println("Overriden area function in Rectangle class");
        return dim1 * dim2;
    }
}

class Triangle extends Figure {
    override function area() : Number {
        println("Overriden area function in Triangle class");
        return dim1 * dim2 / 2.0;
    }
}

function run(){
    var rect = Rectangle{ }
    rect.dim1 = 5.0;
    rect.dim2 = 5.0;
    println(rect.area());

    var triangle  = Triangle{ }
    triangle.dim1 = 10.0;
    triangle.dim2 = 5.0;
    println(triangle.area());
}

Output

Overriden area function in Rectangle class
25.0
Overriden area function in Triangle class
25.0

In Listing 8-9, we have a Java abstract class named Figure, which has an abstract method named area. This Java abstract class is stored in a file called Figure.java. The class is extended by two JavaFX classes, Rectangle and Triangle, and it overrides the abstract method area in a file called AbstractImplementation.fx. Both the Java and JavaFX classes are in the package inheritance. In the run() function, we create instances of the Rectangle and Triangle classes and assign values to the data members. Finally, we call the area() function of the respective implementations.

Anonymous Implementation of Java Interfaces

In Java, we often encounter situations where we need to implement interfaces anonymously, without specifying a class name, and the same thing can be done in JavaFX Script as well. One such example is the implementation of java.awt.event.ActionListener, which is demonstrated in Listing 8-10.

Example 8.10. Implementing Java interfaces anonymously

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

var counter: Integer = 0;

var listener = ActionListener {
    public override function actionPerformed(ae: ActionEvent): Void {
        println("Timer Triggered {counter}");
        counter ++;
    }
}

var timer: Timer = new Timer(1000, listener);
timer.start();

Output

Timer Triggered 0
Timer Triggered 1
Timer Triggered 2
Timer Triggered 3
..
..

In Listing 8-10, we are implementing a swing timer from Java to do some animation. A swing timer requires an action listener and a time interval at which the action listener's actionPerformed must be invoked. Here we are creating an object, named listener, of type ActionListener, by providing an inline implementation of the interface and using it in the Timer constructor. As you see in the output, the swing timer will invoke listener's actionPerformed() function every second (1000ms), infinitely. As you see the ActionListener implementation, it is the same listener method in Java implemented in JavaFX Script syntax with keywords such as override and function.

Summary

Inheritance allows a class to be derived from an existing class. The derived class has all the data and functions of the parent class but adds new ones of its own. Inheritance makes possible reusability—the ability to use a class over and over in different programs with enhanced features. JavaFX supports all types of inheritance as Java does, including multiple inheritance, but in a slightly different way, using mixin classes. A mixin class resembles a regular class in that it contains data members and member functions but is prefixed with the mixin keyword. A JavaFX Script class can extend at most one Java or one JavaFX Script class, but it can extend multiple JavaFX mixin classes. The class that is extended from the mixin class is called a mixee class. JavaFX classes can extend Java abstract classes and implement Java Interfaces. In the next chapter, you will learn more about one of the most important and powerful JavaFX features—data binding.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.17.230