atom feed19 messages in net.java.dev.openjfx-compiler.devMixin inheritance
FromSent OnAttachments
Brian GoetzMar 13, 2009 11:50 am 
Michael AzziMar 13, 2009 12:43 pm 
Brian GoetzMar 13, 2009 1:20 pm 
Michael AzziMar 13, 2009 1:41 pm 
Weiqi GaoMar 14, 2009 6:24 am 
Kim TopleyMar 14, 2009 7:40 am 
Robert FieldMar 15, 2009 12:53 pm 
Brian GoetzMar 16, 2009 11:18 am 
Brian GoetzMar 16, 2009 11:19 am 
Kim TopleyMar 16, 2009 12:21 pm 
Weiqi GaoMar 16, 2009 7:37 pm 
Brian GoetzMar 18, 2009 11:32 am 
Michael AzziMar 18, 2009 11:56 am 
Weiqi GaoMar 18, 2009 7:09 pm 
Brian GoetzMar 20, 2009 9:11 pm 
Brian GoetzMar 20, 2009 9:12 pm 
Brian GoetzMar 20, 2009 9:18 pm 
Weiqi GaoMay 3, 2009 7:31 pm 
Brian GoetzMay 3, 2009 7:52 pm 
Subject:Mixin inheritance
From:Brian Goetz (Bria@Sun.COM)
Date:Mar 13, 2009 11:50:01 am
List:net.java.dev.openjfx-compiler.dev

One of the features we're planning for Marina is removing multiple inheritance from the language and replacing it with mixin inheritance. This will simplify the language, eliminate many bugs, and make the generated code simpler, smaller, and faster, because all classes were burdened with the machinery of multiple inheritance even though few classes used them.

In reality, your code will not change very much. Most of the uses of MI that we've found are handed 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.

Here is a brief overview of how mixins will work.

*Mixin Inheritance

JavaFX supports a form of inheritance called mixin inheritance. Mixin inheritance is an intermediate point between Java's single inheritance with interfaces, and multiple inheritance. Early versions of JavaFX supported multiple inheritance, but this has been replaced with the more well-behaved mixin inheritance. Mixins can be thought of as a generalization of Java interfaces, which, in addition to method declarations, can contain default implementations for those methods, and can also contain var declarations.

Java classes can extend zero or one other Java classes, and any number of Java interfaces. Similarly, JavaFX classes can extend zero or one Java or JavaFX classes, any number of Java interfaces, and any number of JavaFX mixin classes. Mixin classes can extend any number of Java interfaces or JavaFX mixins. (A mixin that is extended by a class or other mixin is called a parent mixin; a class that extends a mixin is called the mixee.)

A mixin class is declared using the mixin keyword. The following example declares a mixin with a single function, hello(). Any (non-abstract) class extending SimpleMixin must provide a method called hello() that takes no arguments and returns a string.

mixin class SimpleMixin { public abstract function hello() : String; }

class A extends SimpleMixin { override function hello() : String { “Hello” } }

In the example above, SimpleMixin behaves exactly as a Java interface. However, mixins can also provide method bodies, which are considered default implementations.

mixin class MixinWithDefault { public abstract function hello() : String { “default” } }

class A extends MixinWithDefault { override function hello() : String { “Hello” } }

class B extends MixinWithDefault { }

In this example, class A provides its own version of hello(), but class B inherits the default implementation provided by the mixin.

**Instanceof and cast

Declaring a mixin class creates a named type (just as declaring an ordinary class does). The name of the mixin can be used in instanceof tests and casts.

**Instantiation

Mixin classes, like interfaces and abstract classes, are non-instantiable. It is a compilation error to use the name of a mixin class in an object literal or “new” expression.

*Function declarations in mixins

A mixin may declare any number of function declarations. Function declarations may or may not have function bodies. If the function declaration has a function body, it is treated as a default implementation for classes extending the mixin. If the function signature is override-compatible with a function signature declared in a parent mixin (directly or indirectly), the override keyword must be specified to indicate that it is overriding a method inherited from another mixin.

**Super keyword in mixin function bodies

The super keyword may appear in function bodies in mixin classes to access parent implementations of of functions. When referencing a function member super.f(...), f must be a method declared in a parent mixin, otherwise the use results in a compilation error. (See the section on “Precedence and member conflict resolution” for the case where the member name is ambiguous.)

**This keyword in mixin function bodies

The this keyword may appear in function bodies in mixin classes. Just as with ordinary classes, the this keyword refers to the object on which the method has been invoked. The static type of the this keyword in a mixin class is the type of the mixin.

*Variable declarations in mixins

A mixin may declare any number of variable declarations. A variable declaration includes the variable name, the variable type, and optionally a default value and/or “on replace” triggers.

Declaring a variable in a mixin is similar to declaring a function with a default implementation in a mixin. Declaring a variable in a mixin requires the mixee to have a variable of that name and type; if the mixee does not declare such a variable, the declaration is inherited from the mixin.

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 : Integer = 3; }

class C extends B { }

In this example, A does not provide its own declaration for the variable x, so the declaration and default value is inherited from M. B provides its own declaration and default value; C inherits the declaration and default value from B. Each have a variable called x, and this is the variable referred to by the default implementation of getX() in M.

**Overriding variable declarations

When inheriting a var from a mixin class, the mixee can use the override keyword to change the default value and/or add an on replace trigger. In the previous example, class B used override to set its own default for x.

**On replace triggers on mixin variables

A variable declaration in a mixin class can contain an “on replace” trigger. Just as with ordinary classes, this trigger is executed when the variable's value changes, even if a mixee has declared its own trigger on that variable with an override declaration.

*Init blocks in mixin classes

Mixin classes may contain “init” and “postInit” blocks. If a mixee inherits from a mixin with an init block, the mixin init is run after the superclass init, but before the mixee init. Init blocks from multiple parent mixins are run in the order they appear in the extends clause.

*Precedence and member conflict resolution

It is possible for the same member to be declared in multiple parent mixins, or in a mixin and a superclass. In such a case, the precedence of inheritance is that superclass members take precedence over mixin members, and a mixin declared earlier in the “extends” list takes precedence over mixins declared later in the extends list.

**Resolving initializer conflicts

If a variable declared in a mixin has a default value, and the variable is overriden in the mixee without a default value, the initial value specified in the mixin is used. If the variable is declared in a superclass, then the default value specified in the superclass is used; if no default value is specified in the superclass, the “default default value” for the type of that variable is used.

**Resolving super conflicts

In the event that a super.f() function reference is ambiguous (because the function was specified in multiple mixins or in a mixin and a superclass) the parent class must be disambiguated with the class name; the super keyword cannot be used in this case. Only the name of an immediate superclass or immediate parent mixin may be used in this manner; for example, given:

mixin M { public function foo() : String { “foo” } }

class B { public function foo() : String { “are” } }

class A extends B { public function foo() : String { “you” } }

class C extends B, M { public function moo() : String { B.foo() } }

This example is legal, as would M.foo(), but it would be illegal to use A.foo() in the body of moo() because A is not a direct superclass or parent
mixin.

**Unresolvable conflicts

If a variable declaration appears in a mixin and a non-private variable of the same name appears in the mixee or a superclass but with a different type, a compilation error results. If a variable declaration appears in multiple mixins with different types, a compilation error results. Similarly, a compilation error results if a function is declared in multiple mixins or a mixin and the mixee or superclass if the functions have the same name and are not override-compatible.

**Inheriting a mixin more than once

It is possible for a class to inherit from the same mixin twice:

mixin M { ... } class A extends M { } class B extends A, M { }

In this case, init blocks and triggers that appear in M are only executed once, and they are executed at the time of “first inheritance.” In the example above, M's init block would run before A's init block, and would not run again before B's init block.

Because of the precedence rules – superclass members take precedence over mixin members – mixing in the same mixin farther down the hierarchy (as in the case of B in this example) has no visible effects. B extends A, which already has all the members declared by M, so the process of mixing in M finds that there is nothing that needs to be mixed in.