PROPOSAL: Static Methods in Interfaces (v1.1)

Neal Gafter neal at gafter.com
Tue Mar 17 19:05:55 PDT 2009


Experience with extension methods have shown them to be a powerful and
more flexible alternative to this.  I believe extension methods are
also simpler.  If there is enough interest I could write it up.

On Tue, Mar 17, 2009 at 6:59 PM, Reinier Zwitserloot
<reinier at zwitserloot.com> wrote:
> Includes Joe D'Arcy's suggestions. An easier to read copy with markup
> is available at:
>
>     http://tinyurl.com/static-methods-in-interfaces
>
>
> The text below is identical aside from the formatting:
>
>
>   PROPOSAL: Static Methods in Interfaces
>
>
>     VERSION
>
>     This is version 1.1.
>     The latest version can be found at
>     http://tinyurl.com/static-methods-in-interfaces
>
>
>     Changes from v1.0 to v1.1: Added links to bug reports on
>     bugs.sun.com, expanded on alternatives, simplified reflective APIs,
>     added note on javadoc. (via Joe D'Arcy).
>
>
>     AUTHOR(S):
>
>     Reinier Zwitserloot
>     Roel Spilker
>
>
>     OVERVIEW
>
>     *FEATURE SUMMARY:*
>
>
>         Static methods are now allowed in interfaces. The static method
>         is defined with a method body in the interface and it exists in
>         the namespace of the interface; it does not imply that
>         implementing classes must implement the method. This feature is
>         especially useful for utility methods that belong with a given
>         interface. For example, many methods in java.util.Collections
>         are specific for a particular interface, such as sort
>         (java.util.List) and unmodifiableMap (java.util.Map).
>
>
>         MAJOR ADVANTAGE:
>
>         Allows maintaining code that 'goes together' in a single file,
>         and helps auto-complete dialogs come up with more relevant
>         operations on any given object. Also improves syntax by using a
>         more specific term (e.g. List) instead of a usually somewhat
>         generic grab bag utility class (e.g. Collections). Also allows
>         interfaces to contain pseudo-constructor, for common tasks and
>         default implementations (e.g. java.io.FileFilter could
> contain a
>         method to create a new one based on a file extension). This
>         proposal will also offer an easy solution to the current
>         deplorable situation that you need to call on 2 separate
> utility
>         classes, one of which has no relation to List whatsoever, just
>         to create an immutable List:
>         Collections.unmodifiableList(Arrays.asList(items)) can be
>         replaced with the much more elegant List.of(items)
>
>
>         MAJOR BENEFIT:
>
>         Java (the language) is very strictly namespaced; the default
>         package is discouraged and java does not allow dynamically
>         adding or changing methods at runtime (so called monkey
>         patching). Java also does not support mixins nor multiple
>         inheritance. Therefore, the platform currently lacks a way to
>         meaningfully offer utility methods for interfaces. Instead,
>         kludges such as java.util.Collections exist as a vehicle for
>         these support methods. Furthermore, static methods in
> interfaces
>         are currently illegal (see JLS Chapter 9.4
>         <http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.4
>  >)
>         so this proposed change does not complicate the language very
> much.
>
>
>         MAJOR DISADVANTAGE:
>
>         Confusion about the notion that static methods in java in java
>         are not 'virtual' (they are not inherited and cannot be
>         overridden) may cause a programmer to erroneously think a
> static
>         method in an interface implies it is something an implementing
>         class must implement. However, the mandatory method body should
>         help avoid confusion. Slightly more complex language
>         specification. No opportunity to use the static keyword in
>         interfaces for some sort of /factory interface/ concept (an
>         interface for constructors and static methods). The proposed
>         implementation is also somewhat inconsistent in rare cases
>         compared to static methods in classes. While this inconsistency
>         is a disadvantage, the authors do not believe there's a way to
>         avoid this inconsistency without creating more serious
> disadvantages
>
>         ALTERNATIVES:
>
>         The usual solution to this problem right now is to offer a
>         separate utility class (a class that is not instantiable and
>         contains only static methods) that contain the utility methods,
>         along with a reference in the /javadoc/ of the interface to
> this
>         utility class. For example, java.util.Collections is the
> utility
>         class that goes with Map, List, Set and other Java Collections
>         API interfaces. The use case of default / common
> implementations
>         is currently handled by having an implementing class with a
>         constructor. For example, a new class called
>         java.io.ExtensionFileFilter could be made that takes a String
>         and implements FileFilter. The sugar employed by this proposal
>         is itself also an alternative: Creating a member type class
> that
>         contains the static methods (With just a backwards and
> migration
>         compatible API addition, you could make List.Utils.of(items)
>         work in java 1.6 notation (The Utils class is an inner member
>         type to the interface, which is legal, and as it is a class,
> may
>         contain static methods.
>
>         Another language change that can serve as an alternative is
>         extension methods (the ability to lexically 'monkey patch'
>         methods onto types, such as "import static
>         java.util.Collections.sort into java.util.List;"
>
>
>     EXAMPLES
>
>
>      SIMPLE EXAMPLE:
>
>         public interface Foo {
>             public static void printHello() {
>                 System.out.println("Hello, World!");
>             }
>         }
>
>         Foo.printHello();  //Prints 'Hello, World!
>
>
>       ADVANCED EXAMPLE:
>
>         package java.util;
>
>         public interface List<E> extends Collection<E> {
>             int size();
>             // List's other instance methods
>
>             public static <T> List<T> of(final T... items) {
>                 return new AbstractList<T>() {
>                     public T get(int index) {
>                         return items[index];
>                     }
>
>                     public int size() {
>                         return items.length;
>                     }
>                 };
>             }
>         }
>
>         List<String> list = List.of("foo", "bar");
>         assert list.get(0).equals("foo");
>         assert list.size() == 2;
>
>
>     DETAILS
>
>      SPECIFICATION:
>
>         Java Language Specification changes:
>
>         JLS Chapter 9.1.4
>         <http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.1.4
>  >:
>         original:
>
>         InterfaceMemberDeclaration:
>                ConstantDeclaration
>                AbstractMethodDeclaration
>                ClassDeclaration
>                InterfaceDeclaration
>                ;
>
>         replacement:
>
>         InterfaceMemberDeclaration:
>                ConstantDeclaration
>                AbstractMethodDeclaration
>                StaticMethodDeclaration
>                ClassDeclaration
>                InterfaceDeclaration
>                ;
>
>
>         JLS Chapter 9.4
>         <http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.4
>  >:
>         original:
>
>             Every method declaration in the body of an interface is
>             implicitly abstract, so its body is always represented by a
>             semicolon, not a block.
>
>         replacement:
>
>             Every non-static method declaration in the body of an
>             interface is implicitly abstract, so its body is always
>             represented by a semicolon, not a block.
>
>         original:
>
>             Note that a method declarated in an interface must not be
>             declared static, or a compile-time error occurs, because
>             static methods cannot be abstract.
>
>         replacement:
>
>             None - this line is removed from the JLS.
>
>
>         JLS Chapter 9.5
>         <http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.5
>  > and
>         9.6
>         <http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.6
>  >
>         are bumped to 9.6 and 9.7, respectively, and a new 9.5 is
> added:
>
> ------------------------------------------------------------------------
>
>
>             9.5 Static Method Declarations
>
>             StaticMethodDeclaration:
>              StaticMethodModifiers TypeParameters_opt  ResultType
>             MethodDeclarator Throws(opt)  ;
>
>             StaticMethodModifiers:
>              static
>              MethodModifiers static
>              static MethodModifiers
>
>         The MethodModifiers are described in 8.4.3
>         <http://java.sun.com/docs/books/jls/third_edition/html/classes.html#78188
>  >,
>         The access modifier |public| is discussed in 6.6
>         <http://java.sun.com/docs/books/jls/third_edition/html/names.html#104285
>  >.
>         A compile-time error occurs if the same modifier appears more
>         than once in an static method declaration. The static keyword
> is
>         mandatory.
>
>
>             Static method declarations in interfaces are either public
>             or private; protected and package private are not allowed.
>             If no access modifier is specified, the static method is
>             implicitly public, to be consistent with the notion that
>             everything else in an interface, be it a field, an abstract
>             method, or a member type, is implicitly public. Private
>             static methods are allowed to accommodate helper
>             methods. Other than being limited to public and private
>             access, a static interface method is identical to a method
>             declaration in a class (8.4)
>             <http://java.sun.com/docs/books/jls/third_edition/html/classes.html#40420
>  >.
>             During compilation, all static methods are stored in a
>             synthetic inner class called $Methods, which is generated
>             with a private constructor. If that class already exists in
>             the source file, a compile-time error occurs.
>
>
> ------------------------------------------------------------------------
>
>         JLS Chapter 15.12.1
>         <http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1
>  >:
>
>         The specification of method invocation forms that invoke static
>         methods are updated to refer to 'class or interface' instead of
>         just 'class', and the following line:
>
>             If /TypeName/ is the name of an interface rather than a
>             class, then a compile-time error occurs, because this form
>             can invoke only |static| methods and interfaces have
>             no |static| methods.
>
>
>         is replaced with:
>
>             If /TypeName/ is the name of an interface rather than a
>             class, then the call is presumed to be for a static method
>             in a synthetic member class of the interface, called
>             $Methods. If the method exists, the class to be searched is
>             denoted by /TypeName.$Methods/ where /$Methods/ is a static
>             inner class of the interface /TypeName/, literally called
>             "$Methods".
>
>
>     COMPILATION:
>
>         Any interface with static methods is sugared by creating a
>         member class called $Methods which will contain the static
>         methods. The generated $Methods class should also have a
> private
>         constructor, as they aren't supposed to be instantiable. If the
>         $Methods inner class has been explicitly created in the source
>         file, any static methods in the interface are considered a
>         compile-time error (if the class is present in the source file,
>         the assumption is that the programmer wants to keep manual
>         control of the static methods). For method invocations,
>         currently, an invocation of the form InterfaceName.method(),
>         will result in an immediate compile error. The compiler will
>         instead need to search the $Methods class (if any exists) for
>         the method, and rewrite the call to
>         InterfaceName.$Methods.method() if the method does exist in the
>         $Methods inner class.
>
>         The method is not inherited by any member types. So, the second
>         'hello()' call in the following example would result in a
>         compile time error (method not found):
>
>             public interface Foo {
>                 public static void hello() {
>                     System.out.println("Hello, World!");
>                 }
>             }
>
>             public class Bar implements Foo {}
>
>             Foo.hello(); //works
>             Bar.hello(); //does not work
>
>
>         This is an unfortunate inconsistency (if Foo was a class,
>         Bar.hello() would work just fine), but there is no way to
>         adequately recreate this inheritance system with syntax sugar.
>         Even with a more thorough solution (that involves changing the
>         JVM), allowing inheritance of the methods would mean allowing
>         diamond relations (where 1 class implements 2 interfaces, that
>         each have the same static method signature with different
>         implementations. Which one is chosen?). This is principally the
>         reason why this proposal suggests not letting the method be
>         inherited. However, if inheritance is deemed important, the
>         alternate solution is to fix the JVM Specification chapter 2.13
>         <http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#16432
>  > in
>         similar ways as the JLS, and make static methods legal in
>         interfaces. The Type.method() invocation would then require a
>         much broader search and should give up with a compile-time
> error
>         if a diamond relation is found, listing the conflicting
>         implementations and asking the programmer to choose one.
>
>         As this proposal breaks the norm on inheritance already, the
>         following syntax is not allowed either, which for static
> methods
>         in classes *is* currently legal but flagged as a warning in all
>         popular IDEs and code style checkers:
>
>             Character c = 'f';
>             c.isWhiteSpace(' '); //if Character was an interface, this
>             would not be legal.
>
>
>
>     TESTING:
>
>         This new feature can be tested by applying the existing tests
>         for static method calls to static methods in interfaces.
>         Compilation and class file parsing needs to be expanded to
> apply
>         tests to read method bodies in interfaces as well as classes.
>         "Finding" the static method inside the $Methods inner class
>         during compilation needs to be tested, which is straight
>         forward. The changes described below to the reflective APIs
> also
>         need testing, which is also straight forward.
>
>
>     LIBRARY SUPPORT:
>
>         No library support is needed. However, it would be advisable to
>         update various interfaces in the core java APIs with useful
>         static utility methods. Some examples:
>
>             * java.util.List/Map/Set: All methods in
>               java.util.Collections should also be made available on
> the
>               appropriate java collections API interface.
>             * java.io.Closeable: should contain a utility method
>               'closeAndIgnoreException' (arguably better suited on
>               InputStream instead).
>             * java.util.List/Set: Should contain an 'of' method that
>               makes unmodifiable lists and sets via varargs.
>             * java.io.FileFilter: Should contain an
>               'ofExtension(String)' method that creates a FileFilter
> for
>               the provided extension.
>
>
>     REFLECTIVE APIS:
>
>         Currently, synthetic members are not treated specially by the
>         reflection API. Therefore, this proposal does not require any
>         reflective API changes. Attempting to use reflection to access
>         these methods requires you to access them via the generated
>         $Methods member. Asking for such a method's parent class would
>         return the $Methods class, not the interface.
>
>
>     OTHER CHANGES:
>
>         The javadoc tool will need a minor change: It will need to
>         produce a new section on static methods for interfaces, if they
>         exist. (Unlike reflection, javadoc should abstract away the
>         inner $Methods class). Fortunately, this code already exists
> for
>         printing static methods in normal classes.
>
>
>     MIGRATION:
>
>         No migration is needed. However, any java projects that
>         currently employ utility classes (defined as having a private
>         constructor that is not called anywhere in scope) which either
>         return interface types, or take as first parameter an interface
>         type, or both, where all previously mentioned interfaces are in
>         the same package, are likely candidates for moving or copying
> to
>         the relevant interface. Thus, IDEs can offer refactor advice to
>         perform this task automatically and to find likely candidates.
>         Such a refactor tool would for example identify all methods in
>         java.util.Collections.
>
>
>     COMPATIBILITY
>
>     BREAKING CHANGES:
>
>         Existing source that already uses an inner type named $Methods
>         in an interface will change semantics when this proposal is
>         implemented, primarily when queried via reflection. Between the
>         vanishingly small odds of both a $Methods already existing and
>         its methods being queried by the reflection API, and the
> general
>         rule that $ should only be used in type names by compilers, the
>         potential breaking change is hardly worth mentioning.
>
>
>     EXISTING PROGRAMS:
>
>         Existing programs are not affected by this change, other than
> as
>         described above in the 'breaking changes' section.
>
>
>     REFERENCES
>
>     EXISTING BUGS:
>
>         http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4093687
> (Main RFE)
>         http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4291381
>         (Duplicate)
>         http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4491759
>         (Duplicate)
>         http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4306573
>         (Duplicate)
>
>
>     URL FOR PROTOTYPE (optional):
>
>         None.
>
>
>
>
>



More information about the coin-dev mailing list