|
|
Table of |
§3. Callout binding |
| §2. Role binding |
Roles and base classes
![]() |
|
| playedBy relation |
A role can be bound to a class outside the team by a playedBy
relation, which declares that each role instances is associated to a
base instances.
|
![]() |
|
| Base class |
The class to which a role is bound (using playedBy) is called
its base class. Role instances may inherit and override
features from their base instance, which is declared using callout
(§3)
and callin (§4) method bindings.
|
![]() |
|
| Bound role |
Each role class that declares a playedBy relation
is called a bound role. The term bound role may also be
used for the instances of such a class.
|
![]() |
|
| Lifting / lowering | Translations between a role and its base are called lifting (base to role) (§2.3) and lowering (role to base) (§2.2). |
![]() |
|
| Translation polymorphism | Conformance between a role and a base is governed by translation polymorphism, which refers to a substitutability that is achieved using either lifting or lowering. |
![]() |
|
| Declared lifting | Generally, lifting happens implicitly at data flows between a role object and its base. Team level methods provide additional data flows, where lifting may be declared explicitly. |
![]() |
|
§2.1. playedBy relation
(a) Role-base bindingplayedBy keyword.
|
playedBy relation is inherited along
explicit and implicit (§1.3.1(c))
role inheritance.
extends)
can refine the playedBy relation to a more
specific base class (this is the basis for
smart lifting (§2.3.3)).If a role class inherits several
playedBy relations from
its super-class and its super-interfaces, there must be a most specific
base-class among these relations, which is conform to all other base-classes.
This most specific base-class is the base-class of the current role.
playedBy relation but never change an existing one.Note however, that implicit inheritance may implicitly specialize an existing
playedBy relation (this advanced situation is illustrated in §2.7(d)).
playedBy relation by itself has no effect
on the behavior of role and base objects.
It is, however, the precondition for translation polymorphism
(lowering: §2.2 and lifting: §2.3)
and for method bindings
(callout: §3 and callin:
§4).
Internally a team manages its roles and corresponding bases using weak references. When using one of the
getAllRoles(..)
methods (see §6.1(a)),
the result may be non-deterministic because these internal structures
may hold weak references to objects that will be collected by the next run of the
garbage collector. We advise clients of getAllRoles(..) to call
System.gc() prior to calling getAllRoles(..) in order
to ensure deterministic results.
§2.1.1. Binding interfaces
playedBy clause. Also the type mentioned after the
playedBy keyword may be an interface.
The language implementation as of OTDT version 1.0.X cannot yet bind a role class to a base interface, but this restriction will go in the future.
§2.1.2. Legal base classes
playedBy must be
visible in the enclosing scope (see below for an exception).
Normally, this scope is defined just by the enclosing team.
For role files (§1.2.5(b))
also additional imports in the role file are considered.§2.1.2(d) below defines how imports can be constrained so that certain types can be used as base types, only.
It is also not allowed to declare a role class of the same name as a base class bound to this or another role of the enclosing team, if that base class is given with its simple name and resolved using a regular import. Put differently, a base class mentioned after
playedBy
may not be shadowed by any role class of the enclosing team.
Base imports as defined below (§2.1.2(e)) relax this rule by allowing to import a class as a base class only. In that case no shadowing occurs since the scopes for base classes and roles are disjoint.
playedBy may not be
an enclosing type (at any depth) of the role class being defined.
This rule prohibits the creation of cycles where the base instance of a given role
R contains roles of the same type R.
More generally any sequence of classes
C1, C2, .. Cn
were each Ci+1 is either a member or the base class of Ci and Cn = C1 is forbidden.Conversely, it is also prohibited to bind a role class to its own inner class.
playedBy exists but is not visible under normal visibility rules of Java, this restriction may be overridden. This concept is called
decapsulation, i.e., the opposite of encapsulation (see also §3.4). A compiler should signal any occurrence of base class decapsulation. If a compiler supports to configure warnings this
may be used to let the user choose to (a) ignore base class decapsulation, (b) treat it as a warning or even (c) treat it
as an error.
Decapsulation is not allowed if the base class is a confined role (see §7.2).
Within the current role a decapsulated base class can be mentioned in the right-hand-side of any method binding (callout or callin). Also arguments in these positions are allowed to mention the decapsulated base class:
- the first argument of the role's constructor is (see lifting constructor §2.4.1).
- the base side of an argument with declared lifting (see declared lifting §2.3.2).
base can be applied to an import in order to specify that this type should be imported for application as a base type only. Example:
|
It is recommended that a type mentioned after the keyword
playedBy is always imported with the base modifier, otherwise the compiler will give a warning.Base imports create a scope that is disjoint from the normal scope. Thus, names that are imported as base will never clash with normally visible names (in contrast to §1.4). More specifically, it is not a problem to use a base class's name also for its role if a base import is used.
It is not possible to specify a parameterized type as a role's base class. If a class to be bound as base class is generic the raw type must be used instead.
Note:
The information from the playedBy declaration is used at run-time
to associate role instances to base instances.
Specifying a parameterized base class would imply that only such base instances
are decorated by a role whose type is conform to the specified parameterized class.
However, the type parameters are not available at run-time, thus the run-time environment
is not able to decide which base instances should have a role and which should not.
This is due to the design of generics in Java which are realized by erasure.
§2.2. Lowering
In other words: lowering translations are inserted by the compiler at all places in a program which would otherwise not be type correct and which using lowering are statically type correct. This may concern:
- the right hand side of an assignment wrt. the static type of the left hand side,
- the argument values of a method or constructor call wrt. the static type of the corresponding formal parameter,
- the return value of a method compared to the declared return type of the method.
- a role parameter in a callout binding (§3.3(d))
- or the return value in a callin binding (§4.5(d))
|
Effects:
- An instance of type
MyRoleis lowered to typeMyBasewhen -
- assigning it to
b(line 7) - passing it as argument to a method with formal parameter of type
MyBase(line 8) - assigning the return value to a variable of type
MyBase(line 9)
- assigning it to
- Note: The constructor call in line 6 uses the lifting constructor as defined in §2.4.1.
- reference comparison (using
==or!=) instanceofchecks- cast expressions
- return values in callout bindings §3.3(d))
- parameters in callin bindings (§4.5(d))
playedBy in the respective role class.
Object, lowering cannot be deduced automatically,
since a type could be interpreted both as a role type and a base type.
These cases may need explicit lowering.
For this purpose the role class must declare to implement the interface
ILowerable (from org.objectteams.Team).
This will cause the compiler to generate a method
|
Note, that each lowering translation will create a new array.
§2.3. Lifting
- §3.3(c) Callout bindings (result)
- §4.5(a,b) Callin bindings (parameters)
- §2.3.2 Declared lifting
playedBy clause
(either directly, or inherited (explicitly or implicitly)
from a super role), to which the given base type is conform.
extends). With translation polymorphism
it suffices that a value can be translated using lifting or lowering.
§2.3.1. Implicit role creation
- the given base object
- the given team object
- the statically required role type
playedBy).
By default the compiler generates such a constructor for each bound role.
On the other hand, default constructors that take no arguments
(as in JLS §8.8.7) are never generated for bound roles.
The super-constructor to be invoked by a default lifting constructor depends on whether the role's super class is a bound role or not.
- If the super-class is a bound role, the default lifting constructor will invoke the default lifting constructor of the super-class.
- If the super-class is not a bound role, the default lifting constructor will invoke the normal argumentless default constructor of the super-class.
If a bound role has an unbound super-class without an argumentless constructor, providing a custom lifting constructor is obligatory, because no legal default lifting constructor can be generated.
§2.3.2. Declared Lifting
(a) Parameters with declared liftingRoleClass) is a role of (playedBy) the first type
(BaseClass).
Furthermore, the role type must be a role
of the enclosing team class defining the given method. The role type
must be given by its simple (ie., unqualified) name.
Such a signature requires the caller to provide a base object (here
BaseClass), but
the callee receives a role object (here RoleClass).
In fact, the client sees a signature in which the "as RoleClass"
part is omitted.Compatibility between caller and callee sides is achieved by an implicitly inserted lifting translation. A signature using declared lifting is only valid, if the requested lifting is possible (see §2.3.3 and §2.3.4 for details).
super or tsuper in a method or constructor which
declares lifting for one or more parameters refers to a method
or constructor with role type parameters,
i.e., lifting takes place before super invocation. Nevertheless, the super method may also have a declared lifting signature.
BaseClass
and RoleClass.
RoleClass must be played by BaseException. Note, that RoleClass itself need not be a throwable. As the effect of this declaration the catch block will catch any exception of type BaseException and provides it wrapped with a RoleClass instance to the subsequent block.Also note, that re-throwing the given instance
param has the semantics of implicitly lowering the role to its base exception before throwing, because the role conforms to the
required type Throwable only via lowering.
Example code:
|
Effects:
- Clients use method
mwith a base instance (typeMyBase) as its argument (line 13). - Before executing the body of
m, the argument is lifted such that the method body receives the argument as of typeMyRole(line 8).
§2.3.3. Smart Lifting
extends), choosing the appropriate role class during
lifting involves the following rules:
B shall be lifted to a role class
R that is not bound to (playedBy)
B, but if a subclass of R
— say R2 —
is bound to B, lifting is statically setup to use
R2, the most general subclass of R that
is bound to B or one of its super-types.Restriction: This step is not applicable for parameter mappings of
replace
callin bindings (§4.5.d).
playedBy such that the role class is a
sub-class of the required (statically declared) role type
and the base class is a super-class of the
dynamic type of the base object.From those possible pairs the most specific base class is chosen. If multiple role classes are bound to this base class the most specific of these classes is chosen.
The analysis includes all roles and their bindings that are inherited from the super-team.
Complex Example:

| role class | base class |
| class R1 | |
| class R2 extends R1 playedBy B2 | class B2 |
| class R3 extends R2 /* inherited: playedBy B2 */ | class B3 extends B2 |
| class R4 extends R3 playedBy B4 | class B4 extends B3 |
| class R5 extends R4 /* inherited: playedBy B4 */ | |
| class B6 extends B4 | |
| class R7 extends R5 playedBy B7 | class B7 extends B6 |
- If declarations require lifting
B3toR1this is statically refined to useR2instead, because this is the most general class declaring a binding to a super–class ofB3. - If the dynamic base type in the same situation is
B6, three steps select the appropriate role:- By searching all
playedByclauses (including those that are inherited) the following role–base pairs are candidates:(R2,B2), (R3,B2), (R4,B4)and(R5,B4). - From these pairs the two containing the most specific base class
B4are chosen. - This makes
R4andR5role candidates, from which the most specificR5is finally chosen.
- By searching all
If the inheritance hierarchies of the involved base and role classes are given (like in the figure above) the smart lifting algorithm can be rephrased to the following "graphical" rule:
Starting with the dynamic base type (B6 in the example) move upwards the the inheritance relation until you reach a base class bound to a role class indicated by
a «playedBy» arrow pointing to the base class (B4). This role class must be conform to the requested role type. Switch to the role side along this arrow (R4). Now move downwards the role inheritance hierarchy as long as the subrole does not refine the playedBy relationship (indicated
by another «playedBy» arrow). The bottom role you reach this way (R5) is the role type selected by smart lifting.
|
||
§2.3.4. Binding ambiguities
R1 and R2
exist such that
R1andR2are played by the same base classB, andR1andR2have a common super roleR0, which is also bound to a base classB0, and- neither role class
R1norR2is a (indirect) sub-class of the other.
B is distinct from B0 it has to be a sub-class of B0.
| Effect: | |
In this case the compiler issues a warning, stating that the
B may not be liftable, because both
role classes R1 and R2 are candidates
and there is no reason to prefer one over the other.If no potential ambiguity is detected, lifting will always be unambiguous. |
B to the role type R0 is an illegal lifting request. If R0 is bound to the same base class B as its sub-roles R1 and R2 are, role R0 is unliftable, meaning that no instance of R0 can ever by obtained by lifting.
Example code:
|
- the situation of potential ambiguity according to (a) above is given and
- lifting is requested
(either by method binding or explicitly
(§2.3.2))
from the shared base class
Bto any role classR0that is a common super role forR1andR2.
| Effect: | |
| Definite ambiguity is a compile time error. |
Example code:
|
| Effect: | |
An actual ambiguity is reported at runtime by a
org.objectteams.LiftingFailedException.
|
Example code:
|
| Effect: | |
This is reported by a org.objectteams.WrongRoleException.
|
Example code:
|
playedBy or by inheriting this relation
from another role class).
I.e., two role classes that have no common bound super role will never cause
any ambiguity.
§2.4. Explicit role creation
§2.4.1. Role creation via a lifting constructor
someTeam.new SomeRole(..))
may never use the lifting constructor.
new
expression, creating a fresh base object, the use of the lifting constructor
is safe. Otherwise the rules of (c) below apply.
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
Within role constructors, four kinds of self-calls are possible:
base(..) |
||
| A constructor of the corresponding base class (§A.5.3(c)). | ||
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 calling super(..) 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). | ||
this(..), super(..) or tsuper(..).
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.
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
playedByrelationship (cf. §2.1(c)),- a lifting constructor must be called explicity 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.
§2.4.3. Role creation in the presence of smart-lifting
Explicitly instantiating a roleR1 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.
|
- A note on line 8: this line passes a fresh instance of
Bto the lifting constructor ofR1(see §2.4.1(b)). In order to return thisBinstance lowering is implicitly used for the return statement. - When line 15 is executed, a lifting of
btoR2is requested but due to line 8 anR1is found in the internal cache.
§2.5. Abstract Roles
abstract. Only those sub-teams are concrete
that provide concrete versions for all role classes used in
creation expressions.This includes the case, where a super-team has a concrete role class and creates instances of this role class and only the sub-team changes the status of this role class to abstract. Also here the sub-team must be marked abstract, because it contains an abstract role class that is used in creation expressions.
abstract if one of its
relevant roles is abstract.A role is relevant in this sense if
- the role class is public or if
- an explicit
newexpression would require to create instances of the role class, or if - any of the lifting methods of the enclosing team
would require to create instances of the role class.
A role is irrelevant with respect to lifting if either of the following holds:- It is not bound to a base class, neither directly nor
by an inherited
playedByclause. - It has a sub-role without a
playedByclause. - It is bound to an abstract base class, and for all concrete sub-classes of the base class, a binding to a more specific role class exists.
- It is not bound to a base class, neither directly nor
by an inherited
§2.6. Explicit base references
base exists, which can be used in the following
contexts:
T1.R1 is again a team
T2, roles of that team T2 can be
externalized (see §1.2.2)
using base as their type anchor. Given that
R2 is a role of T2, one could write:
|
- Note, that the example uses the new, preferred syntax of §9.2.1, whereas the old syntax would have been
base.R2.
T1.R1 which is bound to the team T2 containing role R2.
A static type prefix can be used to disambiguate a base anchor, so the explicit variant of the above type would be R2<@R1.base>.It is not legal to use a type anchor containing
base as an element in a path of references like <@base.field> or <@field.base>.
base(arguments) causes an instance
of the bound base class to be created and linked
(see §2.4.2).
base.m(args) is used to invoke the
originally called method
(see §4.3).
base when keywords. Within such a base guard
predicate base is interpreted as a special identifier
holding a reference to the base object that is about to be lifted
for the sake of a callin method interception
(see §5.4.2(a)).
base to refer to the bound base instance. Such usage requires the role method bound in this method binding to be non-static.
base reference is immutable,
i.e., base can never appear as the left-hand-side of an
assignment.
§2.7. Advanced structures
team modifier) it is a nested team. The depth of nesting is not restricted.
playedBy is a team, the role is said to be stacked on the base team.
Secondary are played by roles of another team Primary (i.e., base classes are roles), the team Secondary defines a layer over the team Primary.
Such layering requires a final reference anchor from Secondary to an instance of Primary. All playedBy declarations within Secondary specify their base classes anchored to that final link anchor.

Secondary are contained within the team instance specified by the link anchor. If roles of Secondary contain any callin bindings to non-static base methods, these will be triggered only when a base method is invoked on a base
instance contained in the team specified by anchor.In accordance with §2.6(a) the anchor in such anchored playedBy declarations could also be the pseudo identifier
base, provided that Secondary is a nested team, which has a playedBy binding to Primary as its base class.
This situation is part of the second example below (see T1 playedBy TB1).
playedBy relation.
This requires the base class to be specified relative to some implicit (OuterTeam.this) or explicit (OuterTeam.base) team anchor. Specializing that team anchor automatically specializes the playedBy declaration, too.
This rule never requires any action from a programmer but only explains the interpretation of a playedBy declaration in
complex situations.
|
![]() |
|
|
![]() |
|
|
Table of |
§3. Callout binding |



