Latest News

§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↑ §3

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. However, callout bindings are not allowed if the role is involved in base class circularity (see §2.1.2.(b)).

(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 (Callout):
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 and declared exceptions.

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 Employee.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 is static iff the bound base method is static, and it declares the same exceptions as the bound base method.

A shorthand callout may optionally declare a visibility modifier, otherwise the generated method inherits the visibility modifier of the bound base method. No further modifiers are set. If a callout overrides an inherited method or callout, it must not reduce the visibility of the inherited method/callout.

(j) Inferred callout

If a non-abstract role class inherits an abstract method the compiler tries to infer a callout binding for implementing the abstract method. Similarly, if a self-call in a role class cannot be resolved, the compiler tries to infer a callout to resolve the self-call.
Inference searches for a method in the bound base class such that

  1. both methods have the same name
  2. both methods have the same number of arguments
  3. each argument of the abstract role method is compatible to the corresponding argument of the base method directly, or using boxing/unboxing or lowering.

Callouts inferred from an interface have public visibility, callouts inferred from a self-call have private visibility.

Per default inferred callout bindings are disabled, i.e., a compiler must report these as an error. However, a compiler should allow to configure reporting to produce a warning only (which can be suppressed using a @SuppressWarnings("inferredcallout") annotation), or to completely ignore the diagnostic.

(k) Callout to generic method

When referring to a generic base method

1
<T> T bm(T a)

a callout binding may either propagate the method's genericity as in

2
<T> T rm(T a) -> T bm(T a);

or it may supply a valid substitution for the type parameter as in

2
String rm(String a) -> String bm(String a);

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↑ §3

(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. Parameter mappings may only occur if the enclosing role is a class, not an interface.

(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 (Callout Parameter Mapping):
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 method being bound is 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 (Callout with Parameter Mapping):
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.95583f -> dm
23
    }
24
    float earnEuro() -> float earnDM () with {
25
      result <- result / 1.95583f
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.95583f" (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.95583f" (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↑ §3

(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↑ §3

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))
  • base call 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↑ §3

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 §3.5.(a) above. The generated access method is static iff the bound base field is static.

A shorthand callout to field may optionally declare a visibility modifier, otherwise the generated method inherits the visibility modifier of the bound base field. No further modifiers are set. If a callout to field overrides an inherited method or callout, it must not reduce the visibility of the inherited method/callout.

(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)).

(h) Inferred callout

If a statement or expression within the body of a bound role class uses a simple name or a name qualified by this which can not be resolved using normal rules, the compiler may infer to use a callout to field instead, given that a field of the required name can be found in the role's declared baseclass.

If a callout to field has explicitly been declared it is used for the otherwise unresolved name, if and only if:

  • the callout declares a role method name the is constructed from the token "set" for a setter or "get" for a getter plus the field name with capital first letter,
  • the base field referenced by the callout has exactly the required name, and
  • the callout kind (set/get) matches the application of the unresolved name as either the left-hand side of an assignment (set) or as an expression (get).

If a matching callout to field is not found, the compiler generates one automatically, which has private visibility.

If a callout to field has been inferred it is an error to directly invoke the implicitly generated callout accessor that is formed according to the above rules.

Per default inferred callout bindings are disabled, i.e., a compiler must report these as an error. However, a compiler should allow to configure reporting to produce a warning only (which can be suppressed using a @SuppressWarnings("inferredcallout") annotation), or to completely ignore the diagnostic. See also §3.1.(j).