Latest News

§1 Teams and Roles

Fundamental concepts of Teams

Teams and Roles
Classes that are defined with the modifier team are called team classes, or teams for short.
Direct inner classes of a team are called role classes, or roles for short.
Role inheritance
Inheritance between teams introduces a special inheritance relationship between their contained roles. The rules of this implicit inheritance are given below (§1.3.1).
Externalized role
Roles are generally confined to the context of their enclosing team instance. Subject to specific restrictions, a role may be passed outside its team using the concept of externalized roles (§1.2.2).

§1.1 Team classes↑ §1

A class declared with the modifier team is a team class (or team for short).

1
public team class MyTeamA {
2
  ...
3
}

Teams are meant as containers for roles, which are defined in the following paragraphs.

1
public team class MyTeamA {
2
  public class MyRole
3
    ...
4
  }
5
}

Teams introduce a new variant of inheritance for contained role classes (see §1.3.1 below). Other properties of teams, which are defined in later sections, are:

  • Team activation (§5)
  • Abstractness and instantiation (§2.5)
  • Declared lifting in team methods (§2.3.2)
  • Reflective functions defined in org.objectteams.ITeam (§6.1)

Apart from these differences, team classes are regular Java classes with methods and fields, whose instances are regular Java objects.

§1.2 Role classes and objects↑ §1

Each direct inner class of a team is a role class. Just like inner classes, each instance of a role class has an implicit reference to its enclosing team instance. This reference is immutable. Within the implementation of a role it can be accessed by qualifying the identifier this with the name of the team class, as in:

1
public team class MyTeamA {
2
  public class MyRole {
3
    public void print() { System.out.println("Team: "+ MyTeamA.this); }
4
  }
5
}

Creation of role instances is further restricted as defined in §2.4. Teams can also define role interfaces just like role classes. With respect to role specific properties a role interface is treated like a fully abstract class.

§1.2.1 Modifiers for roles↑ §1.2

Member classes of a team cannot be static. Also the use of access modifiers for roles is restricted and modifiers have different (stronger) semantics than for regular classes (see below). With respect to accessibility a team acts mainly like a package regarding its roles.

(a) Role class protection

A role class must have exactly one of the access modifiers public or protected.
This rule does not affect the class modifiers abstract, final and strictfp.

(b) protected role classes

A protected role can only be accessed from within the enclosing team or any of its sub-teams. The actual border of encapsulation is the enclosing team instance. The rules for protected roles are given in §1.2.3 below.

(c) public role classes

Only public roles can ever be accessed outside their enclosing team. Accessing a role outside the enclosing team instance is governed by the rules of externalized roles, to be defined next (§1.2.2).

(d) abstract role classes

A role class has to be marked abstract if any of its methods is not effective.
The methods of a role class comprise direct methods and methods acquired by inheritance. In addition to regular inheritance a role class may acquire methods also via implicit inheritance (§1.3.1).
A method may become effective by either:

  • implementation (i.e., a regular method body), or
  • a callout binding (see §3).

§2.5 discusses under which circumstances abstract roles force the enclosing team to be abstract, too.

(e) Role features

Access modifiers for members of roles have some special interpretation:

  1. A private member is also visible in any implicit sub role (see implicit inheritance §1.3.1.(c)).
    In contrast to inner classes in Java, private members of a role are not visible to the enclosing team.
  2. The default visibility of role members restricts access to the current class and its sub-classes (explicit and implicit).
  3. protected role members can only be accessed from the enclosing team or via callin (§4).
  4. public role members grant unrestricted access.

Additionally, a role always has access to all the features that its enclosing team has access to.

Only public members can ever be accessed via an externalized role (§1.2.2).

(f) Static role methods

In contrast to inner classes in pure Java, a role class may indeed define static methods. A static role method requires no role instance but it still requires a team instance in scope. Static role methods can be called:

  • from the enclosing team,
  • via callin (see §4.7).

Within a static role method the syntax MyTeam.this is available for accessing the enclosing team instance.

(g) No static initializers

A static field of a role class must not have a non-constant initialization expression. Static initialization blocks are already prohibited for inner classes by Java (see JLS §8.1.2).

Note:
Static initialization generally provides a means for performing initialization code prior to instantiation, i.e., at class-loading time. Before any role can be created already two levels of initialization are performed: (1) The (outer most) enclosing team class performs static initializations when it is loaded. (2) Any enclosing team executes its constructor when it is instantiated. It should be possible to allocate any early initialization to either of these two phases instead of using static role initializers.

§1.2.2 Externalized roles↑ §1.2

Normally, a team encapsulates its role against unwanted access from the outside. If roles are visible outside their enclosing team instance we speak of externalized roles.

Externalized roles are subject to specific typing rules in order to ensure, that role instances from different team instances cannot be mixed in inconsistent ways. In the presence of implicit inheritance (§1.3.1) inconsistencies could otherwise occur, which lead to typing errors that could only be detected at run-time. Externalized roles use the theory of "virtual classes" [1], or more specifically "family polymorphism" [2], in order to achieve the desired type safety. These theories use special forms of dependent types. Externalized roles have types that depend on a team instance.

§1.2.3 deduces even stronger forms of encapsulation from the rules about externalized roles.

(a) Visibility

Only instances of a public role class can ever be externalized.

(b) Declaration with anchored type

Outside a team role types are legal only if denoted relative to an existing team instance (further on called "anchored types"). The syntax is:

final MyTeam myTeam = expression;
RoleClass<@myTeam> role = expression;

The syntax Type<@anchor> is a special case of a parameterized type, more specifically a value dependent type (§9). The type argument (i.e., the expression after the at-sign) can be a simple name or a path. It must refer to an instance of a team class. The role type is said to be anchored to this team instance.
The type-part of this syntax (in front of the angle brackets) must be the simple name of a role type directly contained in the given team (including roles that are acquired by implicit inheritance).

Note:
Previous versions of the OTJLD used a different syntax for anchored types, where the role type was prefixed with the anchor expression, separated by a dot (anchor.Type, see §A.6.3). A compiler may still support that path syntax but it should be flagged as being deprecated.

(c) Immutable anchor

Anchoring the type of an externalized role to a team instance requires the team to be referenced by a variable which is marked final (i.e., immutable). The type anchor can be a path v.f1.f2... where v is any final variable and f1 ... are final fields.

(d) Implicit type anchors

The current team instance can be used as a default anchor for role types:

  1. In non-static team level methods role types are by default interpreted as anchored to this (referring to the team instance). I.e., the following two declarations express the same:
    public RoleX getRoleX (RoleY r) {  stmts  }
    public RoleX<@this> getRoleX (RoleY<@this> r) {  stmts  }
  2. In analogy, role methods use the enclosing team instance as the default anchor for any role types.

Note, that this and Outer.this are always final.
The compiler uses the pseudo identifier tthis to denote such implicit type anchors in error messages.

(e) Conformance

Conformance between two types RoleX<@teamA> and RoleY<@teamB> not only requires the role types to be compatible, but also the team instances to be provably the same object. The compiler must be able to statically analyze anchor identity.

(f) Substitutions for type anchors

Only two substitutions are considered for determining team identity:

  1. For type checking the application of team methods, this is substituted by the actual call target. For role methods a reference of the form Outer.this is substituted by the enclosing instance of the call target.
  2. Assignments from a final identifier to another final identifier are transitively followed, i.e., if t1, t2 are final, after an assignment t1=t2 the types R<@t1> and R<@t2> are considered identical. Otherwise R<@t1> and R<@t2> are incommensurable.
    Attaching an actual parameter to a formal parameter in a method call is also considered as an assignment with respect to this rule.

(g) Legal contexts

Anchored types for externalized roles may be used in the following contexts:

  1. Declaration of an attribute
  2. Declaration of a local variable
  3. Declaration of a parameter or result type of a method or constructor
  4. In the playedBy clause of a role class (see §2.1).

It is not legal to inherit from an anchored type, since this would require membership of the referenced team instance, which can only be achieved by class nesting.

Note:
Item 4. — within the given restriction — admits the case where the same class is a role of one team and the base class for the role of another team. Another form of nesting is defined in §1.5.

(h) Externalized creation

A role can be created as externalized using either of these equivalent forms:

outer.new Role()
new Role<@outer>()

This requires the enclosing instance outer to be declared final. The expression has the type Role<@outer> following the rules of externalized roles.
The type Role in this expression must be a simple (unqualified) name.

(i) No import

It is neither useful nor legal to import a role type.

Rationale:
Importing a type allows to use the unqualified name in situations that would otherwise require to use the fully qualified name, i.e., the type prefixed with its containing package and enclosing class. Roles, however are contained in a team instance. Outside their team, role types can only be accessed using an anchored type which uses a team instance to qualify the role type. Relative to this team anchor, roles are always denoted using their simple name, which makes importing roles useless.

A static import for a constant declared in a role is, however, legal.

Example code (Externalized Roles):
1
team class FlightBonus extends Bonus {
2
  public class Subscriber {
3
    void clearCredits() { ... }
4
  }
5
  void unsubscribe(Subscriber subscr) { ... }
6
}
7
class ClearAction extends Action {
8
  final FlightBonus context;
9
  Subscriber<@context> subscriber;
10
  ClearAction (final FlightBonus bonus, Subscriber<@bonus> subscr) {
11
    context = bonus; // unique assignment to 'context'
12
    subscriber = subscr;
13
  }
14
  void actionPerformed () {
15
    subscriber.clearCredits();
16
  }
17
  protected void finalize () {
18
    context.unsubscribe(subscriber);
19
  }
20
}
Effects:
  • Lines 1-6 show a terse extract of a published example [NODe02]. Here passengers can be subscribers in a flight bonus program.
  • Lines 7-20 show a sub-class of Action which is used to associate the action of resetting a subscriber's credits to a button or similar element in an application's GUI.
  • Attribute context (line 8) and parameter bonus (line 10) serve as anchor for the type of externalized roles.
  • Attribute subscriber (line 9) and parameter subscr (line 10) store a Subscriber role outside the FlightBonus team.
  • In order to type-check the assignment in line 12, the compiler has to ensure that the types of LHS and RHS are anchored to the same team instance. This can be verified by checking that both anchors are indeed final and prior to the role assignment a team assignment has taken place (line 11).
    Note, that the Java rules for definite assignments to final variables ensure that exactly one assignment to a variable occurs prior to its use as type anchor. No further checks are needed.
  • It is now legal to store this role reference and use it at some later point in time, e.g., for invoking method clearCredits (line 15). This method call is also an example for implicit team activation (§5.3.(b)).
  • Line 18 demonstrates how an externalized role can be passed to a team level method. The signature of unsubscribe is for this call expanded to
    void unsubscribe(Subscriber<@context> subscr)
    (by substituting the call target context for this). This proves identical types for actual and formal parameters.

§1.2.3 Protected roles↑ §1.2

Roles can only be public or protected. A protected role is encapsulated by its enclosing team instance. This is enforced by these rules:

(a) Importing role classes

This rule is superseded by §1.2.2.(i)

(b) Qualified role types

The name of a protected role class may never be used qualified, neither prefixed by its enclosing type nor parameterized by a variable as type anchor (cf. §1.2.2.(a)).

(c) Mixing qualified and unqualified types

An externalized role type is never compatible to an unqualified role type, except for the substitutions in §1.2.2.(f), where an explicit anchor can be matched with the implicit anchor this.

Rules (a) and (b) ensure that the name of a protected role class cannot be used outside the lexical scope of its enclosing team. Rule (c) ensures that team methods containing unqualified role types in their signature cannot be invoked on a team other than the current team. Accordingly, for role methods the team context must be the enclosing team instance.

(d) Levels of encapsulation

Since protected role types can not be used for externalization, instances of these types are already quite effectively encapsulated by their enclosing team. Based on this concept, encapsulation for protected roles can be made even stricter by the rules of role confinement. On the contrary, even protected roles can be externalized as opaque roles which still expose (almost) no information. Confinement and opaque roles are subject of §7.

§1.2.4 Type tests and casts↑ §1.2

In accordance with §1.2.2.(e), in OT/J the instanceof operator and type casts have extended semantics for roles.

(a) instanceof

For role types the instanceof operator yields true only if both components of the type match: the dynamic role type must be compatible to the given static type, and also type anchors must be the same instance.

(b) Casting

Casts may also fail if the casted expression is anchored to a different team instance than the cast type. Such failure is signaled by a org.objectteams.RoleCastException.

(c) Class literal

A class literal of form R.class is dynamically bound to the class R visible in the current instance context. Using a class literal for a role outside its enclosing team instance (see §1.2.2) requires the following syntax:

RoleClass<@teamAnchor>.class

§1.2.5 File structure↑ §1.2

Just like regular inner classes, role classes may be inlined in the source code of the enclosing team. As an alternative style it is possible to store role classes in separate role files according to the following rules:

(a) Role directory

In the directory of the team class a new directory is created which has the same name as the team without the .java suffix.

(b) Role files

Role classes are stored in this directory (a). The file names are derived from the role class name extended by .java.
A role file must contain exactly one top-level type.

(c) package statement

A role class in a role file declares as its package the fully qualified name of the enclosing team class. The package statement of a role file must use the team modifier as its first token.

(d) Reference to role file

A team should mention in its javadoc comment each role class which is stored externally using a @role tag.

(e) Legal types in role files

The type in a role file must not be an enum.

(f) Imports in role files

A role file may have imports of its own. Within the role definition these imports are visible in addition to all imports of the enclosing team. Only base imports (see §2.1.2.(d)) must be defined in the team.

Semantically, there is no difference between inlined role classes and those stored in separate role files.

Note:
Current Java compilers disallow a type to have the same fully qualified name as a package. However, the JLS does not seem to make a statement in this respect. In OT/J, a package and a type are interpreted as being the same team, if both have the same fully qualified name and both have the team modifier.
Role file example:
in file org/objectteams/examples/MyTeamA.java :
1
package org.objectteams.examples;
2
/**
3
 * @author Stephan Herrmann
4
 * @date 20.02.2007
5
 * @file MyTeamA.java
6
 * @role MyRole
7
 */
8
public team class MyTeamA {
9
  ...
10
}
in file org/objectteams/examples/MyTeamA/MyRole.java:
1
team package org.objectteams.examples.MyTeamA;
2
public class MyRole {
3
  ...
4
}

§1.3 Acquisition and implicit inheritance of role classes↑ §1

Every team class implicitly implements the predefined interface org.objectteams.ITeam. If a team class has no explicit extends clause it implicitly extends org.objectteams.Team, thus providing implementations for the methods in org.objectteams.ITeam. If a team class extends a non-team class, the compiler implicitly adds implementations for all methods declared in org.objectteams.ITeam to the team class. Any subclass of a team (including org.objectteams.Team) must again be a team. Interface implementation is not affected by this rule.

Infrastructure provided via interface org.objectteams.ITeam is presented in §6.

§1.3.1 Acquisition and implicit inheritance of role classes↑ §1.3

A team acquires all roles from its super-team. This relation is similar to inheritance of inner classes, but with a few decisive differences as defined next. Two implementation options are mentioned below, which can be used to realize the special semantics of role acquisition (virtual classes and copy inheritance).

Implicit role inheritance
1
public team class S {
2
    protected class R0 {...}
3
    protected class R1 extends R0 {
4
        boolean ok;
5
        R2 m() {...}
6
        void n(R2 r) {...}
7
    }
8
    protected class R2 {...}
9
}
10
public team class T extends S {
11
    @Override protected class R1 {
12
        R2 m() {
13
            if(ok) { return tsuper.m(); }
14
            else { return null; }
15
        }
16
        void doIt() {
17
            n(m());
18
        }
19
    }
20
}

(a) Role class acquisition

A team T which extends a super-team S has one role class T.R corresponding to each role S.R of the super-team. The new type T.R overrides R for the context of T and its roles. Acquisition of role classes can either be direct (see (b) below), or it may involve overriding and implicit inheritance ((c) below).

In the above example (Listing 1.3.1-1) the team S operates on types S.R0, S.R1 and S.R2, while T operates on types T.R0, T.R1 and T.R2.
(Type references like "S.R0" are actually illegal in source code (§1.2.3.(b)). Here they are used for explanatory purposes only)

(b) Direct role acquisition

Within a sub-team T each role S.R of its super-team S is available by the simple name R without further declaration.

The role R2 in Listing 1.3.1-1 can be used in the sub-team T (line 12), because this role type is defined in the super class of the enclosing team.

(c) Overriding and implicit inheritance

If a team contains a role class definition by the same name as a role defined in its super-team, the new role class overrides the corresponding role from the super-team and implicitly inherits all of its features. Such relation is established only by name correspondence.

A role that overrides an inherited role should be marked with an @Override annotation. A compiler should optionally flag a missing @Override annotation with a warning. Conversely, it is an error if a role is marked with an @Override annotation but does not actually override an inherited role.

It is an error to override a role class with an interface or vice versa. A final role cannot be overridden.
Unlike regular inheritance, constructors are also inherited along implicit inheritance, and can be overridden just like normal methods.

In Listing 1.3.1-1 R1 in T implicitly inherits all features of R1 in S. This is, because its enclosing team T extends the team S (line 10) and the role definition uses the same name R1 (line 11). Hence the attribute ok is available in the method m() in T.R1 (line 13). T.R1 also overrides S.R1 which is marked by the @Override annotation in line 11.

(d) Lack of subtyping

Direct acquisition of roles from a super-team and implicit inheritance do not establish a subtype relation. A role of a given team is never conform (i.e., substitutable) to any role of any other team. S.R and T.R are always incommensurable.
Note, that this rule is a direct consequence of §1.2.2.(e).

(e) Dynamic binding of types

Overriding an acquired role by a new role class has the following implication: If an expression or declaration, which is evaluated on behalf of an instance of team T or one of its contained roles, refers to a role R, R will always resolve to T.R even if R was introduced in a super-team of T and even if the specific line of code was inherited from a super-team or one of its roles. Only the dynamic type of the enclosing team-instance is used to determine the correct role class (see below for an example).

A special case of dynamically binding role types relates to so-called class literals (see JLS §15.8.2). Role class literals are covered in §6.1.(c).

The above is strictly needed only for cases involving implicit inheritance. It may, however, help intuition, to also consider the directly acquired role T.R in (b) to override the given role S.R.

In line 17 of Listing 1.3.1-1 the implicitly inherited method n is called with the result of an invocation of m. Although n was defined in S (thus with argument type S.R2, see line 6) in the context of T it expects an argument of T.R2. This is correctly provided by the invocation of m in the context of T.

(f) tsuper

Super calls along implicit inheritance use the new keyword tsuper. While super is still available along regular inheritance, a call tsuper.m() selects the version of m of the corresponding role acquired from the super-team.

See §2.4.2 for tsuper in the context of role constructors.

tsuper can only be used to invoke a corresponding version of the enclosing method or constructor, i.e., an expression tsuper.m() may only occur within the method m with both methods having the same signature (see §2.3.2.(b) for an exception, where both methods have slightly different signatures).

In Listing 1.3.1-1 the role R1 in team T overrides the implicitly inherited method m() from S. tsuper.m() calls the overridden method m() from S.R1 (line 13).

(g) Implicitly inheriting super-types

If a role class has an explicit super class (using extends) this relation is inherited along implicit inheritance.

In Listing 1.3.1-1 the role R1 in T has T.R0 as its implicitly inherited super class, because the corresponding role in the super-team extends R0 (line 3).

Overriding an implicitly inherited super class is governed by §1.3.2.(b), below.
The list of implemented interfaces is merged along implicit inheritance.

(h) Preserving visibility

A role class must provide at least as much access as the implicit super role, or a compile-time error occurs (this is in analogy to JLS §8.4.6.3). Access rights of methods overridden by implicit inheritance follow the same rules as for normal overriding.

(i) Dynamic binding of constructors

When creating a role instance using new not only the type to instantiate is bound dynamically (cf. §1.3.1.(e)), but also the constructor to invoke is dynamically bound in accordance to the concrete type.
Within role constructors all this(..) and super(..) calls are bound statically with respect to explicit inheritance and dynamically with respect to implicit inheritance. This means the target role name is determined statically, but using that name the suitable role type is determined using dynamic binding.
See also §2.5.(a) on using constructors of abstract role classes.

(j) Overriding and compatibility

The rules of JLS §8.4.6 also apply to methods and constructors inherited via implicit inheritance.

(k) Covariant return types

Given a team T1 with two roles R1 and R2 where R2 explicitly inherits from R1, both roles defining a method m returning some type A. Given also a sub-team of T1, T2, where T2.R1 overrides m with a covariant return type B (sub-type of A):

    public team class T1 {
       protected abstract class R1 {
          abstract A m();
       }
       protected class R2 extends R1 {
          A m() { return new A(); }
       }
    }
    public team class T2 extends T1 {
       protected class R1 {
          @Override B m() { return new B(); } // this declaration renders class T2.R2 illegal
       }
    }

In this situation role T2.R2 will be illegal unless also overriding m with a return type that is at least B. Note, that the actual error occurs at the implicitly inherited method T2.R2.m which is not visible in the source code, even T2.R2 need not be mentioned explicitly in the source code. A compiler should flag this as an imcompatibility at the team level, because a team must specialize inherited roles in a consistent way.

Example code (Teams and Roles):
1
public team class MyTeamA {
2
  protected class MyRole {
3
    String name;
4
    public MyRole (String n) { name = n; }
5
    public void print() { System.out.println("id="+name); }
6
  }
7
  protected MyRole getRole() { return new MyRole("Joe"); }
8
}
10
public team class MySubTeam extends MyTeamA {
11
  protected class MyRole {
12
    int age;
13
    public void setAge(int a) { age = a; }
14
    public void print() {
15
      tsuper.print();
16
      System.out.println("age="+age);
17
    }
18
  }
19
  public void doit() {
20
    MyRole r = getRole();
21
    r.setAge(27);
22
    r.print();
23
  }
24
}
25
...
26
MySubTeam myTeam = new MySubTeam();
27
myTeam.doit();
Program output
id=Joe
age=27
Effects:
  • According to §1.3, MyTeamA implements ITeam (line 1).
  • An implicit role inheritance is created for MySubTeam.MyRole (§1.3.1.(c); line 11).
    If we visualize this special inheritance using a fictitious keyword overrides the compiler would see a declaration:
    protected class MyRole overrides MyTeamA.MyRole { ... }
  • Invoking getRole() on myTeam (line 27, 20) creates an instance of MySubTeam.MyRole because the acquired role MyTeamA.MyRole is overridden by MySubTeam.MyRole following the rules of implicit inheritance (cf. §1.3.1.(e)).
  • Overriding of role methods and access to inherited features works as usual.
  • As an example for §1.3.1.(f) see the call tsuper.print() (line 15), which selects the implementation of MyTeamA.MyRole.print.

§1.3.2 Regular role inheritance↑ §1.3

In addition to implicit inheritance, roles may also inherit using the standard Java keyword extends. These restrictions apply:

(a) Super-class restrictions

If the super-class of a role is again a role it must be a direct role of an enclosing team This rule is simply enforced by disallowing type anchors in the extends clause (see §1.2.2.(g)). As an effect, the super-class may never be more deeply nested than the sub-class.

(b) Inheriting and overriding the extends clause

If a role overrides another role by implicit inheritance, it may change the inherited extends clause (see §1.3.1.(g) above) only if the new super-class is a sub-class of the class in the overridden extends clause. I.e., an implicit sub-role may specialize the extends clause of its implicit super-role.

(c) Constructors and overridden 'extends'

Each constructor of a role class that overrides the extends clause of its implicit super-role must invoke a constructor of this newly introduced explicit super-class. Thus it may not use a tsuper constructor (see §2.4.2).

(d) Adding implemented interfaces

implements declarations are additive, i.e., an implicit sub-role may add more interfaces but has to implement all interfaces of its implicit super-role, too.

(e) Visibility of inherited methods

When a role inherits non-public methods from a regular class (as its super class), these methods are considered as private for the role, i.e., they can only be accessed in an unqualified method call m() using the implicit receiver this.

§1.4 Name clashes↑ §1

OT/J restricts Java with respect to handling of conflicting names.

(a) Names of role classes

A role class may not have the same name as a method or field of its enclosing team. A role class may not shadow another class that is visible in the scope of the enclosing team.

(b) Names of role methods and fields

Along implicit inheritance, the names of methods or fields may not hide, shadow or obscure any previously visible name.
(see JLS §8.3, §8.4.6.2, §8.5, §9.3, §9.5 (hiding), §6.3.1 (shadowing), §6.3.2 (obscuring).

§1.5 Team and role nesting↑ §1

Multi-level nesting of classes is restricted only by the following rules.

Example code (Nesting):
1
public team class SuperOuter {
2
  public team class RoleAndTeam {
3
    protected class InnerRole {
4
      Runnable foo() { return null; }
5
    }
6
  }
7
  public team class RoleAndTeamSub extends RoleAndTeam {
8
    protected class InnerRole {
9
      Runnable foo() { throw new RuntimeException(); }
10
    }
11
  }
12
}
13
public team class OuterTeam extends SuperOuter {
14
  public team class RoleAndTeam {
15
    protected class InnerRole {
16
      Runnable foo() {
17
        class Local {};
18
        return new Runnable() { // anonymous class definition
19
          public void run() {}
20
        };
21
      }
22
      // class IllegalMember {}
23
    }
24
  }
25
  public team class RoleAndTeamSub {
26
    protected class InnerRole {
27
      Runnable foo() {
28
        RoleAndTeamSub.tsuper.foo();
29
        return OuterTeam.tsuper.foo();
30
      };
31
    }
32
  }
33
}

(a) Nested teams

If a role class is also marked using the team modifier, it may contain roles at the next level of nesting.

  • In the above example (Listing 1.5) class RoleAndTeam starting in line 14 is a role of OuterTeam and at the same time a team containing a further role InnerRole

Such a hybrid role-and-team has all properties of both kinds of classes.

(b) Nested classes of roles

A regular role class (ie., not marked as team, see above) may contain local types (see JLS §14.3 - in the example: class Local), anonymous types (JLS §15.9.5 - in the example: class defined in lines 18-20) but no member types (JLS §8.5 - in the example: illegal class IllegalMember).
The effect is, that nested types of a regular role cannot be used outside the scope of their enclosing role.

(c) Prohibition of cycles

A nested team may not extend its own enclosing team.

(d) Prohibition of name clashes

A nested team may inherit roles from multiple sources: its explicit super team and any of its implicit super classes (roles) from different levels of nesting. If from different sources a team inherits two or more roles of the same name that are not related by implicit inheritance, this is an illegal name clash.

(e) Precedence among different supers

If a role inherits the same feature from several super roles (super and tsuper), an implicitly inherited version always overrides any explicitly inherited feature, i.e., a role with the same simple name is closer related than one with a different name.

Also implicit inheritance alone may produce several candidate methods inherited by a role class. This is a result of team-nesting where each level of nesting may add one more tsuper role if outer teams also participate in an inheritance relationship. In this case a role inherited from an implicit super team of the enclosing team is closer related than a role inherited from an explicit super team. If necessary this rule is applied inside out until a nesting level is found where indeed explicit team inheritance is involved.
So when comparing classes by their fully qualified names the longest common suffix will determine the closest relationship. E.g., SuperOuter.RoleAndTeamSub.InnerRole is the closest ancestor of SubOuter.RoleAndTeamSub.InnerRole because both share the name suffix RoleAndTeamSub.InnerRole.

In the above example (Listing 1.5) role OuterTeam.RoleAndTeamSub.InnerRole has two direct tsuper roles: OuterTeam.RoleAndTeam.InnerRole and SuperOuter.RoleAndTeamSub.InnerRole. Without the method foo defined in lines 27-30, the enclosing class OuterTeam.RoleAndTeamSub.InnerRole would inherit the method foo defined in SuperOuter.RoleAndTeamSub.InnerRole (line 9), because the common name suffix RoleAndTeamSub.InnerRole creates a stronger relationship making that class the closest ancestor.

Example diagram team nesting

(f) Qualified tsuper

A role in a nested team may qualify the keyword tsuper (see §1.3.1.(f) above) by a type name in order to select among different implicit super classes. A term OuterTeam.tsuper evaluates to a corresponding implicit super class within the context of the explicit super-class (here: SuperOuter) of the enclosing team "OuterTeam". A method call OuterTeam.tsuper.m() evaluates to the method version within SuperOuter that best corresponds to the current method containing the tsuper-call.

  • In the above example (Listing 1.5) line 28 selects the method version within the superclass of RoleAndTeamSub (i.e., within RoleAndTeam), resolving to OuterTeam.RoleAndTeam.InnerRole.foo().
  • Line 29 selects a corresponding method from the context of SuperOuter resolving to SuperOuter.RoleAndTeamSub.InnerRole.foo() which has the same semantics as an unqualified tsuper call would have.

Language implementation:↑ §1

Role acquisition and implicit inheritance can be implemented in at least two ways.

Virtual classes: Each role class is an overridable feature of its enclosing team. Role classes are resolved by dynamic binding with respect to the enclosing team instance. This implementation requires multiple-inheritance in order to also allow regular inheritance between roles of the same team. super and tsuper select parent versions of a method along the two dimensions of inheritance.

Copy inheritance: Role acquisition from a super-team has the effect of copying a role definition T.R yielding a new role Tsub.R. All role applications Rx in the role copy refer to Tsub.Rx. Implicit role inheritance extends a role copy in-place. Only the tsuper construct allows to access the previous version of a method (i.e. before in-place overriding).

References:↑ §1

[1] Ole Lehrmann Madsen and Birger Møller-Pedersen. Virtual classes: A powerful mechanism in object-oriented programming. In Proceedings OOPSLA 89, ACM SIGPLAN Notices, volume 24, 10, pages 397-406, October 1989.

[2] Erik Ernst. Family Polymorphism. In Proceedings ECOOP 2001, LNCS 2072, pages 303-326, Springer, 2001.