Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8061410

Multiply-bounded reference type expressions

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Other
    • Icon: P4 P4
    • None
    • None
    • specification

      A DESCRIPTION OF THE REQUEST :
      As an enhancement to the generics system, I would like to be able to use reference type expressions that have mutliple bounds. A reference should be able to be declared with a type declaration that mirrors the functionality of inheritance - ie, it can inherit the type of exactly one class (if none specified, it is "java.lang.Object") and zero or more interfaces. Type syntax would be something like the following:

      -------------
      Type:
        ReferenceMultiType
        BasicType

      ReferenceMultiType:
        ReferenceType { & ReferenceType }

      ReferenceType:
        Identifier [TypeArguments] { . Identifier [TypeArguments] } BracketsOpt

      TypeArguments:
        <TypeArgument {, TypeArgument}>

      TypeArgument:
        ReferenceMultiType
        ? [(extends | super) ReferenceMultiType]

      NormalClassDeclaration:
        class Identifier TypeParameters(opt) [extends ReferenceType] [implements TypeList] ClassBody

      TypeParameter:
        Identifier [extends ReferenceMultiType]

      TypeList:
        ReferenceType {, ReferenceType}

      Bound: <defunct>
      ------------

      The above grammar modifications are based on JLS 3.0 and are a suggestion only. The examples I have provided are based on the above grammar, but I'm not set on the above grammar, but I'm more interested in the idea being implemented.

      The compiler should perform sanity checking on a ReferenceMultiType expresssion. In particular, it should check that:

      1. There is at most one class in the list of ReferenceTypes.
      2. The classes and interfaces specified in the ReferenceMultiType should be compatible - eg, trying to create a ReferenceMultiType expression from the following interfaces:

      interface A
      {
        public void foo();
      }

      interface B
      {
        public int foo();
      }

      ... should generate at least a compiler warning, as no concrete class can implement both interfaces and hence we will never be able to assign a concrete variable to such a reference. This should perhaps even generate a compiler error.

      Backward compatibility from this mechanism can be maintained through erasure, as per generics. Similar to multiply-bounded type arguments of JLS 3, the erasure of the ReferenceMultiType will be the first ReferenceType in the ReferenceMultiType list.

      JUSTIFICATION :
      In summary, it will improve the type-safety of the language.

      I originally thought of this requirement when I wanted to have a container of serializable, arbitrary Lists. At present, there is no mechanism to enforce this requirement at compile time. For instance, I would like to be able to go:

      List<List<?> & Serializable> list = new ArrayList<List<?> & Serializable>();

      This gives me a compile-time guarantee about the serializability of the elements of the list, without tying me to a concrete, serializable implementation of the List interface. In the current version of the language, I cannot enforce this requirement at compile time (see workarounds).

      Note that while I have focused on two particular interfaces (Serializable and List), this applies in general to any two interfaces. Please do not confuse this as an issue specific to serializability; the problem (and the proposed solution) is more general than that.

      Upon further reflection, I realised that this concept could easily be extended to cover any reference type. For example, suppose I want a reference variable "serializableList" that will hold a reference to any object that is an instance of both java.io.Serializable and java.util.List. I would like to be able to declare the reference variable as follows:

      List<?> & Serializable serializableList;

      I could then assign to this variable a reference to an instance of any class that implements both Serializable and List, for example:

      serializableList = new ArrayList<Object>();
      serializableList = new LinkedList<Object>();

      This reference could be passed in anywhere where either a List or a Serializable is called for:

      List<?> list = serializableList;
      Serializable serializable = serializableList;

      Any reference that list that did not implement both List and Serializable could not be assigned to it:

      serializableList = list; // Compile-time error - incompatible type
      serializableList = serializable; // Compile-time error - incompatible type

      This improved type-safety is in keeping with the strongly-typed nature of Java, along with its particular inheritance mechanism. It will open up further possibilities to the developer in allowing them to generate more robust code.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      See description and justification.
      ACTUAL -
      See description and justification.

      ---------- BEGIN SOURCE ----------

      import java.util.*;
      import java.io.*;

      class Test
      {
        public List & Serializable myList;

      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      There are workarounds, but they all have drawbacks. Three workarounds to my "list of serializable lists" example are provided, along with their drawbacks:

      Workaround 1:

      List<List<?>> list = new ArrayList<List<?>>();

      Drawback: Sacrifices compile-time type safety. Cannot guarantee at compile time that "list" will contain only serializable elements, and hence cannot guarantee serializability of the list.

      Workaround 2:

      List<Serializable> list = new ArrayList<List<?>>();

      Drawback: Sacrifices compile-time type safety. Cannot guarantee that some broken piece of code hasn't put some other serializable object into the collection which does not implement List. Will need to use casts to convert to List references (effectively back to the same problem that we had before we had generic Lists).

      Workaround 3:

      interface SerializableList<T> extends List<T>, Serializable {}
      class SerializableArrayList<T> extends ArrayList<T> implements SerializableList<T> {}

      List<SerializableList<?>> list = new ArrayList<SerializableList<?>>();
      list.add(new SerializableArrayList<String>());

      Drawback: Cannot be retrofitted to arbitrary existing serializable List implementations (eg, ArrayList, LinkedList, custom serializable lists). Need to create "union interfaces" and wrapper subclasses as per my example. This is bulky and annoying.

            abuckley Alex Buckley
            abuckley Alex Buckley
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: