PRE-PROPOSAL: Simple operator overloading.

rssh at gradsoft.com.ua rssh at gradsoft.com.ua
Wed Mar 25 04:23:45 PDT 2009


Permanent URL: http://docs.google.com/Doc?id=dhhvggr8_18djp85shk

Probably too big, but anyway.

As I remember, alternative proposal (made [] and []= syntax sugar on
well-known interfaces, as with for-each loop) still not submitted. If
anybody want submit such proposal, then it is possible to reuse
JLS-changed for 15.26.1 from there.


OVERVIEW:
 FEATURE SUMMARY:

   Add possibility to overloaded common subset of java operators for own
classes.

 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.

 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<T extends Named>
{

  IndexedCollection(int capacity)
  { byNames=new T[capacity];
    byNames=new TreeMap<String,T>();
  }

  @Operator("[]");
  public T get(int i)
  {  return byIndexes.get(i); }
  @Operator("[]");
  public T get(String name)
  {  return byNames.get(i); } }

  @Operator("[]=");
  public void set(int i, T t)
  {
     byIndexes[i]=t;
     byNames[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<NVPair>  pairs = new IndexedCollection<NVPair>(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:

 Nothing advanced here. All simple.


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();
}

 Add to JLS chapter 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.

To chapter 15.13, instead:

 The type of the array reference expression must be an array type (call it
T[], an array whose components are of type T) or a compile-time error
results.

 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.

15.13.1 Runtime Evaluation of Array Access
Instead:

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, 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.)


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 object
                + 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) except
check for name of method step.


15.15.5 Bitwise Complement Operator ~
The type of the operand expression of the unary ~ operator must be a type
that is convertible (&#167;5.1.8) to a primitive integral type, or class,
which have 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 (&#167;) 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

  Otherwise, if exists methods for operand class, annotated by
@Operator("~"), 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 or class, which have non-static member functions 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
(&#167;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 (&#167;5.1.8) to a primitive numeric type, or 
type of first operand must be a class, which have non-static 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 (&#167;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
(&#167;5.1.8) and value set conversion (&#167;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,  with
value of opeeand annotations instean name of 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 (&#167;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 (&#167;5.1.8) to a primitive numeric type,
or a compile-time error occurs.

Otheriwise, the type of first operand must be a class with 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 (&#167;15.11) or an
array access (&#167;15.13). The type of the assignment expression is the
type of the variable after capture conversion (&#167;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 (&#167;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
(&#167;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
(&#167;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 (&#167;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 (&#167;5.2) with the actual type SC of the
array component. This check is similar to a narrowing
cast (&#167;5.5, &#167;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, appropriateve method, annotated with
@Operator("[]=") is called on first operand of array
reference with index subexpression and righ hand operators
as reference.
    * 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 assignment evaluates exactly as
invocation expression of appropriative 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<NVPair>  pairs = new IndexedCollection<NVPair>(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<NVPair>  pairs = new IndexedCollection<NVPair>(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

   Provide usual set of tests. Nothing special

LIBRARY SUPPORT:

   Annotate standard Collection classes;  BigDecimal ans  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
 (no)







More information about the coin-dev mailing list