diff --git a/.gitignore b/.gitignore
index 0ce1ed6d4729d8351479cdf3f2df31e9f9f16d7d..dd288580ab6963cbac2a1795fccf901264833749 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,6 @@ src/test/jastadd/multiple/Multiple.ast
 src/test/jastadd/multiple/Multiple.jadd
 src/test/jastadd/resolver/Resolver.ast
 src/test/jastadd/resolver/Resolver.jadd
-src/test/jastadd/resolver/ResolverRefResolver.jadd
\ No newline at end of file
+src/test/jastadd/resolver/ResolverRefResolver.jadd
+src/test/jastadd/listnames/ListNames.ast
+src/test/jastadd/listnames/ListNames.jadd
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 347c375e2fd4c9b1253248ba0590da68428e4776..fdf2acc274d909b8a9436cf2ffbf72e1b253f379 100644
--- a/build.gradle
+++ b/build.gradle
@@ -182,7 +182,7 @@ task compileMultipleTest(type: JavaExec, group: 'verification') {
 task preprocessResolverTest(type: JavaExec, group: 'verification') {
 
     doFirst {
-        delete 'src/test/jastadd/resolver/Resolver.ast', 'src/test/jastadd/resolver/Resolver.jadd'
+        delete 'src/test/jastadd/resolver/Resolver.ast', 'src/test/jastadd/resolver/Resolver.jadd', 'src/test/jastadd/resolver/ResolverRefResolver.jadd'
     }
 
     classpath = sourceSets.main.runtimeClasspath
@@ -201,6 +201,28 @@ task compileResolverTest(type: JavaExec, group: 'verification') {
     args '--o=src/test/java-gen/', '--package=resolver.ast', 'src/test/jastadd/resolver/Resolver.ast', 'src/test/jastadd/resolver/Resolver.jadd', 'src/test/jastadd/resolver/ResolverUtils.jadd', 'src/test/jastadd/resolver/ResolverRefResolver.jadd', 'src/test/jastadd/resolver/MyRefResolver.jadd', 'src/test/jastadd/Utils.jadd'
 }
 
+task preprocessListNamesTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/jastadd/listnames/ListNames.ast', 'src/test/jastadd/listnames/ListNames.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.relast.compiler.Compiler'
+    args 'src/test/jastadd/listnames/ListNames.relast', '--file', '--grammarName=src/test/jastadd/listnames/ListNames', '--jastAddList=ListyMcListface'
+}
+
+task compileListNamesTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/java-gen/listnames'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.JastAdd'
+    args '--o=src/test/java-gen/', '--package=listnames.ast', 'src/test/jastadd/listnames/ListNames.ast', 'src/test/jastadd/listnames/ListNames.jadd', 'src/test/jastadd/Utils.jadd', '--List=ListyMcListface'
+}
+
 
 test {
     outputs.upToDateWhen { false }
@@ -219,4 +241,7 @@ test.dependsOn compileMultipleTest
 compileMultipleTest.dependsOn preprocessMultipleTest
 
 test.dependsOn compileResolverTest
-compileResolverTest.dependsOn preprocessResolverTest
\ No newline at end of file
+compileResolverTest.dependsOn preprocessResolverTest
+
+test.dependsOn compileListNamesTest
+compileListNamesTest.dependsOn preprocessListNamesTest
\ No newline at end of file
diff --git a/src/main/jastadd/Backend.jadd b/src/main/jastadd/Backend.jadd
index e5261078d1f0bb922249942c89cf65c033f3420c..7e02ef026e8cbbb01b2a602f3f98bdbe4b57b018 100644
--- a/src/main/jastadd/Backend.jadd
+++ b/src/main/jastadd/Backend.jadd
@@ -1,7 +1,9 @@
 aspect BackendAbstractGrammar {
 
   public static String ASTNode.listClass = "ArrayList";
-  public static boolean ASTNode.resolverHelper = false;
+  public static String ASTNode.jastAddListType = "List";
+
+public static boolean ASTNode.resolverHelper = false;
 
   public String Program.generateAbstractGrammar() {
     StringBuilder sb = new StringBuilder();
@@ -121,7 +123,7 @@ aspect BackendAspect {
     return getTypeUse() + " " + getID();
   }
   public String ListComponent.constructorParameter() {
-    return "List<" + getTypeUse() + "> " + getID();
+    return ASTNode.jastAddListType + "<" + getTypeUse() + "> " + getID();
   }
   public String OptComponent.constructorParameter() {
     return "Opt<" + getTypeUse() + "> " + getID();
diff --git a/src/main/java/org/jastadd/relast/compiler/Compiler.java b/src/main/java/org/jastadd/relast/compiler/Compiler.java
index 0eee7309ebf85a2c53c99c07ba00a069ddae07b0..84fb2a7480536a8689be5153ffb293ca18be609a 100644
--- a/src/main/java/org/jastadd/relast/compiler/Compiler.java
+++ b/src/main/java/org/jastadd/relast/compiler/Compiler.java
@@ -19,6 +19,7 @@ public class Compiler {
   private FlagOption optionWriteToFile;
   private FlagOption optionPrintAST;
   private StringOption optionListClass;
+  private StringOption optionJastAddList;
   private StringOption optionGrammarName;
   private FlagOption optionResolverHelper;
   private CommandLine commandLine;
@@ -38,6 +39,12 @@ public class Compiler {
     List<TypeDecl> unresolvedDecls = new ArrayList<>();
     Program p = parseProgram(filenames);
 
+    if (optionJastAddList.isSet()) {
+      System.out.println("JastAdd List type is set to " + optionJastAddList.getValue());
+      ASTNode.jastAddListType = optionJastAddList.getValue();
+    }
+
+
     if (optionResolverHelper.isSet()) {
       // get a list of all (abstract or not) non-terminals
       List<TypeDecl> nonTerminals = new ArrayList<>();
@@ -122,6 +129,8 @@ public class Compiler {
     optionListClass = addOption(new StringOption("listClass", "determine the class name of the nonterminal reference list"));
     optionGrammarName = addOption(new StringOption("grammarName", "name of the generated grammar and aspect (without file extension)"));
     optionResolverHelper = addOption(new FlagOption("resolverHelper", "create a subtype for each type containing a string that can be used to resolve the type later"));
+    optionJastAddList = addOption(new StringOption("jastAddList", "set the name of the List type in JastAdd (has to match the option '--List' or its default List)"));
+
   }
 
   private <OptionType extends Option<?>> OptionType addOption(OptionType option) {
diff --git a/src/test/jastadd/listnames/ListNames.relast b/src/test/jastadd/listnames/ListNames.relast
new file mode 100644
index 0000000000000000000000000000000000000000..b620c6b8e5cb2800d7afa575a566bb0a2666894a
--- /dev/null
+++ b/src/test/jastadd/listnames/ListNames.relast
@@ -0,0 +1,20 @@
+Root ::= A* B*;
+A ::= <Name>;
+B ::= <Name>;
+rel Root.Aa? -> A;
+
+rel A.Di1  -> B;
+rel A.Di2? -> B;
+rel A.Di3* -> B;
+
+rel A.Bi1 <-> B.Bi1;
+rel A.Bi2 <-> B.Bi2?;
+rel A.Bi3 <-> B.Bi3*;
+
+rel A.Bi4? <-> B.Bi4;
+rel A.Bi5? <-> B.Bi5?;
+rel A.Bi6? <-> B.Bi6*;
+
+rel A.Bi7* <-> B.Bi7;
+rel A.Bi8* <-> B.Bi8?;
+rel A.Bi9* <-> B.Bi9*;
diff --git a/src/test/java/org/jastadd/relast/tests/ListNames.java b/src/test/java/org/jastadd/relast/tests/ListNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ddd9e5c80d0c461f6de291d9e0f0c61835db093
--- /dev/null
+++ b/src/test/java/org/jastadd/relast/tests/ListNames.java
@@ -0,0 +1,641 @@
+package org.jastadd.relast.tests;
+
+import listnames.ast.A;
+import listnames.ast.B;
+import listnames.ast.Root;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static org.jastadd.relast.tests.TestHelpers.readFile;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+class ListNames {
+  private Root r;
+  private A a1;
+  private A a2;
+  private A a3;
+  private B b1;
+  private B b2;
+  private B b3;
+
+
+  @Test
+  void checkListNames() throws IOException {
+
+    String aspectFile = "./src/test/jastadd/listnames/ListNames.jadd";
+    String aspect = readFile(aspectFile, Charset.defaultCharset());
+
+    int index = 0;
+    int count = 0;
+    while (aspect.indexOf("ListyMcListface", index) >= 0) {
+      count++;
+      index = aspect.indexOf("ListyMcListface", index) + "ListyMcListface".length();
+    }
+
+    // ListyMcListface must occur exactly twice (in the constructor for Root)
+    assertSame(2, count);
+
+  }
+
+
+  /**
+   * rel A.Di1 -> B;
+   */
+  @Test
+  void testDi1() {
+    setup();
+    a1.setDi1(b2);
+    a2.setDi1(b1);
+
+    assertSame(a1.getDi1(), b2);
+    assertSame(a2.getDi1(), b1);
+
+    a2.setDi1(b2);
+
+    assertSame(a1.getDi1(), b2);
+    assertSame(a2.getDi1(), b2);
+
+    try {
+      a3.setDi1(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Di2? -> B;
+   */
+  @Test
+  void testDi2() {
+    setup();
+    a1.setDi2(b2);
+    a2.setDi2(b1);
+
+    assertSame(a1.getDi2(), b2);
+    assertSame(a2.getDi2(), b1);
+
+    a2.setDi2(b2);
+
+    assertSame(a1.getDi2(), b2);
+    assertSame(a2.getDi2(), b2);
+
+    a2.clearDi2();
+
+    assertSame(a1.getDi2(), b2);
+    assertNull(a2.getDi2());
+
+    assertTrue(a1.hasDi2());
+    assertFalse(a2.hasDi2());
+    assertFalse(a3.hasDi2());
+  }
+
+
+  /**
+   * rel A.Di3* -> B;
+   */
+  @Test
+  void testDi3() {
+    setup();
+    a1.addDi3(b1);
+    a1.addDi3(b2);
+    a1.addDi3(b3);
+    a2.addDi3(b2);
+
+    assertEquals(a1.getDi3s(), Arrays.asList(b1, b2, b3));
+    assertEquals(a1.getDi3List(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.getDi3s(), Arrays.asList(b2));
+    assertEquals(a2.getDi3List(), Arrays.asList(b2));
+    assertEquals(a3.getDi3s(), Arrays.asList());
+    assertEquals(a3.getDi3List(), Arrays.asList());
+
+    a1.addDi3(b1);
+    a2.addDi3(b1);
+    a2.addDi3(b2);
+
+    assertEquals(a1.getDi3s(), Arrays.asList(b1, b2, b3, b1));
+    assertEquals(a1.getDi3List(), Arrays.asList(b1, b2, b3, b1));
+    assertEquals(a2.getDi3s(), Arrays.asList(b2, b1, b2));
+    assertEquals(a2.getDi3List(), Arrays.asList(b2, b1, b2));
+    assertEquals(a3.getDi3s(), Arrays.asList());
+    assertEquals(a3.getDi3List(), Arrays.asList());
+
+    a1.removeDi3(b1);
+    a2.removeDi3(b2);
+
+    assertEquals(a1.getDi3s(), Arrays.asList(b2, b3, b1));
+    assertEquals(a1.getDi3List(), Arrays.asList(b2, b3, b1));
+    assertEquals(a2.getDi3s(), Arrays.asList(b1, b2));
+    assertEquals(a2.getDi3List(), Arrays.asList(b1, b2));
+    assertEquals(a3.getDi3s(), Arrays.asList());
+    assertEquals(a3.getDi3List(), Arrays.asList());
+  }
+
+
+  /**
+   * rel A.Bi1 <-> B.Bi1;
+   */
+
+
+  @Test
+  void testBi11() {
+    // Init
+    setup();
+    a1.setBi1(b1);
+    a2.setBi1(b2);
+
+    // Change
+    a2.setBi1(b1);
+
+    assertNull(a1.getBi1());
+    assertSame(a2.getBi1(), b1);
+    assertSame(b1.getBi1(), a2);
+    assertNull(b2.getBi1());
+  }
+
+  @Test
+  void testBi12() {
+    // Init
+    setup();
+    a1.setBi1(b2);
+
+    // Change
+    a2.setBi1(b2);
+
+    assertNull(a1.getBi1());
+    assertSame(a2.getBi1(), b2);
+    assertNull(b1.getBi1());
+    assertSame(b2.getBi1(), a2);
+  }
+
+
+  /**
+   * rel A.Bi2 <-> B.Bi2?;
+   */
+
+  @Test
+  void testBi21() {
+    // Init
+    setup();
+    a1.setBi2(b1);
+    a2.setBi2(b2);
+
+    // Change
+    a2.setBi2(b1);
+
+    assertNull(a1.getBi2());
+    assertSame(a2.getBi2(), b1);
+    assertSame(b1.getBi2(), a2);
+    assertNull(b2.getBi2());
+  }
+
+  @Test
+  void testBi22() {
+    // Init
+    setup();
+    a1.setBi2(b2);
+
+    // Change
+    a2.setBi2(b2);
+
+    assertNull(a1.getBi2());
+    assertSame(a2.getBi2(), b2);
+    assertNull(b1.getBi2());
+    assertSame(b2.getBi2(), a2);
+  }
+
+
+  /**
+   * rel A.Bi3 <-> B.Bi3*;
+   */
+  @Test
+  void testBi3() {
+    setup();
+    a2.setBi3(b2);
+
+    assertNull(a1.getBi3());
+    assertSame(a2.getBi3(), b2);
+    assertEquals(b1.getBi3s(), Arrays.asList());
+    assertEquals(b1.getBi3List(), Arrays.asList());
+    assertEquals(b2.getBi3s(), Arrays.asList(a2));
+    assertEquals(b2.getBi3List(), Arrays.asList(a2));
+    assertEquals(b3.getBi3s(), Arrays.asList());
+    assertEquals(b3.getBi3List(), Arrays.asList());
+
+    a2.setBi3(b3);
+
+    assertNull(a1.getBi3());
+    assertSame(a2.getBi3(), b3);
+    assertEquals(b1.getBi3s(), Arrays.asList());
+    assertEquals(b1.getBi3List(), Arrays.asList());
+    assertEquals(b2.getBi3s(), Arrays.asList());
+    assertEquals(b2.getBi3List(), Arrays.asList());
+    assertEquals(b3.getBi3s(), Arrays.asList(a2));
+    assertEquals(b3.getBi3List(), Arrays.asList(a2));
+
+    a1.setBi3(b3);
+    a3.setBi3(b3);
+
+    assertSame(a1.getBi3(), b3);
+    assertSame(a2.getBi3(), b3);
+    assertSame(a3.getBi3(), b3);
+    assertEquals(b1.getBi3s(), Arrays.asList());
+    assertEquals(b1.getBi3List(), Arrays.asList());
+    assertEquals(b2.getBi3s(), Arrays.asList());
+    assertEquals(b2.getBi3List(), Arrays.asList());
+    assertEquals(b3.getBi3s(), Arrays.asList(a2, a1, a3));
+    assertEquals(b3.getBi3List(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi3(b1);
+
+    assertSame(a1.getBi3(), b3);
+    assertSame(a2.getBi3(), b1);
+    assertSame(a3.getBi3(), b3);
+    assertEquals(b1.getBi3s(), Arrays.asList(a2));
+    assertEquals(b1.getBi3List(), Arrays.asList(a2));
+    assertEquals(b2.getBi3s(), Arrays.asList());
+    assertEquals(b2.getBi3List(), Arrays.asList());
+    assertEquals(b3.getBi3s(), Arrays.asList(a1, a3));
+    assertEquals(b3.getBi3List(), Arrays.asList(a1, a3));
+
+    try {
+      a2.setBi3(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Bi4? <-> B.Bi4;
+   */
+  @Test
+  void testBi41() {
+    // Init
+    setup();
+    a1.setBi4(b1);
+    a2.setBi4(b2);
+
+    // Change
+    a2.setBi4(b1);
+
+    assertNull(a1.getBi4());
+    assertSame(a2.getBi4(), b1);
+    assertSame(b1.getBi4(), a2);
+    assertNull(b2.getBi4());
+  }
+
+  @Test
+  void testBi42() {
+    // Init
+    setup();
+    a1.setBi4(b2);
+
+    // Change
+    a2.setBi4(b2);
+
+    assertNull(a1.getBi4());
+    assertSame(a2.getBi4(), b2);
+    assertNull(b1.getBi4());
+    assertSame(b2.getBi4(), a2);
+  }
+
+
+  /**
+   * rel A.Bi5? <-> B.Bi5?;
+   */
+  @Test
+  void testBi51() {
+    // Init
+    setup();
+    a1.setBi5(b1);
+    a2.setBi5(b2);
+
+    // Change
+    a2.setBi5(b1);
+
+    assertNull(a1.getBi5());
+    assertSame(a2.getBi5(), b1);
+    assertSame(b1.getBi5(), a2);
+    assertNull(b2.getBi5());
+  }
+
+  @Test
+  void testBi52() {
+    // Init
+    setup();
+    a1.setBi5(b2);
+
+    // Change
+    a2.setBi5(b2);
+
+    assertNull(a1.getBi5());
+    assertSame(a2.getBi5(), b2);
+    assertNull(b1.getBi5());
+    assertSame(b2.getBi5(), a2);
+  }
+
+
+  /**
+   * rel A.Bi6? <-> B.Bi6*;
+   */
+  @Test
+  void testBi6() {
+    setup();
+    a2.setBi6(b2);
+
+    assertNull(a1.getBi6());
+    assertSame(a2.getBi6(), b2);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList(a2));
+    assertEquals(b2.getBi6List(), Arrays.asList(a2));
+    assertEquals(b3.getBi6s(), Arrays.asList());
+    assertEquals(b3.getBi6List(), Arrays.asList());
+
+    a2.setBi6(b3);
+
+    assertNull(a1.getBi6());
+    assertSame(a2.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a2));
+    assertEquals(b3.getBi6List(), Arrays.asList(a2));
+
+    a1.setBi6(b3);
+    a3.setBi6(b3);
+
+    assertSame(a1.getBi6(), b3);
+    assertSame(a2.getBi6(), b3);
+    assertSame(a3.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a2, a1, a3));
+    assertEquals(b3.getBi6List(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi6(b1);
+
+    assertSame(a1.getBi6(), b3);
+    assertSame(a2.getBi6(), b1);
+    assertSame(a3.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList(a2));
+    assertEquals(b1.getBi6List(), Arrays.asList(a2));
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a1, a3));
+    assertEquals(b3.getBi6List(), Arrays.asList(a1, a3));
+
+    a2.clearBi6();
+
+    assertSame(a1.getBi6(), b3);
+    assertNull(a2.getBi6());
+    assertSame(a3.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a1, a3));
+    assertEquals(b3.getBi6List(), Arrays.asList(a1, a3));
+
+    assertTrue(a1.hasBi6());
+    assertFalse(a2.hasBi6());
+    assertTrue(a3.hasBi6());
+  }
+
+
+  /**
+   * rel A.Bi7* <-> B.Bi7;
+   */
+  @Test
+  void testBi7() {
+    setup();
+    a2.addBi7(b2);
+
+    assertEquals(a1.getBi7s(), Arrays.asList());
+    assertEquals(a1.getBi7List(), Arrays.asList());
+    assertEquals(a2.getBi7s(), Arrays.asList(b2));
+    assertEquals(a2.getBi7List(), Arrays.asList(b2));
+    assertNull(b1.getBi7());
+    assertSame(b2.getBi7(), a2);
+    assertNull(b3.getBi7());
+
+    a2.addBi7(b3);
+    a1.addBi7(b2);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertNull(b1.getBi7());
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+
+    a1.addBi7(b1);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertSame(b1.getBi7(), a1);
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+
+    a1.addBi7(b1);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertSame(b1.getBi7(), a1);
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+
+    a1.removeBi7(b1);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertNull(b1.getBi7());
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+  }
+
+
+  /**
+   * rel A.Bi8* <-> B.Bi8?;
+   */
+  @Test
+  void testBi8() {
+    setup();
+    a2.addBi8(b2);
+
+    assertEquals(a1.getBi8s(), Arrays.asList());
+    assertEquals(a1.getBi8List(), Arrays.asList());
+    assertEquals(a2.getBi8s(), Arrays.asList(b2));
+    assertEquals(a2.getBi8List(), Arrays.asList(b2));
+    assertNull(b1.getBi8());
+    assertSame(b2.getBi8(), a2);
+    assertNull(b3.getBi8());
+
+    a2.addBi8(b3);
+    a1.addBi8(b2);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertNull(b1.getBi8());
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+
+    a1.addBi8(b1);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertSame(b1.getBi8(), a1);
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+
+    a1.addBi8(b1);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertSame(b1.getBi8(), a1);
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+
+    a1.removeBi8(b1);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertNull(b1.getBi8());
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+  }
+
+
+  /**
+   * rel A.Bi9* <-> B.Bi9*;
+   */
+  @Test
+  void testBi9() {
+    setup();
+    a1.addBi9(b1);
+    a1.addBi9(b2);
+
+    assertEquals(a1.getBi9s(), Arrays.asList(b1, b2));
+    assertEquals(a1.getBi9List(), Arrays.asList(b1, b2));
+    assertEquals(a2.getBi9s(), Arrays.asList());
+    assertEquals(a2.getBi9List(), Arrays.asList());
+    assertEquals(a3.getBi9s(), Arrays.asList());
+    assertEquals(a3.getBi9List(), Arrays.asList());
+    assertEquals(b1.getBi9s(), Arrays.asList(a1));
+    assertEquals(b1.getBi9List(), Arrays.asList(a1));
+    assertEquals(b2.getBi9s(), Arrays.asList(a1));
+    assertEquals(b2.getBi9List(), Arrays.asList(a1));
+    assertEquals(b3.getBi9s(), Arrays.asList());
+    assertEquals(b3.getBi9List(), Arrays.asList());
+
+    b3.addBi9(a1);
+    b3.addBi9(a3);
+    b3.addBi9(a1);
+
+    assertEquals(a1.getBi9s(), Arrays.asList(b1, b2, b3, b3));
+    assertEquals(a1.getBi9List(), Arrays.asList(b1, b2, b3, b3));
+    assertEquals(a2.getBi9s(), Arrays.asList());
+    assertEquals(a2.getBi9List(), Arrays.asList());
+    assertEquals(a3.getBi9s(), Arrays.asList(b3));
+    assertEquals(a3.getBi9List(), Arrays.asList(b3));
+    assertEquals(b1.getBi9s(), Arrays.asList(a1));
+    assertEquals(b1.getBi9List(), Arrays.asList(a1));
+    assertEquals(b2.getBi9s(), Arrays.asList(a1));
+    assertEquals(b2.getBi9List(), Arrays.asList(a1));
+    assertEquals(b3.getBi9s(), Arrays.asList(a1, a3, a1));
+    assertEquals(b3.getBi9List(), Arrays.asList(a1, a3, a1));
+
+    b3.removeBi9(a1);
+
+    assertEquals(a1.getBi9s(), Arrays.asList(b1, b2, b3));
+    assertEquals(a1.getBi9List(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.getBi9s(), Arrays.asList());
+    assertEquals(a2.getBi9List(), Arrays.asList());
+    assertEquals(a3.getBi9s(), Arrays.asList(b3));
+    assertEquals(a3.getBi9List(), Arrays.asList(b3));
+    assertEquals(b1.getBi9s(), Arrays.asList(a1));
+    assertEquals(b1.getBi9List(), Arrays.asList(a1));
+    assertEquals(b2.getBi9s(), Arrays.asList(a1));
+    assertEquals(b2.getBi9List(), Arrays.asList(a1));
+    assertEquals(b3.getBi9s(), Arrays.asList(a3, a1));
+    assertEquals(b3.getBi9List(), Arrays.asList(a3, a1));
+  }
+
+
+  @Test
+  void testImmutableList() {
+    setup();
+
+    a1.addDi3(b1);
+    a1.addDi3(b2);
+    try {
+      a1.getDi3s().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addBi7(b1);
+    a1.addBi7(b2);
+    try {
+      a1.getBi7s().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addBi9(b1);
+    a1.addBi9(b2);
+    try {
+      a1.getBi9s().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+  @BeforeEach
+  void setup() {
+    r = new Root();
+    a1 = new A("a1");
+    a2 = new A("a2");
+    a3 = new A("a3");
+    b1 = new B("b1");
+    b2 = new B("b2");
+    b3 = new B("b3");
+
+    r.addA(a1);
+    r.addA(a2);
+    r.addA(a3);
+    r.addB(b1);
+    r.addB(b2);
+    r.addB(b3);
+  }
+}