Mixins in JavaFX 1.2 Technology

   
By Robert Eckstein, August 2009  

With the release of version 1.2 of JavaFX technology, developers have a new style of class inheritance: a mixin. A mixin is a type of class that can be inherited by a subclass, but it is not meant for instantiation. In this manner, mixins are similar to interfaces in the Java programming language. However, the difference is that mixins may actually define method bodies and class variables in addition to constants and method signatures.

Let's look at an example that compares Java technology with JavaFX technology. In the Java programming language, you can create an interface that mandates that any implementing class must define methods that match the stated signatures.

public interface MyInterface {
    public void myFunction(Object param1);
}
public class MyClass1 implements MyInterface {
    public String variable1 = "Hello";
    public String variable2 = "World";
    public void myFunction(Object param1) {
        System.out.println("Java says: " + variable1 + " " +
            variable2);
    }
}
public class MyClass2 implements MyInterface {
    public String variable1 = "Hello";
    public String variable2 = "World";
    public void myFunction(Object param1) {
        System.out.println("Java says: " + variable1 + " " +
            variable2);
    }
}
       

Note that in this example, the body of the myFunction() method is identical in MyClass1 and MyClass2. In addition, each class has two String variables that are initialized outside of the constructor.

Now compare a mixin that accomplishes something similar in JavaFX technology:

mixin class MyMixin {
    var variable1 = "Hello";
    var variable2 = "World";
    function myFunction():Void {
        println("JavaFX says: {variable1} {variable2}");
    }
}
class MyClass1 extends MyMixin { }
class MyClass2 extends MyMixin { }
       

The benefit with JavaFX technology is that if you have functions or variables that are duplicated across multiple classes, you can maintain them in a single file in JavaFX, as opposed to maintaining them in each class that implements the interface in Java technology.

Multiple Inheritance and the Diamond Problem

With version 1.2, JavaFX no longer directly supports multiple inheritance. Instead, the language incorporates various features that act as a middle ground, with the aim to offer many of the benefits of multiple inheritance while avoiding the implementation complexities. To demonstrate such a complexity, let's talk about one of the most common pitfalls of multiple inheritance: an ambiguity that language designers often call the diamond problem.

The textbook definition of the diamond problem often outlines two classes, ClassB and ClassC, that extend (or inherit from) ClassA. The definition also outlines ClassD, which extends both ClassB and ClassC. Figure 1 shows this relationship.

 
The Diamond Problem
Figure 1: The Diamond Problem
 
 

Note that ClassA has an abstract method called myMethod(), and that ClassB and ClassC provide two different implementations of that method. ClassD has the whichOne() method, which invokes the myFunction() method.

However, this brings to light an ambiguity: If the whichOne() method is called in ClassD, which version of myMethod() gets invoked: the version in ClassB or the version in ClassC?

The diamond problem is intentionally simplistic, but the ambiguities that such a scenario creates can demonstrate how problematic the implementation of multiple-inheritance language runtimes can become.

For this and other reasons, the designers of the JavaFX language decided to strip pure multiple inheritance out of version 1.2. According to the designers, removal of multiple inheritance would simplify the language, eliminate many bugs, and make the generated "code simpler, smaller, and faster, because all classes [had been] burdened with the machinery of multiple inheritance even though few classes used them. In reality, [JavaFX] code will not change very much. Most of the uses of multiple inheritance that we've found are handled perfectly well by mixins, and for those, simply adding the 'mixin' keyword in front of classes that are designed for multiple inheritance will do the trick."

Mixin Specifics

Now that you have an understanding of how mixins work, let's look at the specifics. The new inheritance rules for JavaFX technology are more formally stated in the official documentation:

  1. JavaFX classes are allowed to extend, at most, one other JavaFX class. Because the JavaFX Script programming language is based on the Java platform, this superclass also could be written in Java and your code would still compile and run correctly.
  2. JavaFX classes are allowed to extend any number of JavaFX mixin classes — this offers a degree of multiple inheritance in the JavaFX language. (If you are a Java programmer, know that JavaFX classes can also extend any number of Java interfaces as well.)
  3. JavaFX mixin classes are allowed to extend any number of other JavaFX mixin classes. As with #2 above, JavaFX mixin classes may also extend any number of Java interfaces. Note that "When a class includes a mixin, the class … includes, rather than inherits, all the mixin's attributes and methods. They become part of the class during compilation."

You should be aware of a few other rules, as initially provided by designer Brian Goetz in a posting on the JavaFX forums and expanded here.

As this article mentioned earlier, you cannot instantiate a mixin. In other words, you cannot do the following:

var m:MyMixin = new MyMixin();      //  
                 ILLEGAL. COMPILER ERROR.
       
              

In addition, you cannot instantiate a mixin using the alternative JavaFX object-literal form:

var m:MyMixin = MyMixin {           //  
                 ILLEGAL. COMPILER ERROR.
    variable1: "Hello Again"
    variable2: "World"
}
       
              

Instead, you can only instantiate a class that extends the mixin class.

var c:MyClass1 = new MyClass1();              //  CORRECT
       

Just like the abstract classes and interfaces in Java technology, the JavaFX compiler treats a mixin class as a named type. As such, it is valid to do both of the following:

var myClass1Instance = new MyClass1();

var m:
                 MyMixin = myClass1Instance;             //  CORRECT

if (myClass1Instance instanceof  
                 MyMixin) {    //  CORRECT
    println("true");
}
       
              

As this article mentioned earlier, a function declaration inside a mixin may have a body. Alternatively, it may be declared abstract if a subclass must provide an implementing function.

mixin class MyMixin {

    function myFunction1():Void {  // CORRECT: FUNCTION HAS BODY
        println("Hello World");
    }

     
                 abstract function myFunction2():Void; // NO BODY, IS ABSTRACT
}

class MyClass1 extends MyMixin {

    override function myFunction1():Void {
        //  CORRECT. (IF ABSENT, USES DEFAULT VERSION IN MyMixin)
    }

    override function myFunction2():Void {
        //   
                 REQUIRED BECAUSE THE MyMixin FUNCTION IS ABSTRACT

    }
}
       
              

Mixins may extend one ore more other mixins. Note that if a mixin function is itself inheriting from a parent mixin, the keyword override must be specified.

mixin class MyMixin1 {
    abstract function myFunction():Void;
}

mixin class MyMixin2 extends MyMixin1 {
     
                 override function myFunction():Void {
        // MUST USE OVERRIDE KEYWORD BECAUSE MyMixin2 EXTENDS MyMixin1
    }
}
       
              

You may have noticed that because mixins offer multiple inheritance, you can again encounter the diamond problem, but many of these issues can be resolved at compile-time. With JavaFX technology, you can get around this by qualifying the function that you intend to invoke. Consider the following example:

mixin class M {
    public function foo() : Void {
        println("M foo");
    }
}

class B {
    public function foo() : Void {
        println("B foo");
    }
}

class A extends B {
    override public function foo() : Void {
        println("A foo");
    }
}

class C extends B, M {
    public function myFunction() : Void {
        B.foo();   //  LEGAL
        M.foo();   //  LEGAL
        A.foo();   //   
                 ILLEGAL: Not direct superclass or parent
    }
}
       
              

Here, the myFunction() method in class C disambiguates the foo() method calls by specifying the parent that you intend to invoke. It would be legal to call both B.foo() and M.foo(). However, as you might expect, you can invoke only functions that are direct parents or superclasses. You can't invoke A.foo() because A is not a direct superclass or parent mixin.

The diamond problem specified earlier is a simplified version of a common multiple-inheritance issue. In practice, class hierarchies can consist of hundreds of classes with a wide variety of structures. As such, it is possible for the same variable or function to be declared in multiple parent mixins, or in a mixin and a superclass. If that occurs, the following rules apply for precedence in inheritance:

  • Superclass variables or functions take precedence over mixin variables or functions.
  • A mixin declared earlier in the extends list takes precedence over mixins declared later in the extends list.

Also, you can use the this keyword within a mixin function just as you do within abstract classes in the Java programming language. In this case, this refers to the live object on which the method has been invoked.

mixin class MyMixin {
    var variable1 = "Hello";
    var variable2 = "World";
    function myFunction():Void {
        println("This object is: {
                 this}");
    }
}
class MyClass1 extends MyMixin { }
class MyClass2 extends MyMixin { }

var m1:MyClass1 = new MyClass1();
m1.myFunction();   // Prints the MyClass1 object reference

var m2:MyClass2 = new MyClass2();
m2.myFunction();   // Prints the MyClass2 object reference
       
              

Variables in mixins are treated in much the same way as functions. As with JavaFX classes, a variable declaration includes the variable name, the variable type, and optionally a default value and on replace triggers. If the inheriting class does not override a mixin variable, it inherits that mixin variable and any default values that are defined. For example, consider the following example, provided by Brian Goetz:

mixin class M {
    public var x : Integer = 0;
    public function getX() : Integer { x }
}

class A extends M { }
class B extends M {
     
                 override public var x = 3;
}

class C extends B { }
       
              

In this example, classes A, B, and C each extend — directly or indirectly — mixin M. Class A does not provide its own declaration for the variable x, so the declaration and default value are inherited from mixin class M.

B overrides M with its own declaration and default value. Note that there is no need to specify the variable type again, because the compiler already knows that x is an Integer. If you specify the variable type at this point, the JavaFX compiler will flag it as an error.

C inherits the declaration and default value from B. Note that when inheriting a variable from a mixin class, the class can use the override keyword to either change the default value or add an on replace trigger, or both. In the previous example, class B used the override keyword to set its own default for x.

A variable declaration in a mixin class can contain an on replace trigger. Just as with ordinary classes, this trigger is executed when the value of the variable changes, even if an inheriting class or mixin has declared its own trigger on that variable with an override declaration. In that case, both triggers are executed. Consider this amended example:

mixin class M {
    public var x : Integer = 0 on replace {
         
                 println("on replace M");
    }

    public function getX() : Integer { x }
}

class A extends M { }
class B extends M {
    override public var x = 3 on replace {
         
                 println("on replace B");

    }
}

class C extends B { }

var c:C = new C();
c.x = 5;
       
              

When you execute this code, you will see the following output. This demonstrates that the triggers in both B and M are executed, first when the x variable is set to 3, and then again when it is reset to 5.

on replace M
on replace B
on replace M
on replace B
       

Conclusion

Mixins are an exciting new feature that are new to JavaFX in version 1.2. At this point, you should know how to take advantage of mixins within your JavaFX programs, and be able to author programs that master the complexities of multiple inheritance.

For More Information

JavaFX Home Page
JavaFX APIs
JavaFX Forum on forums.sun.com
Tutorial Lesson 9 of the Official JavaFX Documentation

Rate This Article

 
 

Discussion

We welcome your participation in our community. Please keep your comments civil and on point. You can optionally provide your email address to be notified of replies—your information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.