<< §5 Team Activation | ↑ Table of Contents ↑ | §7 Role Encapsulation >> |
§6 Object Teams API
The role of predefined types and methods
§6.1 Reflection↑ §6
Object Teams supports reflection with respect to teams, roles, and role-base relationships.
(a) Interface to the role registry
Each team instance internally has a registry of known role objects indexed by their base object.
Programmers may make use of this registry using the following reflective methods defined in
org.objectteams.ITeam
:
boolean hasRole ( Object aBase ) ;
- This method checks whether a role for the passed base object already exists in the target team.
boolean hasRole ( Object aBase, Class roleType ) ;
- This method checks whether a instance of type
roleType
as a role for the passed base objectaBase
already exists in the target team. The role may also be of any subtype of the specified role type.
IfroleType
is not a member type of the current team anIllegalArgumentException
is thrown. Object getRole ( Object aBase ) ;
- If the passed base object
aBase
already has a role in the target team, this role is returned. Otherwisenull
is returned. <T> T getRole ( Object aBase, Class<T> roleType ) ;
- If the passed base object
aBase
already has a role in the target team that is assignable to the type represented byroleType
, this role is returned. Otherwisenull
is returned.
IfroleType
is not a member type of the current team anIllegalArgumentException
is thrown. Object[] getAllRoles () ;
- Retrieves all existing (registered) bound roles (§2.1.(a)) in the target team.
This method uses internal structures of weak references. For that reason it may return role instances which were about to be reclaimed by the garbage collector. If performance permits, it is thus advisable to always callSystem.gc()
prior to callinggetAllRoles()
in order to achieve deterministic results (see also §2.1.(f)). <T> T[] getAllRoles ( Class<T> roleType ) ;
- Retrieves all existing (registered) bound roles (§2.1.(a)) in the target team that are assignable to the type represented by
roleType
.
IfroleType
is not a member type of the current team anIllegalArgumentException
is thrown.
See the note about garbage collection above. void unregisterRole ( Object aRole ) ;
- This method unregisters the passed role object from the target team. Thus the corresponding base looses this role. After calling this method the role should no longer be used.
void unregisterRole ( Object aRole, Class roleType ) ;
- This method unregisters the passed role object from the target team. Thus the corresponding base loses this role.
After calling this method the role should no longer be used.
The only difference to the previous method is improved speed because no search for the corresponding registry
has to be performed.
IfroleType
is not a member type of the current team anIllegalArgumentException
is thrown.
It is desirable and possible to use these methods within guards (see §5.4). These methods allow to write the specification of guards in a more concise and more expressive way. Determined by the signature, the first four methods can only be used in a base-level guard (§5.4.2) because they require a reference to a base object.
Example code (Guards and Reflection):
1 | public team class SpecialConditions { |
2 | public void participate(Account as BonusAccount ba) {} |
3 | public class BonusAccount playedBy Account |
4 | base when(SpecialConditions.this.hasRole(base, BonusAccount.class)) |
5 | { |
6 | callin void creditBonus(int amount) { |
7 | base.creditBonus(amount + bonus); |
8 | } |
9 | void creditBonus(int amount) <- replace void credit(int i) |
10 | base when (i > 1000); |
11 | } |
12 | } |
(b) Behavioral reflection
The following reflective methods defined in org.objectteams.ITeam can be used to inspect the dynamic behavior of a team:
boolean isExecutingCallin () ;
- This method is used to inspect whether a control flow has already been intercepted by at least one callin binding of the current team. It can be used to avoid undesirable re-entrance to a team.
boolean isActive () ;
- This method checks whether the team instance is active for the current thread.
boolean isActive ( Thread aThread ) ;
- This method checks whether the team instance is active for the thread
aThread
.
(c) Class literals for roles
The Java syntax for so-called class literals, MyClass.class
(see JLS §15.8.2)
can be used for role types with slightly changed semantics: Role types are virtual types (§1.3.1)
that are bound dynamically (§1.3.1.(e)). This applies to role class literals, too.
From this follows the constraint that a role class literal can only be used within the non-static context of a team,
ie., for evaluating a role class literal an enclosing team instance must be in scope.
Unlike regular type checking for role types, the class literal itself does not have a dependent type.
Thus type checking of calls to methods like hasRole(Object, Class)
cannot detect, whether the Class
instance
has actually been obtained from the correct team instance. Any attempt to pass a class that is not known
as a bound role within the given team results in an IllegalArgumentException
at run-time.
§6.2 Other API Elements↑ §6
(a) Interfaces for role encapsulation
A set of pre-defined types exist that do not extend java.lang.Object
and have no features except the operators ==
and !=
.
Note:
The JLS defines that each interface declares all methods defined injava.lang.Object
(JLS §9.2)
and also each object referenced by an interface type can be widened to java.lang.Object
.
Compilers commonly implement this by declaring java.lang.Object
the super-type of all interfaces.
Such implementation has no visible difference with respect to the more complex definition in the JLS.
These predefined types are
org.objectteams.IConfined
- regular interface
org.objectteams.ITeam.IConfined
- role interface
org.objectteams.Team.Confined
- role class
These types provide no new functionality but inheriting from these types influences the semantics with respect to encapsulation. The purpose and usage of these types is described in §7.
(b) Interface for explicit lowering
The following role interface exists for the purpose of allowing explicit lowering:
org.objectteams.ITeam.ILowerable
- role interface
This interface was introduced in detail in §2.2.(d).
(c) Team activation methods
Every team can be activated and deactivated by predefined methods of the interface org.objectteams.ITeam
.
activate()
andactivate(Thread th)
- Methods for activation of a team
deactivate()
anddeactivate(Thread th)
- Methods for deactivation of a team
The usage of these Methods is described in §5.2.(b).
(d) Exceptions
The following Exceptions
can be thrown during the execution of an ObjectTeam/Java program:
ResultNotProvidedException
- Thrown if a replace callin without a base call does not provide the necessary (primitive type) base result (see §4.3.(e)).
LiftingFailedException
- Thrown if an actual ambiguity occurs during lifting (see §2.3.4.(c)) or if lifting would need to instantiate an abstract role class (see §2.5.(b)). This is a checked exception. See §2.3.5 for more information.
WrongRoleException
- Thrown during lifting if the base object has, with respect to the same team instance, previously been lifted to a role type that is not conform to the currently requested type (see §2.3.4.(d) and §2.4.3).
DuplicateRoleException
- Thrown during explicit role creation, if a new role is created for a base object, which already has a role of the required type in the given team (see §2.4.1.(c)).
RoleCastException
- Thrown during cast of an externalized role, if the casted expression is anchored to a different team instance than the cast type (see §1.2.4.(b)).
LiftingVetoException
- This exception is used internally to abort the process of lifting when a relevant guard predicate (§5.4) evaluated to false.
Such exceptions thrown from generated code will never appear in client code, so there is usually no need to catch a
LiftingVetoException
. However, in some situations it is useful to explicitly throw aLiftingVetoException
from a lifting constructor (§2.3.1.(b)) of a role. This style allows to abort lifting even after the lifting constructor has started to work and also for method parameters requiring lifting. If lifting was triggered due to a callin method binding, this binding will simply not trigger if aLiftingVetoException
is thrown while preparing the call to the role method.
(e) Role migration
The following interfaces can be used to enable role migration:
IBaseMigratable
- This interface declares a method
and instructs the compiler to generate an implementation of this method for any bound role declaring
<B> void migrateToBase(B otherBase)
IBaseMigratable
as its super-interface.
The effect of callingmigrateToBase
on a role instance is to re-bind this role to a new base instance. The base instance must be compatible to the role's base class (in order to avoid problems during lifting the compiler may require the base to be of the exact type of the role's base class). Passingnull
to this method causes anNullPointerException
to be thrown. ITeamMigratable
- This interface declares a method
and instructs the compiler to generate an implementation of this method for any role declaring
<R> R<@otherTeam> migrateToTeam(final ITeam otherTeam)
ITeamMigratable
as its super-interface.
The effect of callingmigrateToTeam
on a role instance is to re-bind this role to become a contained part of a new team instance. The team instance must be of the exact type of the role's enclosing team. Passingnull
to this method causes aNullPointerException
to be thrown.Caveat:
This method intentionally breaks the rules of family polymorphism: any referenceR<@previousTeam> r
which was established before migration will incorrectly imply that the role's enclosing team still ispreviousTeam
, which is no longer true after migration. While this does not effect any method lookup (which is still safe), further assumptions based on a role's dependent type are invalidated by team migration. The same holds for references from the migrating role to any sibling role instances.
If the rules of family polymorphism should be maintained one should just refrain from declaringITeamMigratable
as a role's super-interface.
For both methods the signature declared in the interface is over-generalized, yet the compiler performs the necessary checks
to
ensure that role, base and team instances are indeed compatible and additionally the return type of migrateToTeam
is checked as a self-type, i.e., it reflects the exact type of the call target.
§6.3 Annotations↑ §6
(a) Controlling implicit team activation
Implicit team activation is disabled by default and can be enabled by adding the annotation
@org.objectteams.ImplicitTeamActivation
.
See §5.3.(d) for details.
(b) Controlling lifting
If lifting as defined in §2.3 and specifically §2.3.1 causes
performance problems, the semantics of lifting can be modified per role class using the annotation
@org.objectteams.Instantiation
.
See §2.3.1.(d) for details.
<< §5 Team Activation | ↑ Table of Contents ↑ | §7 Role Encapsulation >> |
Effects:
This teams provides a bonus system for registeredAccount
s. Every time an amount of more than 1000 is deposited to a registered account, additional 1% of the amount is credited.participate
in line 2 uses declared lifting (see §2.3.2) to allow the passedAccount
object to participate the bonus system provided by theSpecialConditions
team.hasRole
to check whether the base object already has a role of typeBonusAccount
in the surrounding team. The expressionBonusAccount.class
returns thejava.lang.Class
object representing the roleBonusAccount
(see JLS §15.8.2). This guard ensures, that only accounts explicitly registered viaparticipate
are ever decorated with a role of typeBonusAccount
.creditBonus
to calls where the base method argumentamount
is greater than 1000.