<< §2.3 Lifting | ↑ Table of Contents ↑ | §2.5 Abstract Roles >> |
§2.4 Explicit role creation
Lifting is the normal technique by which role objects are created implicitly. This section defines under which conditions a role can also be created explicitly.
§2.4.1 Role creation via a lifting constructor
↑ §2.4
Lifting uses the default constructor for roles (see §2.3.1). This constructor can be invoked from client code, if the following rules are respected.
(a) Team context
The lifting constructor can be used only within the enclosing team of
the role to be instantiated. Thus, qualified allocation expressions
(someTeam.new SomeRole(..)
) may never use the lifting constructor.
(b) Fresh base object
If the argument to a lifting constructor invocation is a new
expression, creating a fresh base object, the use of the lifting constructor
is safe. Otherwise the rules of (c) below apply.
(c) Duplicate role runtime check
If it cannot be syntactically derived, that the argument to a lifting
constructor is a freshly created base object (b), a compile time warning will
signal that an additional runtime check is needed: It must be prevented that
a new role is created for a base object, which already has a role of the
required type in the given team. It is not possible to replace an existing
role by use of the lifting constructor. At runtime, any attempt to do so
will cause a org.objectteams.DuplicateRoleException
to be thrown.
This exception can only occur in situations where the mentioned compile
time warning had been issued.
§6.1 will introduce reflective functions
which can be used to manually prevent errors like a duplicate role.
§2.4.2 Role creation via a regular constructor
↑ §2.4
Roles may also be created explicitly using a custom constructor with arbitrary signature
other than the signature of the lifting constructor.
Within role constructors, four kinds of self-calls are possible:
base(..)
- A constructor of the corresponding base class (§A.5.3(c)), unless the role is involved in base class circularity (§2.1.2.(b)), in which case a base constructor call is illegal.
this(..)
- Another constructor of the same class.
super(..)
- A constructor of the super-class (normal
extends
), unless the super-class is bound to a different base class, in which case callingsuper(..)
is not legal. tsuper(..)
- A constructor of the corresponding role of the super-team (§A.5.4(e)). Also see the constraint in §1.3.2.(c).
(a) Unbound roles
Each constructor of a role that is not bound to a base class must use
one of this(..)
, super(..)
or tsuper(..)
.
(b) Bound roles
Each constructor of a bound role must directly or indirectly invoke either
a base(..)
constructor or a lifting constructor (see §2.3.1).
Indirect calls to the base constructor or lifting constructor may use any of this(..)
, super(..)
or tsuper(..)
, which simply delegates the obligation to the called constructor.
If a constructor referenced by base(..)
is not visible according to the
regular rules of Java, it may still be called using decapsulation (see
also §3.4, §2.1.2.(c)).
Note, that if the super or tsuper role is not bound, delegating the obligation to that unbound role will not work.
(c) Super-call for bound roles
Instead of or prior to calling base(..)
a constructor of a bound role explicitly or implicitly calls a super constructor.
Which constructor is applicable depends on the super role and its playedBy
clause.
- If the super role is bound to the same base class as the current role is,
- not writing a super-call causes the lifting constructor of the super role to be invoked.
- explicitly calling a super constructor requires the super constructor to either
- create a role instance using a base constructor call (directly or indirectly), or
- be a lifting constructor receiving a base instance, which the current role must provide as the argument.
- If the super role is bound but the current role refines the
playedBy
relationship (cf. §2.1.(c)),- a lifting constructor must be called explicitly passing a base object as the argument.
- If the role has an explicit or implicit super role which is unbound the constructor may optionally
call a super constructor (using
super(..)
ortsuper(..)
) prior to callingbase(..)
. Otherwise the default constructor is implicitly invoked.
When invoking a lifting constructor of a super role the base object can optionally be obtained by using a base constructor call as an expression:
super(base(<args>));
The language system evaluates the base constructor by creating an instance of the appropriate base class using a constructor with matching signature. Also the internal links are setup that are needed for accessing the base object from the role and for lifting the base object to the new role in the future.
The syntax for base constructors follows the rule that role implementations never directly refer to any names of base classes or their features.
§2.4.3 Role creation in the presence of smart lifting
↑ §2.4
Explicitly instantiating a role R1
bound to a base B
where smart lifting of B
to R1
would actually
provide a subrole R2
is dangerous: Instantiation enters the R1
into the team's internal cache. If at any time later lifting
this B
to R2
is requested, which is a legal request, the runtime system will answer by throwing a org.objectteams.WrongRoleException
because it finds the R1
instead of the required R2
.
For this reason, in this specific situation the explicit instantiation new R1(..)
will be flagged by a warning.
The problem can be avoided by using R2
in the instantiation expression.
Example code (WrongRoleException):
1 | public class B { void bm() {} } |
2 | public team class T { |
3 | protected class R1 playedBy B {...} |
4 | protected class R2 extends R1 { // inherits the binding to B |
5 | void rm() { /* body omitted */ } |
6 | } |
7 | public B getDecoratedB() { |
8 | return new R1(new B()); // compile-time warning! |
9 | } |
10 | public void requestLifting(B as R2 r) {} |
11 | } |
12 | // plus these calls: |
13 | T t = new T(); |
14 | B b = t.getDecoratedB(); // creates an R1 for b |
15 | t.requestLifting(b); // => |
<< §2.3 Lifting | ↑ Table of Contents ↑ | §2.5 Abstract Roles >> |
B
to the lifting constructor ofR1
(see §2.4.1.(b)). In order to return thisB
instance lowering is implicitly used for the return statement.b
toR2
is requested but due to line 8 anR1
is found in the internal cache.