Proposal: Collection Literals
Neal Gafter
neal at gafter.com
Mon Mar 30 17:40:31 PDT 2009
Josh-
This approach precludes some designs for reifying generics in the
future because it is defined by the implicit creation of non-reified
interfaces (e.g. List, etc). It isn't clear if we'll ever add
reification, but it would be a shame to restrict or remove our option
to do so. You can avoid this by not implicitly tying language
constructs to particular existing interfaces.
Regards,
Neal
On Mon, Mar 30, 2009 at 4:30 PM, Joshua Bloch <jjb at google.com> wrote:
> Folks,
>
> Hi. I'd like to throw one more proposal into the Coin purse. This proposal
> is intended to coexist with indexing access syntax for lists and maps. It
> is similar to Shams Mahmood Imam's Concise Collection Initialization Syntax
> proposal, but represents a different point in the design space. It is safer
> (in that it only produces immutable collections) and more concise (in that
> you need not specify the implementation type). It is likely to be higher in
> space and time performance, in that the compiler can choose efficient
> implementations for small collections (such as empty collections and
> singletons). On the other hand, it is less flexible, in that you
> cannot specify
> the implementation type.
> The proposal can be found here:
> http://docs.google.com/Doc?id=ddv8ts74_4cbnn5mhj , and is reproduced below
> as required by the submission guidelines.
>
> Regards,
>
> Josh
>
> Collection Literals
>
> *AUTHOR: *Joshua Bloch
>
>
> *OVERVIEW*
>
>
> FEATURE SUMMARY: Add support for immutable List, Set, and Map literals, with
> a syntax similar to that of array initializers.
>
>
> MAJOR ADVANTAGE: Reduces verbosity and adds clarity when creating small
> lists, sets, and maps and whose contents are known at compile time. This
> feature is well-liked in the many languages that support it (e.g., Perl,
> Python).
>
>
> MAJOR DISADVANTAGE: The facility can be abused to simulate named parameters,
> resulting in loss of type safety. Hopefully people won't do this.
>
>
> ALTERNATIVES: Typically programmers create an empty collection and insert
> the initial elements or mappings. Sometimes they use Arrays.asList. It is
> also possible to abuse anonymous classes to reduce verbosity ("crazybob's
> contraption"). Arguably the current best practice for creating immutable
> collections is to use the builder pattern, as demonstrated by the immutable
> collections in the Google
> Collections<http://code.google.com/p/google-collections/>
> project.
>
>
> *EXAMPLES*
>
>
> SIMPLE EXAMPLES: Here is a code fragment to create an immutable list as it
> would be done today:
>
>
> final List<Integer> piDigits =
> Collections.unmodifiableList(Arrays.asList(
>
> 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9 ));
>
>
> The method invocations (Collections.unmodifiableList(Arrays.asList) are just
> noise. With list literals, it would look like this:
>
> final List<Integer> piDigits = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9];
>
>
> By substituting curly braces for the square brackets, you get a set literal
> in place of a list:
>
>
> final Set<Integer> primes = { 2, 7, 31, 127, 8191, 131071, 524287 };
>
>
> Here is the empty set literal:
>
>
> Set<Senator> honestSenators = {};
>
>
> Here is a code fragment to create an immutable map as it would be done
> today:
>
>
> final Map<Integer, String> platonicSolids;
>
> static {
>
> solids = new LinkedHashMap<Map<Integer, String>;
>
> solids.put(4, "tetrahedron");
>
> solids.put(6, "cube");
>
> solids.put(8, "octahedron");
>
> solids.put(12, "dodecahedron");
>
> solids.put(20, "icosahedron");
>
> platonicSolids = Collections.immutableMap(solids);
>
> }
>
>
> Here’s how it would look with map literals:
>
>
> final Map<Integer, String> platonicSolids = { 4 : "tetrahedron",
>
> 6 : "cube", 8 : "octahedron", 12 : "dodecahedron", 20 : "icosahedron"
> };
>
>
> Here is the empty map literal, which has a slightly irregular syntax to make
> it differ from the empty set:
>
>
> Map<String, Integer> noJokeHere = { : };
>
>
> In all cases, the iteration order of collection literal corresponds to the
> order in which the elements or keys appear in the code. Collections
> literals are serializable (assuming their elements, keys or values are
> serializable). List literals implement java.util.RandomAccess (dynamically,
> but not statically).
>
>
> COMPLEX EXAMPLES: Here is a nested list literal:
>
>
> List<List<Integer>> pascalsTriangle =
>
> [[1],
>
> [1, 1],
>
> [1, 2, 1],
>
> [1, 3, 3, 1],
>
> [1, 4, 6, 4, 1]];
>
>
> Note that the element type of the list literal (and the key and value types
> of the map literal) are inferred by the compiler. As is the case when
> invoking parameterized methods, the compiler cannot always infer the desired
> type. For example this list literal will not compile, as the compiler will
> infer the wrong element type:
>
>
> final List<Number> numbers = [ 2, 2.718281828 ];
>
>
>
> (Instead of Number, the compiler will infer Number & Comparable<? extends
> Number & Comparable<?>>.) Therefore, we provide a similar "escape hatch"
> that lets you specify the element (or key and value) types manually. Like
> its method invocation analogue, it isn't pretty, and programmers will rarely
> have to use it in practice:
>
>
> final List<Number> numbers = *<Number>* [ 2, 2.718281828 ];
>
>
> *DETAILS*
>
> * *
>
> SPECIFICATION: What follows is not intended to be a formal JLS-quality
> specification. It emphasizes brevity and clarity over thoroughness.
>
>
>
> SYNTAX: Two new expression types would be defined in JLS 15, *List Literals*
> , *Set Literals*, and *Map Literals*:
>
>
>
> *ListLiteral*:
>
> *NonWildTypeArgument**opt** [* *List**ElementInitializers**opt ] *
>
> *ListElementInitializers*:
>
> *Expression*
>
> *List**ElementInitializers ,* *Expression*
>
> *NonWildTypeArgument:*
>
> * *< *ReferenceType *>
>
>
> *SetLiteral*:
>
> *NonWildTypeArgument**opt** *{ *SetElementInitializers**opt } *
>
> *SetElementInitializers*:
>
> *AugmentedConstant**Expression*
>
> *SetE**lementInitializers ,* *AugmentedConstant**Expression*
>
> *NonWildTypeArgument:*
>
> * *< *ReferenceType *>
>
>
>
> *MapLiteral*:
>
> *NonWildTypeArgumentPair**opt** *{ *Entry**Initializers** } *
>
> * NonWildTypeArgumentPairopt EmptyMapLiteral*
>
> *EntryInitializers*:
>
> *AugmentedConstant**Expression* : *Expression*
>
> Entry*Initializers ,* *EntryInitializer*
>
> *NonWildTypeArgumentPair:*
>
> * *<* ReferenceType , ReferenceType *>
>
> *EmptyMapLiteral*
>
> * *{ : }
>
>
> The following expression type would be added to (or immediately following)
> JLS §15.28, which currently defines the term constant expression, and the
> corresponding nonterminal symbol *ConstantExpression*:
>
>
> *AugmentedConstant**Expression:*
>
>
> An *augmented constant expression* is a generalization of a constant
> expression that allows several additional kinds of expressions:
>
>
>
> - enum constants (JLS §8.9)
> - class literals (JLS §8.9)
> - set literals (JLS 15.8.2)
> - lists literals all of whose element initializers are themselves
> augmented constant expressions
> - map literals all of whose value initializers are themselves augmented
> constant expressions
>
>
>
> SEMANTICS and COMPILATION: A list literal behaves roughly as if replaced by
> the following "desugaring":
>
>
>
> Arrays. *NonWildTypeArguments**opt* asUnmodifiableList( *
> ElementInitializers* )
>
>
>
> This desugaring assumes the existence of a new library method,
> asUnmodifiableList, described below under Library Support. Note that type
> inference of the element type for the list literal is exactly as if the
> following method were invoked with *ElementInitializers *as as actual
> parameters:
>
>
> <E> void foo(E... args) { }
>
>
>
>
> A set literal b*ehaves roughly as if replaced by the following desugaring:*
>
>
> Collections.unmodifiableSet(Arrays. *NonWildTypeArguments**opt* asList(
> *ElementInitializers* ))
>
>
> *It is a compile time error for a set literal to contain multiple equal
> element values.* Therefore, the size of the set (as reported by the
> size method)
> is the number of element initializers.
>
>
> In both desugarings above, the wiggle word "roughly" is used in that there
> is no guarantee that the indicated methods (Arrays.asUnmodifiableList,
> Collections.unmodifiableSet, Arrays.asList) methods are actually called.
>
>
> Moreover,* there **are no guarantees as to the implementation types of the
> collections produced by collection literals.* The compiler can call any
> public Java SE APIs to construct any List, Set, or Map implementations with
> the correct semantics (immutability, iteration order, serializability and,
> in the case of lists, RandomAccess). So, for example, if a list literal has
> no elements, the compiler can emit a call to Collections.emptyList. Similarly,
> if a list literal has a single element, the compiler can emit a call to
> Collections.singletonList. Many other such optimizations are possible, and
> new ones may become available in subsequent releases.
>
> A map literal behaves as if replaced by the following desugaring:
>
>
>
> Arrays. *NonWildTypeArguments**opt* #unmodifiableMap(
>
> * #gather( k0, k1, ... kn-1 ),*
>
> * #gather( v0, v1, ... vn-1 ))*
>
>
> where ki and vi are the keys and value expressions in the ith
> EntryInitializer in EntryInitializers, and #gather and #unmodifiableMap are the
> following hypothetical methods:
>
>
> private static <T> T[] #gather(T... args) {
>
> return args;
>
> }
>
>
> private static <K, V> Map<K, V> #unmodifiableMap(K[] keys, V[] vals) {
> Map <K, V> m = new LinkedHashMap<K, V>(2 * keys.length);
> for (int i = 0; i < keys.length; i++)
> m.put(keys[i], vals[i]);
> return Collections.unmodifiableMap(m);
> }
>
> The compiler does not actually emit calls to the methods #unmodifiableMap
> and #gather (which don't exist), but emulates their behavior. These methods
> are merely an artifice to describe the semantics of the construct, including
> type inference.
>
>
> *It is a compile time error for a map literal to contain multiple equal key
> values.* Therefore, the size of the map (as reported by the size method) is
> the number of entry initializers.
>
>
> TYPE SYSTEM: The proposal has no effect on the type system.
>
>
>
> TESTING: The proposed construct can be tested by writing list and map
> literals to generate lists and maps of various lengths (say 0 through 100)
> and types, and ensuring that these collections have the correct behavior by
> using preexisting tests for immutable collections, which are part of the Google
> Collections <http://code.google.com/p/google-collections/> project. (These
> tests will have to be modified slightly, but it will be straightforward.)
>
>
>
> LIBRARY SUPPORT: The following method (which is trivial to implement) will
> be added to java.util.arrays:
>
>
> /**
> * Returns a fixed-size, unmodifiable list backed by the specified
> array.
> * (Any changes to the array "write through" to the list.) Attempting
> to
> * modify the list will result in an {@link
> UnsupportedOperationException}.
> *
> * <p>The returned list is serializable and implements {@link
> RandomAccess}.
> *
> * @param a the array by which the list will be backed
> * @return an unmodifiable list view of the specified array
> */
> public static <T> List<T> asUnmodifiableList(T... a);
>
>
> The compiler may (but is not required to) emit calls to this method in the
> implementation of list literals. It may also be desirable to add some
> library support for immutable sets or lists (akin to that provided by the
> Google Collections project), but this isn't necessary for the proposal to go
> forward, and the compiler can take advantage of such support in whatever
> release it happens to be added.
>
> REFLECTIVE APIS: This proposal has no effect on core reflective APIs. The tree
> API inside javac<http://java.sun.com/javase/6/docs/jdk/api/javac/tree/index.html>
> would require a minor extension.
>
>
>
> OTHER CHANGES: No other parts of the platform need be to be updated.
>
>
>
> MIGRATION: Existing manual list, set, and map construction code could be
> replaced by more concise list and map literals, but this is not necessary.
>
>
>
> *COMPATIBILITY*
>
> * *
>
> BREAKING CHANGES: All previously valid programs remain valid, and their
> semantics is unaffected.
>
> * *
>
> EXISTING PROGRAMS: Source and class files of earlier versions are unaffected
> by the feature. No new overloadings or overridings can occur.
>
>
> *REFERENCES*
>
> * *
>
> EXISTING BUGS: 4632701 <http://bugs.sun.com/view_bug.do?bug_id=4632701>,
> 6237556 <http://bugs.sun.com/view_bug.do?bug_id=6237556>.
>
>
> *FUTURE DIRECTIONS*
>
>
> Assuming we make sure that no syntactic ambiguity would result, a future
> release could allow certain collection literals as annotation values. This
> is *not *proposed for Project Coin, but should inform the work.
>
>
More information about the coin-dev
mailing list