PROPOSAL: Method and Field Literals (original) (raw)
Jesse Wilson jesse at swank.ca
Tue Mar 10 19:49:47 PDT 2009
- Previous message: Fwd: Draft proposal: allow the use of relational operators on Comparable classes
- Next message: PROPOSAL: Method and Field Literals
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Rich text (preferred) here: http://docs.google.com/View?docID=dhfm3hw2_62dxg677jn
Proposal: Method and Field Literals
AUTHOR(S): Jesse Wilson
OVERVIEW
FEATURE SUMMARY: Add syntax for method and field literals. Enables compile-time checking of member references and avoids an impossible checked exception.
MAJOR ADVANTAGE: The Java language neatly balances dynamic behaviour with type safety. Many Java frameworks are successful because they leverage this balance. Class literals like ArrayList.class are popular because of the powerful APIs they enable.
Unfortunately methods and fields are missing literal syntax. As a consequence, code that reflects on a specific method or field is significantly more clumsy. The member must be referenced using a String, which hinders refactoring. Code that looks up specific members must cope with a checked exception that will never be thrown.
Adding literals for method and fields allows the compiler to catch typos earlier. IDEs will be able to find and fix member references that were previously obscured by Strings. Finally, it reduces the amount of boilerplate code to create and use frameworks.
MAJOR DISADVANTAGE: Like annotations and generics, member literals can be abused. Excessive use of reflection makes applications more difficult to maintain, and this proposal encourages reflection.
The member literal syntax is easily confused with regular dereferencing syntax, which can be a source of confusion.
ALTERNATIVES: Frameworks like EasyMock[1] obtain method references via invocation. Users invoke a factory method to construct a dynamic proxy, and invoke methods on that proxy to reference them. This approach is full of compromises and doesn't support fields, static methods, or final classes.
One can lookup methods using an identifying annotation. This requires a possibly-unwanted layer of indirection, and it cannot be used with third-party code.
One can obtain a method reference via its String name.
Instead of method and field literals it may be desirable to add property support to the JDK. This proposal does not preclude that.
EXAMPLES:
BEFORE: Method get; Method set; try { get = List.class.getMethod("get"); set = List.class.getMethod("set", Object.class); } catch (NoSuchMethodException e) { throw new AssertionError(); }
AFTER: Method get = List#get(); Method set = List#set(Object);
DETAILS
SPECIFICATION: The following new grammar rules are added:
Expression: MethodLiteral FieldLiteral ...
MethodLiteral: Typeopt # MethodName (TypeList)
FieldLiteral: Typeopt # FieldName
TypeList: Type [, TypeList]
If the type is omitted, the literal refers to a visible member in the current scope. This may be a member of the current type, of an enclosing type, or a statically imported member. The rules for resolution are the same as those for invocation.
COMPILATION: Each member reference would be replaced with a call to an internal desugaring API method. The helper method will handle runtime inheritance and visibility issues:
public class A { public void main(String[] args) { Method driveMethod = Corvette#drive(Direction,Speed); Field brakeField = Corvette#brake; } }
would be desugared to :
public class A { public void main(String[] args) { Method driveMethod = $Internal.methodLiteral( A.class, Corvette.class, "drive", Direction.class, Speed.class); Field brakeField = $Internal.fieldLiteral(A.class, Corvette.class, "brake"); } }
VISIBILITY: Unlike the java.lang.reflect() API, only visible methods may be referenced in literals. To illustrate:
// file A.java public class A { public String visible; private String secret; }
// file B.java { public class B { private String mine; private Field myField = B#mine; // ok private Field visibleField = A#visible; // ok private Field secretField = A#secret; // compile error, accessing a private method private Field nonexistentField = A#nonexistent; // compile error, no such field }
The standard rules of visibility will apply: If you can invoke a method, you can reference it. We will leverage the compiler's existing visibility behaviour to implement this.
INHERITANCE: When a literal references a member inherited from a supertype, the specific supertype method will be resolved at runtime. Although this adds additional runtime cost, it ensures that literals and invocations always have the same behaviour. For example, consider:
// file Car.java public class Car { public void honk() { System.out.println("honk"); } }
// file Corvette.java public class Corvette extends Car { }
// file Main.java public class Main { public static void main(String[] args) { Method honk = Corvette#honk(); System.out.println(honk.getDeclaringClass()); // prints "Car" } }
Now change the source code for Corvette.java to override the honk() method. Recompile only Corvette.java:
// file Corvette.java public class Corvette extends Car { public void honk() { System.out.println("beep"); } }
Now when we run Main.main(), it should print "Corvette" to illustrate that method resolution occurs at runtime.
STATIC DISPATCH: Java method overloading uses the compile-time type of method arguments to determine which method is invoked. During desugaring, the argument method's types are replaced with the compile-time types of the target method. This ensures that a reference and invocation always refer to the same overload, even if the target method's class is recompiled. For example: Method removeMethod = List#remove(String) is desugared to: Method removeMethod = $Internal.methodLiteral( A.class, List.class, "remove", Object.class);
STATIC RESOLUTION: Members must be specified fully using constant types. The following is not supported: Class<?> argument = ... Method remove = List#remove(argument);
MODIFIERS: This syntax applies equally to members with any modifier. Synthetic members are not supported.
RAW TYPES: The Method and Field classes are not parameterized types. This proposal does not preclude adding type parameters to these classes.
TESTING: It is necessary to test all combinations of visibility and inheritance on both fields and methods. Both success and failure cases need to be tested.
LIBRARY SUPPORT: It's necessary to add internal-use static methods to do runtime method resolution. The existing APIs Class.getMethod() and Class.getDeclaredMethod() are insufficient because they do not take the caller's visibility into account.
REFLECTIVE APIS: No changes to java.lang.reflect. The unreleased javac tree API will need expressions for member literals.
OTHER CHANGES: None.
COMPATIBILITY
BREAKING CHANGES: None.
EXISTING PROGRAMS: Classes that use this feature cannot be targeted to earlier JVMs.
DESIGN ALTERNATIVES: It is tempting to also support constructor literals, but coming up with an intuitive syntax is difficult. Some options: #ArrayList(Collection) ArrayList#(Collection) ArrayList#new(Collection)
REFERENCES [1] EasyMock: http://www.easymock.org/EasyMock2_4_Documentation.html
EXISTING BUGS:
- Previous message: Fwd: Draft proposal: allow the use of relational operators on Comparable classes
- Next message: PROPOSAL: Method and Field Literals
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]