刚偶尔看到了[url=http://justjavac.iteye.com/]justjavac[/url]写的[url=http://justjavac.iteye.com/blog/175250]java解惑 - 半斤八两(一)[/url]和[url=http://justjavac.iteye.com/blog/175251]java解惑 - 半斤八两(二)[/url]。里面提到了Java的复合赋值运算符的两个陷阱:1) 复合赋值运算符有强制类型转换的语义;2) += 左侧必须是原始类型中的数字类型,或者是String类型。
JLS3e如是说:
[quote="Java Language Specification, 3rd Edition"][size=small][b]15.26.2 Compound Assignment Operators[/b][/size]
A compound assignment expression of the form [i]E1 op= E2[/i] is equivalent to [i]E1 = (T)((E1) op (E2))[/i], where T is the type of E1, except that E1 is evaluated only once. [/quote]
核心就这么一句,后面还有一堆细节规定,有兴趣可以到官网阅读:[url=http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.26.2]JLS3e: 15.26.2 Compound Assignment Operators[/url]
读下来,只有当复合赋值运算符的左手边是数组,且数组元素类型是String时才有提到特别针对String的+=,在15.26.2的其它地方并没有提到justjavac说的第二个限制,怪哉。
ECMA-334如是说:
[quote="ECMA-334, 4th Edition"][size=small][b]14.14.2 Compound assignment[/b][/size]
An operation of the form [b]x [i]op[/i]= y[/b] is processed by applying binary operator overload resolution (§14.2.4) as if the operation was written [b]x [i]op[/i] y[/b]. Then,
[list][*]If the return type of the selected operator is implicitly convertible to the type of [b]x[/b], the operation is evaluated as [b]x = x [i]op[/i] y[/b], except that x is evaluated only once.
[*]Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of [b]x[/b], and if [b]y[/b] is implicitly convertible to the type of [b]x[/b] or the operator is a shift operator, then the operation is evaluated as [b]x = (T)(x [i]op[/i] y)[/b], where [b]T[/b] is the type of [b]x[/b], except that [b]x[/b] is evaluated only once.
[*]Otherwise, the compound assignment is invalid, and a compile-time error occurs.[/list]
The term “evaluated only once” means that in the evaluation of x op y, the results of any constituent expressions of x are temporarily saved and then reused when performing the assignment to x. [[i]Example[/i]: In the assignment [b]A()[B()] += C()[/b], where [b]A[/b] is a method returning [b]int[][/b], and [b]B[/b] and [b]C[/b] are methods returning [b]int[/b], the methods are invoked only once, in the order [b]A[/b], [b]B[/b], [b]C[/b]. [i]end example[/i]]
When the left operand of a compound assignment is a property access or indexer access, the property or indexer shall have both a get accessor and a set accessor. If this is not the case, a compile-time error occurs.
The second rule above permits [b]x [i]op[/i]= y[/b] to be evaluated as [b]x = (T)(x [i]op[/i] y)[/b] in certain contexts. The rule exists such that the predefined operators can be used as compound operators when the left operand is of type [b]sbyte[/b], [b]byte[/b], [b]short[/b], [b]ushort[/b], or [b]char[/b]. Even when both arguments are of one of those types, the predefined operators produce a result of type [b]int[/b], as described in §14.2.6.2. Thus, without a cast it would not be possible to assign the result to the left operand.
[color=blue]The intuitive effect of the rule for predefined operators is simply that [b]x [i]op[/i]= y[/b] is permitted if both of [b]x [i]op[/i] y[/b] and [b]x = y[/b] are permitted.[/color] [[i]Example[/i]: In the following code
the intuitive reason for each error is that a corresponding simple assignment would also have been an error. [i]end example[/i]]
[[i]Note[/i]: Compound assignment operations support lifted operators. Since a compound assignment [b]x [i]op[/i]= y[/b] is evaluated as either [b]x = x [i]op[/i] y[/b] or [b]x = (T)(x [i]op[/i] y)[/b], the rules of evaluation implicitly cover lifted operators. [i]end note[/i]]
[size=small][b]14.14.3 Event assignment[/b][/size]
If the left operand of a [b]+=[/b] or [b]-=[/b] operator is an event, the expression is classified as an event access, and is evaluated as follows:
[list][*]The instance expression, if any, of the event access is evaluated.
[*]The right operand of the [b]+=[/b] or [b]-=[/b] operator is evaluated, and, if required, converted to the type of the left operand through an implicit conversion (§13.1).
[*]An event accessor of the event is invoked, with argument list consisting of the value computed in the previous step. If the operator was [b]+=[/b], the add accessor is invoked; if the operator was [b]-=[/b], the remove accessor is invoked.[/list]
An event assignment expression does not yield a value. Thus, an event assignment expression is valid only in the context of a statement-expression (§15.6).[/quote]
C#中复合赋值运算符比Java规定了更多的转换相关规则,不过还是可以很直观的理解:(注意上面用蓝色色高亮的那句)[color=darkblue]只有当x op y和x = y都是合法的时候,x op= y才是合法的[/color]。
也正是因为这样,所以justjavac提到的Java的两个陷阱在C#里都不存在:
1、编译错误:
不用像Java那样等到运行的时候再发现数据被剪了。
2、编译没问题,运行也没问题:
==================
从OpenJDK的邮件列表里挖个东西顺带放这儿:
[quote]Changeset: f09d6a3521b1
Author: jjg
Date: 2008-03-06 10:07 -0800
URL: http://hg.openjdk.java.net/jdk7/hotspot-comp/langtools/rev/f09d6a3521b1
4741726: allow Object += String
Summary: remove code in line with restriction removed from JLS
Reviewed-by: mcimadamore
Contributed-by: michaelbailey0 at gmail.com
! src/share/classes/com/sun/tools/javac/comp/Attr.java
+ test/tools/javac/StringConversion2.java
- test/tools/javac/expression/ObjectAppend.java[/quote]
JLS3e如是说:
[quote="Java Language Specification, 3rd Edition"][size=small][b]15.26.2 Compound Assignment Operators[/b][/size]
A compound assignment expression of the form [i]E1 op= E2[/i] is equivalent to [i]E1 = (T)((E1) op (E2))[/i], where T is the type of E1, except that E1 is evaluated only once. [/quote]
核心就这么一句,后面还有一堆细节规定,有兴趣可以到官网阅读:[url=http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.26.2]JLS3e: 15.26.2 Compound Assignment Operators[/url]
读下来,只有当复合赋值运算符的左手边是数组,且数组元素类型是String时才有提到特别针对String的+=,在15.26.2的其它地方并没有提到justjavac说的第二个限制,怪哉。
ECMA-334如是说:
[quote="ECMA-334, 4th Edition"][size=small][b]14.14.2 Compound assignment[/b][/size]
An operation of the form [b]x [i]op[/i]= y[/b] is processed by applying binary operator overload resolution (§14.2.4) as if the operation was written [b]x [i]op[/i] y[/b]. Then,
[list][*]If the return type of the selected operator is implicitly convertible to the type of [b]x[/b], the operation is evaluated as [b]x = x [i]op[/i] y[/b], except that x is evaluated only once.
[*]Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of [b]x[/b], and if [b]y[/b] is implicitly convertible to the type of [b]x[/b] or the operator is a shift operator, then the operation is evaluated as [b]x = (T)(x [i]op[/i] y)[/b], where [b]T[/b] is the type of [b]x[/b], except that [b]x[/b] is evaluated only once.
[*]Otherwise, the compound assignment is invalid, and a compile-time error occurs.[/list]
The term “evaluated only once” means that in the evaluation of x op y, the results of any constituent expressions of x are temporarily saved and then reused when performing the assignment to x. [[i]Example[/i]: In the assignment [b]A()[B()] += C()[/b], where [b]A[/b] is a method returning [b]int[][/b], and [b]B[/b] and [b]C[/b] are methods returning [b]int[/b], the methods are invoked only once, in the order [b]A[/b], [b]B[/b], [b]C[/b]. [i]end example[/i]]
When the left operand of a compound assignment is a property access or indexer access, the property or indexer shall have both a get accessor and a set accessor. If this is not the case, a compile-time error occurs.
The second rule above permits [b]x [i]op[/i]= y[/b] to be evaluated as [b]x = (T)(x [i]op[/i] y)[/b] in certain contexts. The rule exists such that the predefined operators can be used as compound operators when the left operand is of type [b]sbyte[/b], [b]byte[/b], [b]short[/b], [b]ushort[/b], or [b]char[/b]. Even when both arguments are of one of those types, the predefined operators produce a result of type [b]int[/b], as described in §14.2.6.2. Thus, without a cast it would not be possible to assign the result to the left operand.
[color=blue]The intuitive effect of the rule for predefined operators is simply that [b]x [i]op[/i]= y[/b] is permitted if both of [b]x [i]op[/i] y[/b] and [b]x = y[/b] are permitted.[/color] [[i]Example[/i]: In the following code
byte b = 0;
char ch = '\0';
int i = 0;
b += 1; // Ok
b += 1000; // Error, b = 1000 not permitted
b += i; // Error, b = i not permitted
b += (byte)i; // Ok
ch += 1; // Error, ch = 1 not permitted
ch += (char)1; // Ok
the intuitive reason for each error is that a corresponding simple assignment would also have been an error. [i]end example[/i]]
[[i]Note[/i]: Compound assignment operations support lifted operators. Since a compound assignment [b]x [i]op[/i]= y[/b] is evaluated as either [b]x = x [i]op[/i] y[/b] or [b]x = (T)(x [i]op[/i] y)[/b], the rules of evaluation implicitly cover lifted operators. [i]end note[/i]]
[size=small][b]14.14.3 Event assignment[/b][/size]
If the left operand of a [b]+=[/b] or [b]-=[/b] operator is an event, the expression is classified as an event access, and is evaluated as follows:
[list][*]The instance expression, if any, of the event access is evaluated.
[*]The right operand of the [b]+=[/b] or [b]-=[/b] operator is evaluated, and, if required, converted to the type of the left operand through an implicit conversion (§13.1).
[*]An event accessor of the event is invoked, with argument list consisting of the value computed in the previous step. If the operator was [b]+=[/b], the add accessor is invoked; if the operator was [b]-=[/b], the remove accessor is invoked.[/list]
An event assignment expression does not yield a value. Thus, an event assignment expression is valid only in the context of a statement-expression (§15.6).[/quote]
C#中复合赋值运算符比Java规定了更多的转换相关规则,不过还是可以很直观的理解:(注意上面用蓝色色高亮的那句)[color=darkblue]只有当x op y和x = y都是合法的时候,x op= y才是合法的[/color]。
也正是因为这样,所以justjavac提到的Java的两个陷阱在C#里都不存在:
1、编译错误:
static class Program {
static void Main(string[] args) {
byte b = 0;
int i = 1;
b += i; // error CS0266: 无法将类型“int”隐式转换为“byte”。存在一个显式转换(是否缺少强制转换?)
}
}
不用像Java那样等到运行的时候再发现数据被剪了。
2、编译没问题,运行也没问题:
using System;
using System.Linq.Expressions;
static class Program {
static void Main(string[] args) {
string s = "str: ";
object obj = (Expression<Func<int, int>>) (x => x + 1);
s += obj;
obj += s;
Console.WriteLine(s); // str: x => (x + 1)
Console.WriteLine(obj); // x => (x + 1)str: x => (x + 1)
}
}
==================
从OpenJDK的邮件列表里挖个东西顺带放这儿:
[quote]Changeset: f09d6a3521b1
Author: jjg
Date: 2008-03-06 10:07 -0800
URL: http://hg.openjdk.java.net/jdk7/hotspot-comp/langtools/rev/f09d6a3521b1
4741726: allow Object += String
Summary: remove code in line with restriction removed from JLS
Reviewed-by: mcimadamore
Contributed-by: michaelbailey0 at gmail.com
! src/share/classes/com/sun/tools/javac/comp/Attr.java
+ test/tools/javac/StringConversion2.java
- test/tools/javac/expression/ObjectAppend.java[/quote]