ObjectTeams/Java Language Definition - §3.
 
 
 
§3. Callout binding

Notion of callout binding

callout binding A callout binding declares that a method call to a role object may be forwarded to a base method of the associated base object (the role object "calls out" to the base).
declarative completeness Even if a role class does not implement all needed methods, but forwards some to its base, also these methods must be declared within the role. Secondly, no forwarding occurs, unless explicitly declared by a callout binding.
expected/provided A callout binding binds an expected method of the role class (needed but not implemented here) to a provided method of the base class.
 

§3.1. Callout method binding

A role class may acquire the implementation for any of its (expected) methods by declaring a callout binding.
 (a) Prerequisite: Class binding
A callout binding requires the enclosing class to be a role class bound to a base class according to §2.1.
 (b) Definition
A callout binding maps an abstract role method ("expected method") to a concrete base method ("provided method"). It may appear within the role class at any place where feature declarations are allowed. It is denoted by
expected_method_designator "->" provided_method_designator;
The effect is that any call to the role method will be forwarded to the associated base object using the provided base method.

Example code:

1
 team class Company { 
2
    public class Employee playedBy Person { 
3
       abstract String getIdentification();	
4
       // callout binding see below...
5
    }
6
 }
 (c) Kinds of method designators
A method designator may either be a method name
4
 getIdentification -> getName; 

or a complete method signature including parameter declarations and return type declaration, but excluding any modifiers.
4
 String getIdentification() -> String getName(); 

Effects:

  • Line 4 declares a callout binding for the role method getIdentification(), providing an implementation for the abstract method defined in line 3.
  • In combination with the role binding in line 2 this has the following effect:
  • Any call to Emloyee.getIdentification is forwarded to the method Person.getName.
Both sides of a callout binding must use the same kind of designators, i.e., designators with and without signature may not be mixed.
Each method designator must uniquely select one method. If a method designator contains a signature this signature must match exactly with the signature of an existing method, i.e., no implicit conversions are applied for this matching. If overloading is involved, signatures must be used to disambiguate.
 (d) Inheritance of role method declarations
The role method being bound by a callout may be declared in the same class as the binding or it may be inherited from a super class or super interface.
 (e) Callout override
If an inherited role method is concrete, callout binding regarding this method must use the token "=>" instead of "->" in order to declare that this binding overrides an existing implementation.
Using the "=>" operator for an abstract method is an error.
It is also an error (and not useful anyway) to callout-bind a method that is implemented in the same class as the binding.
 (f) Inheritance of callout bindings
Callout bindings are inherited along explicit and implicit inheritance. Inherited callout bindings can be overridden using "=>".
 (g) Duplicate bindings
It is an error if a role class has multiple callout bindings for the same role method.
 (h) Declared Exceptions
It is an error if a base method to be bound by callout declares in its throws clause any exceptions that are not declared by the corresponding role method.
 (i) Shorthand definition
A callout binding whose method designators specify full method signatures does not require an existing role method. If no role method is found matching the expected method of such a callout binding, a new method is implicitly generated. The new method has default visibility and none of the possible modifiers for methods are set. The new method declares the same exceptions as the bound base method.

A callout binding either attaches an implementation to a previously declared method or adds (3.1(i) above) a forwarding method to a role class. Apart from this implementation, callout-bound methods do not differ from regular methods.

When we say, a callout binding defines forwarding this means that control is passed to the base object. In contrast, by a delegation semantics control would remain at the role object, such that self-calls would again be dispatched starting at the role. Callout bindings on their own do not support delegation. However, in conjunction with method overriding by means of callin bindings (see §4.) the effect of delegation can easily be achieved.
 

§3.2. Callout parameter mapping

 (a) with clause
If the method designators in a callout binding are signatures (not just method names), parameters and return value may be mapped by a with{...} sub-clause.
 (b) Mapping one parameter
For each parameter of the provided base method, exactly one parameter mapping defines, which value will actually be passed to the base method. Callout parameter mappings have this form:
expression->base_method_parameter_name
 (c) Result mapping
The return value of a callout method may be provided by a result mapping:
"result"  "<-" expression
The right hand side expression of a result mapping may use the special identifier result to refer to the value returned by the base method.
In a method binding with parameter mappings, it is an error to use result as the name of a regular method argument.

Example code:

1
 Integer absoluteValue(Integer integer) -> int abs(int i) with { 
2
    integer.intValue() -> i,
3
    result <- new Integer(result)
4
 }
 (d) Visible names
Each identifier that appears within the expressions of a parameter mapping must be either:
  • a feature visible in the scope of the role instance.
  • a parameter of the role method (for parameter mappings).
  • the special name result (for result mappings).
  • in a result mapping also the special name base can be used in order to refer to the bound base instance (provided the methods being bound are not static).
The names of base method arguments (i.e., names after mapping) are only legal in the position given in §3.2(b).
 (e) Implicit parameter mappings
If parameter mappings should be omitted the following conditions must hold:
  1. each method parameter of the role method must conform to the corresponding parameter of the base method, and
  2. the result type of the base method must conform to the result type of the role method.
Here conformance includes translation polymorphism (cf. §3.3(d)).
Parameter correspondence without parameter mapping is determined by declaration order not by names.
Two adjustments can, however, be performed implicitly:
  • If the role method has more parameters than the base method, unused trailing parameters may be silently ignored.
  • If the role method returns void, any result from the base method may be silently ignored.

Example code:

1
 public team class MyTeamA {
2
   public abstract class Role1 {
3
     abstract void payEuro(float euro);
4
     abstract float earnEuro();
5
     void idle(int seconds) { /* do nothing */ };
6
   }
7
   Role1 boss, worker = // initialization omitted
8
   public void transaction () {
9
      boss.payEuro(worker.earnEuro());
10
      boss.idle(123);
11
   }
12
 }
13
 public class Staff { // a base class 
											
14
   public void payDM (float dm) { … };
15
   public float earnDM () { … };
16
   public int doze() { … };
17
   // other methods omitted
18
 }
19
 public team class MySubTeam extends MyTeamA {
20
   public class Role1 playedBy Staff {
21
     void payEuro(float euro) -> void payDM(float dm) with {
22
       euro * 1.95338f -> dm
23
     }
24
     float earnEuro() -> float earnDM () with {
25
       result <- result / 1.95338f
26
     }
27
     idle => doze; // override existing implementation of idle()
28
   }
29
   void doit() {
30
      transaction();
31
   }
32
 }

Effects

  • Class MyTeamA is declaratively complete and can be type checked because it only uses methods that are visible or declared within this context. MyTeamA.Role1 can, however, not be instantiated, because it is abstract.
  • Line 30 has the normal effect of invoking transaction.
  • When executing transaction, the call of worker.earnEuro() is forwarded to the corresponding base object using method earnDM() (binding declaration in line 24). The result is converted by "result / 1.95338f" (line 25).
  • Within the same execution of transaction, the call of boss.payEuro() is forwarded to the corresponding base object using method payDM() (binding declaration in line 21). The parameter euro is converted by "euro * 1.95338f" (line 22).
  • Method idle is forwarded to doze without any parameter mapping. This requires doze to have a signature that is conformable to the signature of idle. In this case a role parameter and a base result are ignored.
    Using the => operator, this binding overrides the existing implementation of idle.
 

§3.3. Lifting and lowering

(For basic definitions see §2.2 and §2.3)
 (a) Call target translation
Invoking a base method due to a callout binding first lowers the role object in order to obtain the effective call target.
 (b) Parameter translation
Passing a role object as parameter to a callout method implicitly lowers this parameter, if the base method declares a corresponding base type parameter.
Lifting of callout parameters is not possible.
 (c) Result translation
When returning a base object from a callout method where the role method declares the result to be of a role class, this object is implicitly lifted to the appropriate role.
Lowering the result of a callout binding is not possible.
 (d) Typing rules
A parameter mapping (implicit by parameter position or explicit by a with clause) is well typed if the left hand side conforms to the right hand side, either by
  • type equality
  • implicit primitive type conversion
  • subtype polymorphism
  • translation polymorphism, here: lowering,
  • or by a combination of the above.
A result mapping (implicit or explicit by a with clause) is well typed, if the value at the right hand side conforms to the left hand side according to the rules given above, except that translation polymorphism here applies lifting instead of lowering.
 (e) Role arrays
For arrays of roles as parameters §2.2(e) applies accordingly. For arrays as a return value §2.3(d) applies.
 

§3.4. Overriding access restrictions

In contrast to normal access restrictions, method bindings may refer to hidden base methods. This concept is the inverse of encapsulation, hence it is called decapsulation.
Decapsulation may occur in these positions:
  • playedBy declaration (see §2.1.2(c))
  • base constructor call (see §2.4.2(b)).
  • callout bindings (see next)
  • callout to field (see §3.5(e))
  • basecall within a callin method (see §4.6)
 (a) Callout to inaccessible base method
By means of callout bindings it is possible to access methods of a base class regardless of their access modifiers. Method bindings are the only place in a program which may mention otherwise inaccessible methods. Access to the callout method at the role side is controlled by regular mechanisms, based on the declaration of the role method.
 (b) Sealing against decapsulation
A base package may be "sealed" which re-establishes the standard Java visibility rules.
Sealing is achieved by the corresponding capability of Jar files.
 (c) Warning levels
A compiler should signal any occurrence of 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 (cf. §2.1.2(c)).
Optionally, a batch compiler may support three levels of verbosity with respect to decapsulation:
-nodecapsulation No warnings.
default Warn only if/that access restrictions are overridden.
-decapsulation Detailed messages containing the binding and the hidden base method.
 (d) Private methods from super classes
If a callout binding shall bind to a private base method, that method must be defined in the exact base class to which the current role class is bound using playedBy. I.e., for private methods § 3.1(d) does not hold.
The same holds for private base fields (see below).
If a private base feature must indeed be callout-bound, a role class must be defined that is played by the exact base class defining the private feature. Another role bound to a sub-base-class can then be defined as a sub class of the first role. It will inherit the callout binding and through this it can access the desired feature.
1
public class SuperBase {
2
   private int secret;
3
}
4
public class SubBase extends SuperBase  { /* details omitted */ }
5
public team class MyTeam {
6
   protected class SuperRole playedBy SuperBase {
7
      int steal() -> get int secret; // OK
8
   }
9
   protected class SubRole extends SuperRole playedBy SubBase {
10
      int steal() -> get int secret;  // illegal!
11
   }
12
}
 

§3.5. Callout to field

Also fields of a base class can be made accessible using a callout binding.
 (a) Syntax
Using one of the callout modifiers get or set a role method can be bound to a field of the role's base class:
1
 getValue -> get value;
2
 setValue -> set value;
3
 int getValue() -> get int value;
where getValue, setValue are abstract role methods of appropriate signatures and value is a field of the bound base class.
A longer syntax is available, too (see line 3 above), which uses complete signatures. For the left hand side §3.1(c) applies, for the right hand side, this longer version prepends the field type to the field name.
 (b) Compatibility
A role method bound with the modifier get should have no arguments (it may have arbitrary arguments, which are silently ignored) and should have a return type to which the base field is compatible. A role method returning void will ignore the given value and thus has no effect at all, which will be signaled by a compiler warning.
A role method bound with the modifier set must have a first argument that is compatible to the base field's type (additional arguments - if present - are silently ignored) and must not declare a return type.
 (c) Value mapping
Values can be mapped similar to parameter mappings in pure method bindings (§3.2). Such mappings can be used to establish compatibility as required above.
In both get and set bindings, the base side value is denoted by the field's name (lines 2 and 4 below).
1
 Integer getValue()           -> get int val
2
    with { result             <-    new Integer(val) }
3
 void setValue(Integer i)     -> set int val
4
    with { i.intValue()       ->    val }

 (d) Effect
Callout-binding a role method to a base field generates an implementation for this role method, by which it acts as a getter or setter for the given field of the associated base object.
 (e) Access control
For accessing an otherwise invisible field, the rules for decapsulation (§3.4) apply accordingly.
Recall, that according to JLS §8.3 fields may be hidden in sub-classes of a given base class. Therefore, it is relevant to know that a callout to a field will always access the field that is visible in the exact base class of the role class defining the callout. This is especially relevant for accessing private fields.
 (f) Shorthand definition
Just as in §3.1(i) a shorthand definition allows to introduce a callout field access method without prior abstract declaration. This requires the callout field binding to specify types as in line 3 of (a) "Syntax" above.
 (g) Callout override
Similar to method callouts a callout to field may override an existing role method if and only if the token => is used instead of -> (see §3.1(e) and §3.1(f)).
© Stephan Herrmann, Christine Hundt
OT/J Version 1.0 — Last modified: Fri Mar 30 2007