Java 赋值运算符与 Lambda 表达式详解
1. 赋值运算符
在 Java 中,赋值运算符是编程时经常会用到的操作符,其中有一些特殊情况值得我们深入探讨。
1.1 数组存储异常示例
有一个比较有趣的例子:
Threads[1]=StringBuffer => ArrayStoreException
。这表明,当试图将一个
StringBuffer
的引用存储到一个元素类型为
Thread
的数组中时,会抛出
ArrayStoreException
异常。虽然在编译时,代码的类型是正确的,赋值操作的左边是
Object[]
类型,右边是
Object
类型,但在运行时,
testFour
方法的第一个实际参数是一个
Thread
数组的实例引用,第三个实际参数是
StringBuffer
类的实例引用,就会出现上述异常。
1.2 复合赋值运算符
复合赋值表达式
E1 op= E2
等价于
E1 = (T) ((E1) op (E2))
,其中
T
是
E1
的类型,不过
E1
只计算一次。例如:
short x = 3;
x += 4.6;
这段代码的结果是
x
的值为 7,因为它等价于:
short x = 3;
x = (short)(x + 4.6);
在运行时,表达式的计算方式分为两种情况:
-
左操作数不是数组访问表达式
:
1. 首先计算左操作数以生成一个变量。如果这个计算突然结束,那么赋值表达式也会因为同样的原因突然结束,右操作数不会被计算,也不会进行赋值操作。
2. 否则,保存左操作数的值,然后计算右操作数。如果这个计算突然结束,那么赋值表达式也会因为同样的原因突然结束,不会进行赋值操作。
3. 否则,使用左操作数保存的值和右操作数的值执行复合赋值运算符指示的二元运算。如果这个运算突然结束,那么赋值表达式也会因为同样的原因突然结束,不会进行赋值操作。
4. 否则,将二元运算的结果转换为左操作数变量的类型,进行值集转换(§5.1.13)到适当的标准值集(不是扩展指数值集),并将转换结果存储到变量中。
-
左操作数是数组访问表达式
:
1. 首先计算左操作数数组访问表达式的数组引用子表达式。如果这个计算突然结束,那么赋值表达式也会因为同样的原因突然结束,索引子表达式和右操作数不会被计算,也不会进行赋值操作。
2. 否则,计算左操作数数组访问表达式的索引子表达式。如果这个计算突然结束,那么赋值表达式也会因为同样的原因突然结束,右操作数不会被计算,也不会进行赋值操作。
3. 否则,如果数组引用子表达式的值为
null
,则不会进行赋值操作,并抛出
NullPointerException
异常。
4. 否则,数组引用子表达式的值确实引用一个数组。如果索引子表达式的值小于零,或者大于或等于数组的长度,则不会进行赋值操作,并抛出
ArrayIndexOutOfBoundsException
异常。
5. 否则,使用索引子表达式的值选择数组引用子表达式所引用数组的一个元素。保存这个元素的值,然后计算右操作数。如果这个计算突然结束,那么赋值表达式也会因为同样的原因突然结束,不会进行赋值操作。
对于简单赋值运算符,右操作数的计算在检查数组引用子表达式和索引子表达式之前进行;而对于复合赋值运算符,右操作数的计算在这些检查之后进行。
以下是一个复合赋值到数组元素的示例代码:
class ArrayReferenceThrow extends RuntimeException { }
class IndexThrow extends RuntimeException { }
class RightHandSideThrow extends RuntimeException { }
class IllustrateCompoundArrayAssignment {
static String[] strings = { "Simon", "Garfunkel" };
static double[] doubles = { Math.E, Math.PI };
static String[] stringsThrow() {
throw new ArrayReferenceThrow();
}
static double[] doublesThrow() {
throw new ArrayReferenceThrow();
}
static int indexThrow() {
throw new IndexThrow();
}
static String stringThrow() {
throw new RightHandSideThrow();
}
static double doubleThrow() {
throw new RightHandSideThrow();
}
static String name(Object q) {
String sq = q.getClass().getName();
int k = sq.lastIndexOf('.');
return (k < 0) ? sq : sq.substring(k+1);
}
static void testEight(String[] x, double[] z, int j) {
String sx = (x == null) ? "null" : "Strings";
String sz = (z == null) ? "null" : "doubles";
System.out.println();
try {
System.out.print(sx + "[throw]+=throw => ");
x[indexThrow()] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[throw]+=throw => ");
z[indexThrow()] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sx + "[throw]+=\"heh\" => ");
x[indexThrow()] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[throw]+=12345 => ");
z[indexThrow()] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sx + "[" + j + "]+=throw => ");
x[j] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[" + j + "]+=throw => ");
z[j] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sx + "[" + j + "]+=\"heh\" => ");
x[j] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[" + j + "]+=12345 => ");
z[j] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
}
public static void main(String[] args) {
try {
System.out.print("throw[throw]+=throw => ");
stringsThrow()[indexThrow()] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[throw]+=throw => ");
doublesThrow()[indexThrow()] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[throw]+=\"heh\" => ");
stringsThrow()[indexThrow()] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[throw]+=12345 => ");
doublesThrow()[indexThrow()] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=throw => ");
stringsThrow()[1] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=throw => ");
doublesThrow()[1] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=\"heh\" => ");
stringsThrow()[1] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=12345 => ");
doublesThrow()[1] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
testEight(null, null, 1);
testEight(null, null, 9);
testEight(strings, doubles, 1);
testEight(strings, doubles, 9);
}
}
这个程序的输出结果如下:
throw[throw]+=throw => ArrayReferenceThrow
throw[throw]+=throw => ArrayReferenceThrow
throw[throw]+="heh" => ArrayReferenceThrow
throw[throw]+=12345 => ArrayReferenceThrow
throw[1]+=throw => ArrayReferenceThrow
throw[1]+=throw => ArrayReferenceThrow
throw[1]+="heh" => ArrayReferenceThrow
throw[1]+=12345 => ArrayReferenceThrow
null[throw]+=throw => IndexThrow
null[throw]+=throw => IndexThrow
null[throw]+="heh" => IndexThrow
null[throw]+=12345 => IndexThrow
null[1]+=throw => NullPointerException
null[1]+=throw => NullPointerException
null[1]+="heh" => NullPointerException
null[1]+=12345 => NullPointerException
null[throw]+=throw => IndexThrow
null[throw]+=throw => IndexThrow
null[throw]+="heh" => IndexThrow
null[throw]+=12345 => IndexThrow
null[9]+=throw => NullPointerException
null[9]+=throw => NullPointerException
null[9]+="heh" => NullPointerException
null[9]+=12345 => NullPointerException
Strings[throw]+=throw => IndexThrow
doubles[throw]+=throw => IndexThrow
Strings[throw]+="heh" => IndexThrow
doubles[throw]+=12345 => IndexThrow
Strings[1]+=throw => RightHandSideThrow
doubles[1]+=throw => RightHandSideThrow
Strings[1]+="heh" => Okay!
doubles[1]+=12345 => Okay!
Strings[throw]+=throw => IndexThrow
doubles[throw]+=throw => IndexThrow
Strings[throw]+="heh" => IndexThrow
doubles[throw]+=12345 => IndexThrow
Strings[9]+=throw => ArrayIndexOutOfBoundsException
doubles[9]+=throw => ArrayIndexOutOfBoundsException
Strings[9]+="heh" => ArrayIndexOutOfBoundsException
doubles[9]+=12345 => ArrayIndexOutOfBoundsException
其中,倒数第十一个和第十二个结果比较有趣:
Strings[1]+=throw => RightHandSideThrow
doubles[1]+=throw => RightHandSideThrow
这两个例子表明,当右操作数抛出异常时,确实会抛出该异常,而且这是所有例子中唯一出现这种情况的例子,这也证明了右操作数的计算确实是在检查数组引用子表达式和索引子表达式之后进行的。
另外,还有一个关于复合赋值运算符保存左操作数的值的示例:
class Test {
public static void main(String[] args) {
int k = 1;
int[] a = { 1 };
k += (k = 4) * (k + 2);
a[0] += (a[0] = 4) * (a[0] + 2);
System.out.println("k==" + k + " and a[0]==" + a[0]);
}
}
这个程序的输出是
k==25 and a[0]==25
。复合赋值运算符
+=
在计算右操作数
(k = 4) * (k + 2)
之前,会保存
k
的值 1。计算右操作数时,先将 4 赋值给
k
,计算
k + 2
的值为 6,然后将 4 乘以 6 得到 24,再将 24 与保存的值 1 相加得到 25,最后通过
+=
运算符将 25 存储到
k
中。对于
a[0]
的情况,分析过程是相同的。
1.3 复合赋值运算符计算流程
graph TD;
A[开始] --> B{左操作数是否为数组访问表达式};
B -- 否 --> C[计算左操作数生成变量];
C -- 异常 --> D[赋值表达式异常结束];
C -- 正常 --> E[保存左操作数的值];
E --> F[计算右操作数];
F -- 异常 --> D;
F -- 正常 --> G[执行二元运算];
G -- 异常 --> D;
G -- 正常 --> H[结果转换并存储];
B -- 是 --> I[计算数组引用子表达式];
I -- 异常 --> D;
I -- 正常 --> J[计算索引子表达式];
J -- 异常 --> D;
J -- 正常 --> K{数组引用是否为 null};
K -- 是 --> L[抛出 NullPointerException];
K -- 否 --> M{索引是否越界};
M -- 是 --> N[抛出 ArrayIndexOutOfBoundsException];
M -- 否 --> O[选择数组元素并保存值];
O --> P[计算右操作数];
P -- 异常 --> D;
P -- 正常 --> Q{左操作数类型};
Q -- 基本类型 --> R[执行二元运算];
R -- 异常 --> D;
R -- 正常 --> S[结果转换并存储到数组元素];
Q -- 引用类型 --> T[执行字符串连接];
T -- 异常 --> D;
T -- 正常 --> U[结果存储到数组元素];
2. Lambda 表达式
Lambda 表达式是 Java 中一个非常强大的特性,它类似于一个方法,提供了一个形式参数列表和一个用这些参数表示的主体(表达式或代码块)。
2.1 Lambda 表达式的基本概念
Lambda 表达式的语法为:
LambdaParameters -> LambdaBody
。它总是多态表达式,只能出现在赋值上下文、调用上下文或强制类型转换上下文中,否则会产生编译时错误。
计算一个 Lambda 表达式会生成一个函数式接口的实例,但不会立即执行表达式的主体,而是在后续调用函数式接口的适当方法时才会执行。
以下是一些 Lambda 表达式的示例:
() -> {} // 无参数,结果为 void
() -> 42 // 无参数,表达式主体
() -> null // 无参数,表达式主体
() -> { return 42; } // 无参数,带返回值的代码块主体
() -> { System.gc(); } // 无参数,void 代码块主体
() -> { // 带返回值的复杂代码块主体
if (true) return 12;
else {
int result = 15;
for (int i = 1; i < 10; i++)
result *= i;
return result;
}
}
(int x) -> x+1 // 单声明类型参数
(int x) -> { return x+1; } // 单声明类型参数
(x) -> x+1 // 单推断类型参数
x -> x+1 // 单推断类型参数,括号可选
(String s) -> s.length() // 单声明类型参数
(Thread t) -> { t.start(); } // 单声明类型参数
s -> s.length() // 单推断类型参数
t -> { t.start(); } // 单推断类型参数
(int x, int y) -> x+y // 多声明类型参数
(x, y) -> x+y // 多推断类型参数
(x, int y) -> x+y // 非法:不能混合推断和声明类型
(x, final y) -> x+y // 非法:推断类型不能使用修饰符
这种语法的优点是可以减少简单 Lambda 表达式周围的括号噪声,当 Lambda 表达式作为方法的参数,或者主体是另一个 Lambda 表达式时,这种优势更加明显。它还能清晰地区分表达式和语句形式,避免了歧义或过度依赖分号。当需要额外的括号来区分整个 Lambda 表达式或其主体表达式时,自然也支持使用括号。
2.2 Lambda 表达式的解析挑战
Lambda 表达式的语法在解析时存在一些挑战。Java 编程语言在遇到
(
符号后,一直需要任意的前瞻来区分类型和表达式,因为后面可能是强制类型转换或带括号的表达式。泛型重用了二元运算符
<
和
>
后,这个问题变得更加复杂。Lambda 表达式引入了新的可能性,
(
后面的标记可能描述的是类型、表达式或 Lambda 参数列表。有些标记可以立即表明是参数列表(如注解、
final
),在其他情况下,有一些特定的模式必须被解释为参数列表(如连续两个名称、不在
<
和
>
嵌套内的逗号),有时直到遇到
->
才能做出判断。一种简单的解析方式是使用状态机,每个状态代表可能的解释子集(类型、表达式或参数),当状态机转换到一个单元素集合的状态时,解析器就知道是哪种情况了,但这种方式不太适合固定前瞻的语法。
2.3 Lambda 表达式的参数
Lambda 表达式的形式参数可以通过两种方式指定:一种是用括号括起来的逗号分隔的参数说明符列表,另一种是用括号括起来的逗号分隔的标识符列表。在参数说明符列表中,每个参数说明符由可选的修饰符、类型(或
var
)和指定参数名称的标识符组成;在标识符列表中,每个标识符指定参数的名称。
如果 Lambda 表达式没有形式参数,那么在
->
和 Lambda 主体之前会出现一对空括号。如果 Lambda 表达式只有一个形式参数,并且该参数是由标识符而不是参数说明符指定的,那么标识符周围的括号可以省略。
形式参数可以有推断类型或声明类型:
- 如果形式参数是由使用
var
的参数说明符指定的,或者是由标识符而不是参数说明符指定的,那么该形式参数具有推断类型,其类型是从 Lambda 表达式所针对的函数式接口类型推断出来的。
- 如果形式参数是由不使用
var
的参数说明符指定的,那么该形式参数具有声明类型。如果不是可变参数,声明类型由
UnannType
表示(如果
UnannType
和
VariableDeclaratorId
中没有括号对),否则由 §10.2 指定;如果是可变参数,声明类型是由 §10.2 指定的数组类型。
需要注意的是,
(int... x) -> BODY
和
(int[] x) -> BODY
这两种 Lambda 参数列表没有区别,无论函数式接口的抽象方法是固定参数还是可变参数,都可以使用。
一个 Lambda 表达式,如果所有形式参数都有声明类型,则称为显式类型;如果所有形式参数都有推断类型,则称为隐式类型;没有形式参数的 Lambda 表达式是显式类型。如果 Lambda 表达式是隐式类型,其主体的解释取决于它出现的上下文,这意味着在尝试对 Lambda 主体进行类型检查之前,必须先推断形式参数的类型。
此外,Lambda 表达式不能混合声明类型和推断类型的形式参数,例如
(x, int y) -> BODY
或
(var x, int y) -> BODY
是非法的。同时,Lambda 表达式不能声明两个同名的形式参数,使用
_
作为 Lambda 参数名也是不允许的,声明为
final
的形式参数不能在 Lambda 主体内被赋值。
2.4 Lambda 表达式参数类型规则
| 参数指定方式 | 参数类型 | 说明 |
|---|---|---|
| 参数说明符使用 var 或标识符指定 | 推断类型 | 从函数式接口类型推断 |
| 参数说明符不使用 var 指定 | 声明类型 | 非可变参数由 UnannType 或 §10.2 指定,可变参数由 §10.2 指定数组类型 |
Java 赋值运算符与 Lambda 表达式详解
3. Lambda 表达式的类型检查与使用注意事项
3.1 类型检查规则
在 Java 中,Lambda 表达式的类型检查是一个重要的环节。由于 Lambda 表达式总是多态表达式,它需要在特定的上下文中进行类型检查,这些上下文包括赋值上下文、调用上下文或强制类型转换上下文。
对于隐式类型的 Lambda 表达式,其形式参数的类型需要从目标函数式接口类型中推断出来。例如:
import java.util.function.IntBinaryOperator;
public class LambdaTypeInference {
public static void main(String[] args) {
IntBinaryOperator add = (x, y) -> x + y;
int result = add.applyAsInt(3, 5);
System.out.println(result);
}
}
在这个例子中,
(x, y) -> x + y
是一个隐式类型的 Lambda 表达式,它的目标类型是
IntBinaryOperator
函数式接口。根据这个接口的抽象方法
int applyAsInt(int left, int right)
,可以推断出
x
和
y
的类型都是
int
。
而对于显式类型的 Lambda 表达式,其形式参数的类型在声明时就已经确定。例如:
import java.util.function.IntBinaryOperator;
public class ExplicitlyTypedLambda {
public static void main(String[] args) {
IntBinaryOperator subtract = (int x, int y) -> x - y;
int result = subtract.applyAsInt(8, 3);
System.out.println(result);
}
}
这里的
(int x, int y) -> x - y
就是显式类型的 Lambda 表达式,
x
和
y
的类型明确为
int
。
3.2 使用注意事项
-
避免类型混合
:如前文所述,不能在 Lambda 表达式中混合使用推断类型和声明类型的形式参数,例如
(x, int y) -> x + y是不合法的。这是为了保证代码的清晰性和一致性,避免类型推断的混乱。 -
参数命名规范
:不能声明两个同名的形式参数,并且从 Java SE 9 开始,
_是关键字,不能作为 Lambda 参数名。良好的参数命名有助于提高代码的可读性和可维护性。 -
final参数限制 :如果形式参数被声明为final,则不能在 Lambda 主体内对其进行赋值。这是因为final关键字表示该参数是不可变的,一旦赋值就不能再改变。例如:
import java.util.function.Consumer;
public class FinalParameterExample {
public static void main(String[] args) {
Consumer<Integer> printer = (final int num) -> {
// 以下代码会编译错误,因为 num 是 final 的
// num = num + 1;
System.out.println(num);
};
printer.accept(5);
}
}
4. Lambda 表达式与赋值运算符的结合应用
在实际编程中,Lambda 表达式和赋值运算符可以结合使用,实现一些复杂的功能。例如,我们可以将 Lambda 表达式赋值给一个变量,然后在后续的代码中使用这个变量。
import java.util.function.Function;
public class LambdaAssignmentCombination {
public static void main(String[] args) {
// 将 Lambda 表达式赋值给 Function 类型的变量
Function<Integer, Integer> square = x -> x * x;
int input = 4;
int result = square.apply(input);
System.out.println("The square of " + input + " is " + result);
// 复合赋值运算符与 Lambda 表达式结合(这里只是示例概念,实际可能更复杂)
Function<Integer, Integer> increment = x -> x + 1;
int num = 2;
num += increment.apply(num);
System.out.println("After increment: " + num);
}
}
在这个例子中,首先将一个计算平方的 Lambda 表达式赋值给
square
变量,然后使用这个变量计算输入值的平方。接着,将一个递增的 Lambda 表达式赋值给
increment
变量,并使用复合赋值运算符
+=
结合这个 Lambda 表达式对
num
进行操作。
5. 总结
通过对 Java 赋值运算符和 Lambda 表达式的详细介绍,我们了解到:
-
赋值运算符
:普通赋值运算符和复合赋值运算符在使用时有着不同的规则和注意事项。复合赋值运算符在处理数组元素时,右操作数的计算时机与简单赋值运算符不同,并且会保存左操作数的值。在实际编程中,需要注意数组引用为
null
和索引越界等异常情况。
-
Lambda 表达式
:它是 Java 中一种强大的语法特性,类似于方法,提供了简洁的语法来表示匿名函数。Lambda 表达式的参数可以是推断类型或声明类型,但不能混合使用。其解析存在一定挑战,需要通过状态机等方式进行处理。在使用 Lambda 表达式时,要遵循类型检查规则和各种使用注意事项。
在实际的 Java 编程中,合理运用赋值运算符和 Lambda 表达式可以提高代码的简洁性和可读性,同时也能增强代码的灵活性和可维护性。希望本文的介绍能帮助你更好地理解和使用这两个重要的 Java 特性。
6. 参考示例代码汇总
以下是本文中涉及的所有示例代码汇总,方便你查看和测试:
// 复合赋值到数组元素示例
class ArrayReferenceThrow extends RuntimeException { }
class IndexThrow extends RuntimeException { }
class RightHandSideThrow extends RuntimeException { }
class IllustrateCompoundArrayAssignment {
static String[] strings = { "Simon", "Garfunkel" };
static double[] doubles = { Math.E, Math.PI };
static String[] stringsThrow() {
throw new ArrayReferenceThrow();
}
static double[] doublesThrow() {
throw new ArrayReferenceThrow();
}
static int indexThrow() {
throw new IndexThrow();
}
static String stringThrow() {
throw new RightHandSideThrow();
}
static double doubleThrow() {
throw new RightHandSideThrow();
}
static String name(Object q) {
String sq = q.getClass().getName();
int k = sq.lastIndexOf('.');
return (k < 0) ? sq : sq.substring(k+1);
}
static void testEight(String[] x, double[] z, int j) {
String sx = (x == null) ? "null" : "Strings";
String sz = (z == null) ? "null" : "doubles";
System.out.println();
try {
System.out.print(sx + "[throw]+=throw => ");
x[indexThrow()] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[throw]+=throw => ");
z[indexThrow()] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sx + "[throw]+=\"heh\" => ");
x[indexThrow()] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[throw]+=12345 => ");
z[indexThrow()] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sx + "[" + j + "]+=throw => ");
x[j] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[" + j + "]+=throw => ");
z[j] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sx + "[" + j + "]+=\"heh\" => ");
x[j] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print(sz + "[" + j + "]+=12345 => ");
z[j] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
}
public static void main(String[] args) {
try {
System.out.print("throw[throw]+=throw => ");
stringsThrow()[indexThrow()] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[throw]+=throw => ");
doublesThrow()[indexThrow()] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[throw]+=\"heh\" => ");
stringsThrow()[indexThrow()] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[throw]+=12345 => ");
doublesThrow()[indexThrow()] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=throw => ");
stringsThrow()[1] += stringThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=throw => ");
doublesThrow()[1] += doubleThrow();
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=\"heh\" => ");
stringsThrow()[1] += "heh";
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
try {
System.out.print("throw[1]+=12345 => ");
doublesThrow()[1] += 12345;
System.out.println("Okay!");
} catch (Throwable e) { System.out.println(name(e)); }
testEight(null, null, 1);
testEight(null, null, 9);
testEight(strings, doubles, 1);
testEight(strings, doubles, 9);
}
}
// 复合赋值运算符保存左操作数的值示例
class Test {
public static void main(String[] args) {
int k = 1;
int[] a = { 1 };
k += (k = 4) * (k + 2);
a[0] += (a[0] = 4) * (a[0] + 2);
System.out.println("k==" + k + " and a[0]==" + a[0]);
}
}
// Lambda 表达式示例
import java.util.function.Function;
public class LambdaExamples {
public static void main(String[] args) {
// 无参数 Lambda 表达式
Runnable emptyTask = () -> System.out.println("Empty task executed");
emptyTask.run();
// 单参数 Lambda 表达式
Function<Integer, Integer> increment = x -> x + 1;
int num = 3;
int newNum = increment.apply(num);
System.out.println("After increment: " + newNum);
// 多参数 Lambda 表达式
Function<Integer, Function<Integer, Integer>> adder = x -> y -> x + y;
int result = adder.apply(2).apply(3);
System.out.println("2 + 3 = " + result);
}
}
// Lambda 表达式与赋值运算符结合示例
import java.util.function.Function;
public class LambdaAssignmentCombination {
public static void main(String[] args) {
// 将 Lambda 表达式赋值给 Function 类型的变量
Function<Integer, Integer> square = x -> x * x;
int input = 4;
int result = square.apply(input);
System.out.println("The square of " + input + " is " + result);
// 复合赋值运算符与 Lambda 表达式结合(这里只是示例概念,实际可能更复杂)
Function<Integer, Integer> increment = x -> x + 1;
int num = 2;
num += increment.apply(num);
System.out.println("After increment: " + num);
}
}
通过运行这些代码,你可以更深入地理解 Java 赋值运算符和 Lambda 表达式的用法和特性。在实际开发中,根据具体的需求合理运用这些知识,能够编写出更加高效、简洁的 Java 代码。
超级会员免费看
4万+

被折叠的 条评论
为什么被折叠?



