PROPOSAL: Simple Operator Overloading (original) (raw)
rssh at gradsoft.com.ua rssh at gradsoft.com.ua
Sun Mar 29 06:54:27 PDT 2009
- Previous message: Submission: switch (...) instanceof feature
- Next message: Ergonomic Issues with ARM blocks
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Permanent URL: http://docs.google.com/Doc?id=dhhvggr8_18djp85shk
Proof of concept implementation: http://code.google.com/p/jsoo-coin/
Submission as text:
OVERVIEW: FEATURE SUMMARY:
Add possibility to overloaded common subset of operators for java classes. Submission define @Operator method annotation for methods, which can be called as operators in classes and interfaces.
MAJOR ADVANTAGE:
In many cases (especially for Collections and own subtypes of numeric) using operator syntax is more clear and simple, than call by name of method.
This proposal allows use operator ovlerloading, and at same time avoid complexity by defining operator overloading as thin layer on top of usual method calls.
MAJOR DISADVANTAGE
Inaccurate usage of this feature can cause producing of code which hard to read. Compiler is slightly complex then before.
ALTERNATIVES:
Live without operators as before.
EXAMPLES
SIMPLE EXAMPLE:
Example 1 import javax.lang.Operator;
public class Z3 { public Z3(int value) { this.value = (value%3); }
public Z3(Z3 z3) { this.value = z3.value; }
@Operator("+") public Z3 plus(Z3 x) { return new Z3(value + x.value); }
@Operator("+=") public Z3 plusAssign(Z3 x) { value=((value+x.value)%3); }
@Operator("-") public Z3 minus(Z3 x) { return new Z3(value-x.value); }
@Operator("-=") public Z3 minusAssign(Z3 x) { value=((value-x.value)%3); }
private int value; }
Usage:
Z3 z3 = new Z3(2); Z3 z4 = z3+z3;
Example 2
public interface Named { public String getName(); }
public class IndexedCollection {
IndexedCollection(int capacity) { byIndexes=new T[capacity]; byNames=new TreeMap<String,T>(); }
@Operator("[]") public T get(int i) { return byIndexes[i]; }
@Operator("[]") public T get(String name) { return byNames.get(name); }
@Operator("[]=") public void set(int i, T t) { byIndexes[i]=t; byNames.put(t.getName(),t); }
private T[] byIndexes; private Map<String,T> byNames; }
Usage:
class NVPair extends Named { public NVPair(String name, Object value) { this.name = name; this.value = value; }
public String getName() { return name; }
public String getValue() { return value; }
private String name; private String value; }
IndexedCollection pairs = new IndexedCollection(10); pairs[0]=new NVPair("CISCO-AV-Pair","lcp:interface=1"); pairs[1]=new NVPair("X",4); pairs[2]=new NVPair("Y",5);
NVPair pair1=pairs[1]; NVPair pair2=pairs["X"];
ADVANCED EXAMPLE:
public interface StringAppendable { @Operator("+=") public void append(String x); }
public class X implements StringAppendable { .... public void append(String x) { value+=x; } }
X x; x+="qqq";
public interface X { @Operator("+="); public void addZ(Z z) }
public interface Y { @Operator("+="); public void plusZ(Z z) }
public class XY implements X, Y { public void addZ(Z z); public void plusZ(Z z); }
XY xy; xy+=z; // compile-time error. (ambiguity of operator call resolution)
DETAILS:
Add to Java Library @Operator method annotation with next signature:
package java.lang.Annotation
@Documented @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface Operator { public String value(); }
JLS changed.
Add 9.6.1.7 about Operator annotation:
The annotation type annotation.Operator(v) is used to indicate, that annotated method can be shortcated as operator in program test.
v is String, which can be one of: ( "[]", "[]=", "+", "-", "~", "!", "", "/", "%", "+=", "-=", "=", "/=", %= )
Signature of operator method must be compatible with signature of operator, which means that methods, annotated as binary operators must be nonstatic member functions with one argument; unary operators - as nonstatic member functions without arguments.
Add to 15.12.1. Compile-Time Step 1: Determine Class or Interface to Search Case for search of method for operator expression:
If the form is OperatorExpression then classe for search is first
operand of expression. Change 15.12.2.1 Identify Potentially Applicable Methods to A member method is potentially applicable to a method invocation if and only if all of the following are true:
* Names of operator or method are matched, i.e.
o When analyzed invocation is method invocation by name:
+ the name of the member is identical to the name of the
method in the method invocation. o if method invocation is call of overloaded operator, + method is annotated with @Operator annotations with operator name as in operator expression or + method is override or implements method in superclass or superinterfaces, annotated with @Operator annotation with the name same as in operator expression. * The member is accessible (§6.6) to the class or interface in which the method invocation appears. * The arity of the member is lesser or equal to the arity of the method invocation. * If the member is a variable arity method with arity n, the arity of the method invocation is greater or equal to n-1. * If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n. * If the method invocation includes explicit type parameters, and the member is a generic method, then the number of actual type parameters is equal to the number of formal type parameters.
To chapter 15.13 to next form:
The type of the array reference expression must be an array type (call it T[], an array whose components are of type T) or type of class with methods, annotated by @Operator("[]") or @Operator("[]=") annotations(call one class array reference expression) or a compile-time error occurs.
Chapter 15.13.1 (Runtime Evaluation of Array Access) to next form:
An array access expression is evaluated using the following procedure:
* First, the array reference expression is evaluated. If this
evaluation completes abruptly, then the array access completes abruptly for the same reason and the index expression is not evaluated. * Otherwise, o if array reference is array + the index expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason. + Otherwise, if the value of the array reference expression is null, then a NullPointerException is thrown. + Otherwise, the value of the array reference expression indeed refers to an array. If the value of the index expression is less than zero, or greater than or equal to the array's length, then an ArrayIndexOutOfBoundsException is thrown. + Otherwise, the result of the array access is the variable of type T, within the array, selected by the value of the index expression. (Note that this resulting variable, which is a component of the array, is never considered final, even if the array reference expression is a final variable. o if array reference is class-array-reference exception + if exists method, annotated as @Operator("[]=") and if such expression is situated at the left part of plain assigment expression X=Y, than this subexpression does not evaluated itself, instead components of array access expression take part in process of assigment. + otherwise, if exists set of methods for such class, annotated by @Operator("[]"), than better candidate from set of appropriative annotated methods is called, as specified in 15.12 (Method Invocation Expression).
15.15.5 Bitwise Complement Operator ~ The type of the operand expression of the unary ~ operator must be a type that is convertible (§5.1.8) to a primitive integral type, or class, which declare or override or implement non-static member functions without arguments, annotated by @Operator("~") , or a compile-time error occurs. If operand expression is convertable to a primitive integram type, than
* Unary numeric promotion (§) is performed on the operand. The
type of the unary bitwise complement expression is the promoted type of the operand.
* At run time, the value of the unary bitwise complement expression is
the bitwise complement of the promoted value of the operand; note that, in all cases, ~x equals (-x)-1
If operand expression is class, where methods annotated by @Operator("~") are accessible, than appropriative methods is called, as specified in 15.12 ( except check for name of method step) and result type is type of method invocaton.
15.15.6 Logical Complement Operator ! The type of the operand expression of the unary ! operator must be boolean or Boolean, or class, which declare, override or implements function without arguments, annotated by @Operator("!") , or a compile-time error occurs. When type of the operand expression is boolean or Boolean, than
* type unary logical complement expression is boolean,
* At run time, the operand is subject to unboxing conversion
(§5.1.8) if necessary; the value of the unary logical complement expression is true if the (possibly converted) operand value is false and false if the (possibly converted) operand value is true
When type of the operand expression is class, which have non-static member functions without arguments, annotated by @Operator("!") than expression is processed as invocation of appropriative method.
15.17 Multiplicative Operators The operators *, /, and % are called the multiplicative operators. They have the same precedence and are syntactically left-associative (they group left-to-right).
MultiplicativeExpression:
UnaryExpression
MultiplicativeExpression * UnaryExpression
MultiplicativeExpression / UnaryExpression
MultiplicativeExpression % UnaryExpression
The type of each of the operands of a multiplicative operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or type of first operand must be a class, which contains (or implements or override) accessible methods with one argument, annotated by @Operator("*" or "/" or "%"), or compile-time error occurs.
In case of types, convertible to promitive numeric type: Binary numeric promotion is performed on the operands (§5.6.2). The type of a multiplicative expression is the promoted type of its operands. If this promoted type is int or long, then integer arithmetic is performed; if this promoted type is float or double, then floating-point arithmetic is performed.
Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
Chapter 15.17.1, 15.17.2, 15.17.3 just rename to Primitive Numeric Multiplication Operator, Primitive Numeric Division Operator and Primitive Numeric Remind operator.
Add 15.17.4 Multiplicative Operators for class with operator-annotated methods.
In case when type of first operand is class with method, annotated by appropreative annotations, than expression is processed as invocation expression ($15.2) from set of appropriative annotated methods.
15.18 Additive Operators
The operators + and - are called the additive operators. They have the same precedence and are syntactically left-associative (they group left-to-right).
AdditiveExpression:
MultiplicativeExpression
AdditiveExpression + MultiplicativeExpression
AdditiveExpression - MultiplicativeExpression
If the type of either operand of a + operator is String, then the operation is string concatenation.
Otherwise, the type of each of the operands of the + operator must be a type that is convertible (§5.1.8) to a primitive numeric type. In every case, the type of each of the operands of the binary - operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.
Otheriwise, the type of first operand must be a class, which contains or override or implement nonstatic member function with one argument, annotated by @Operator("+" or "-") or compile-time error occurs.
add 15.18.3 Additive Operators for class with operator-annotated methods:
In case when type of first operand is class with method, annotated by appropreative annotations, than expression is processed as invocation expression from set of appropriative annotated methods.
15.26 There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.
AssignmentExpression:
ConditionalExpression
Assignment
Assignment:
LeftHandSide AssignmentOperator AssignmentExpression
LeftHandSide:
ExpressionName
FieldAccess
ArrayAccess
AssignmentOperator: one of
= *= /= %= += -= <<= >>= >>>= &= ^= |=
The result of the first operand of an assignment operator must be a variable, or class array reference expression ($15.3), or a compile-time error occurs. Variable operand may be a named variable, such as a local variable or a field of the current object or class, or it may be a computed variable, as can result from a field access (§15.11) or an array access (§15.13). The type of the assignment expression is the type of the variable after capture conversion (§5.1.10).
15.26.1 Simple Assignment Operator = A compile-time error occurs when
* left hand operand expression is a value and the type of the
right-hand operand cannot be converted to the type of the variable by assignment conversion (§5.2) * left hand operand expression is class array reference expression and type of right-hand operator cannot be converted to the type of second argument for one of methods, annotated with @Operator("[]=") annotation.
At run time, the expression is evaluated in one of three ways:
* If the left-hand operand expression is a field access expression
(§15.11) e.f, possibly enclosed in one or more pairs of parentheses, then: o First, the expression e is evaluated. If evaluation of e completes abruptly, the assignment expression completes abruptly for the same reason. o Next, the right hand operand is evaluated. If evaluation of the right hand expression completes abruptly, the assignment expression completes abruptly for the same reason. o Then, if the field denoted by e.f is not static and the result of the evaluation of e above is null, then a NullPointerException is thrown. o Otherwise, the variable denoted by e.f is assigned the value of the right hand operand as computed above. * If the left-hand operand is an array access expression (§15.13), possibly enclosed in one or more pairs of parentheses, then: * o If array reference expression is array type + First, the array reference subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no assignment occurs. + Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no assignment occurs. + Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs. + Otherwise, if the value of the array reference subexpression is null, no assignment occurs and a NullPointerException is thrown. + Otherwise, the value of the array reference subexpression indeed refers to an array. If the value of the index subexpression is less than zero, or greater than or equal to the length of the array, then no assignment occurs and an ArrayIndexOutOfBoundsException is thrown. + Otherwise, the value of the index subexpression is used to select a component of the array referred to by the value of the array reference subexpression. This component is a variable; call its type SC. Also, let TC be the type of the left-hand operand of the assignment operator as determined at compile time. + If TC is a primitive type, then SC is necessarily the same as TC. The value of the right-hand operand is converted to the type of the selected array component, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the array component. + If TC is a reference type, then SC may not be the same as TC, but rather a type that extends or implements TC. Let RC be the class of the object referred to by the value of the right-hand operand at run time.
The compiler may be able to prove at compile time that
the array component will be of type TC exactly (for example, TC might be final). But if the compiler cannot prove at compile time that the array component will be of type TC exactly, then a check must be performed at run time to ensure that the class RC is assignment compatible (§5.2) with the actual type SC of the array component. This check is similar to a narrowing cast (§5.5, §15.16), except that if the check fails, an ArrayStoreException is thrown rather than a ClassCastException. Therefore:
# If class RC is not assignable to type SC, then no
assignment occurs and an ArrayStoreException is thrown. + Otherwise, the reference value of the right-hand operand is stored into the selected array component. o If array reference expression is class reference expression ($15.13), than + First, the first operand of array reference expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no array assigmnent methods are called. + Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no array assignment methods are called. + Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no call of array assignment method occurs. + Otherwise, invocation of appropriateve method, (which annotated by @Operator("[]=") or override or implement methods, annotated by Operator("[]=")) is called on first operand of array reference with index subexpression and righ hand operators as reference. + If appropriative method is not found, than compile-time error occurs. (to prevent call single assigment on result of method, annotated by Operator("[]") * Otherwise, three steps are required: o First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs. o Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
15.16.2 Compound Assignment Operators A compound assignment expression of the form E1 op= E2 is when
* type of E1 is Primitive Numeric Types or Booleans or String, then
this is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once. * type of E1 is Class, which have methods m, annotated by @Operator("op="), than compound assigment evaluates exactly as invocation expression of appropriatev methods, with method names.
COMPILATION:
During compilation overloaded operators will be translated to set of appropriative annotated methods. I.e. for
Example 1
Z3 z3 = new Z3(2); Z3 z4 = z3+z3;
which will be translated to:
Z3 z3 = new Z3(2); Z3 z4 = Z3.plus(z3,z3);
Example 2
IndexedCollection pairs = new IndexedCollection(10); pairs[0]=new NVPair("CISCO-AV-Pair","lcp:interface=1"); pairs[1]=new NVPair("X",4); pairs[2]=new NVPair("Y",5);
NVPair pair1=pairs[1]; NVPair pair2=pairs["X"];
will be equal to:
IndexedCollection pairs = new IndexedCollection(10);
pairs.set(0,new NVPair("CISCO-AV-Pair","lcp:interface=1")); pairs.set(1,new NVPair("X",4)); pairs.set(2,new NVPair("Y",5));
NVPair pair1=pairs.get(1); NVPair pair2=pairs.get("X");
TESTING
Implementation must provide set of tests for each of operators and possible confilcts for overriding/implementation and chained invocations.
LIBRARY SUPPORT:
@Operator annotation must be added to standard library.
Also, not required but be practical to annotate standard Collection classes; BigDecimal and BigInteger.
REFLECTIVE APIS: None
OTHER CHANGES: None
MIGRATION: None
COMPABILITY None
REFERENCES
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4905919
IMPLEMENTATION PROTOTYPE URL
Proof of concept implementation, implemented as preprocessor, is published at http://code.google.com/p/jsoo-coin/
- Previous message: Submission: switch (...) instanceof feature
- Next message: Ergonomic Issues with ARM blocks
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]