JDK-8061410 : Multiply-bounded reference type expressions
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Priority: P4
  • Status: Closed
  • Resolution: Other
  • Submitted: 2005-11-15
  • Updated: 2014-10-17
  • Resolved: 2014-10-17
Related Reports
Relates :  
Relates :  
Relates :  
Description
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.

Comments
The 'specification' component of the Java Bug System is for reporting technical errors and ambiguities in the text of The Java Language Specification and The JVM Specification. It is not the venue to propose new features in the Java language or JVM. Ongoing feature development is carried out in OpenJDK (http://openjdk.java.net/jeps/); corresponding enhancements to The Java Language Specification and The JVM Specification are managed through the Java Community Process (http://jcp.org/).
17-10-2014

EVALUATION The proposal is to allow an intersection type (JLS4.9) to be denoted if it takes exactly the form permitted as a type variable bound (JLS4.4). (Note there is no dependency on generics; an intersection type could easily represent non-generic class+interfaces.) It is fundamentally a good idea but not top of the list for type system enhancements at this time. (Regarding "2. The classes and interfaces specified in the ReferenceMultiType should be compatible" - this is already described by the override-equivalence requirements on declared/inherited methods. Such requirements are relevant because by JLS4.9, an intersection type inherits everything in its direct superclass and superinterfaces, which would be those denoted by a ReferenceMultiType.) (Another workaround is to use generic methods, e.g. <T extends List<?> & Serializable> writeSerializableList(T t) {...})
06-11-2006