Java 编译器错误地将变量标记为可能已经分配给
Java compiler wrongly marks varible as might already have been assigned to
出于某种原因 java 编译器将较低的 str 变量标记为“可能已经分配给”,尽管这种情况是不可能的(至少我认为是这样)。
知道为什么吗?
我知道删除 final 修饰符会解决它,但我想了解原因...
final List<Long> idList = List.of(1L,2L,3L);
final String str;
boolean found = false;
for (Long id : idList) {
if (id == 2) {
str = "id" + id;
found = true;
break;
}
}
if (!found) {
str = "none";
}
在 intelliJ 上使用 java 15 IDE
鉴于 str 具有修饰符 final 这一事实,编译器会查看它,在这种情况下,就好像 str 是一个 String 类型的变量,它将能够更改(给定它的变量)修饰符最终无法接受更改。
编译器不会对您的代码进行深入分析来找出答案。这将花费很长时间,而且(请参阅停止问题),完美在数学上几乎是无法实现的。
因此,编译器有一个它应用的简单操作列表。
在这种情况下,str =
赋值是在 if
中,它在 while
循环中,因此,它可以 运行 0 次,1 次,或多次。它既不推断 属性 设置了 str
(可能 运行 0 次),也不允许触及最终变量(可能 运行 2 次或更多次).
关于不能多次赋值给最终变量的规则,写得更正式一些(JLS 4.12.4):
It is a compile-time error if a final variable is assigned to unless it is definitely unassigned immediately prior to the assignment.
Java 语言规范用了整个 chapter 16 来讨论什么是“明确分配”和“绝对未分配”。变量可以是“明确赋值”或“明确未赋值”,两者,或两者都不.
在这种情况下,如果您在代码中应用有关 for 循环的规则,您将看到只有一条规则讨论 for 循环后的明确 [un] 赋值:
V is [un]assigned after a for statement iff both of the following are true:
Either a condition expression is not present or V is [un]assigned after the condition expression when false.
V is [un]assigned before every break statement for which the for statement is the break target.
让我们尝试证明 str
在 for 循环之后肯定是未赋值的。
在一个迷人的 for 循环中,显然有一个条件表达式(检查迭代器是否 hasNext
)。该条件不涉及 str
,因此 str
像以前一样保持未分配状态。我们遇到了第一个要点。 str
是在休息前分配的,所以我们不符合第二个要点。
我们无法使用第 16 章中的规则证明 str
在 for 循环之后绝对未赋值。(事实上我们也无法证明它在 for 循环之后被赋值,但那是无关紧要的。 ) if (!found)
语句也没有做任何赋值,所以这意味着 str
在 str = "none";
之前 而不是 绝对未赋值。因此编译器错误根据 4.12.4.
如果您仍想使用 final 变量,请尝试使用基本的 for 循环来获取找到的元素的索引,然后将其分配给 str
:
final List<Long> idList = List.of(1L,2L,3L);
final String str;
int index = -1;
for (int i = 0; i < idList.size(); i++) {
Long id = idList.get(i);
if (id == 2) {
index = i;
break;
}
}
if (index < 0) {
str = "none";
} else {
str = "id" + idList.get(index);
}
或者,使用流:
final String str =
idList.stream()
.filter(x -> x == 2)
.findFirst()
.map(x -> "id" + x)
.orElse("none");
出于某种原因 java 编译器将较低的 str 变量标记为“可能已经分配给”,尽管这种情况是不可能的(至少我认为是这样)。
知道为什么吗?
我知道删除 final 修饰符会解决它,但我想了解原因...
final List<Long> idList = List.of(1L,2L,3L);
final String str;
boolean found = false;
for (Long id : idList) {
if (id == 2) {
str = "id" + id;
found = true;
break;
}
}
if (!found) {
str = "none";
}
在 intelliJ 上使用 java 15 IDE
鉴于 str 具有修饰符 final 这一事实,编译器会查看它,在这种情况下,就好像 str 是一个 String 类型的变量,它将能够更改(给定它的变量)修饰符最终无法接受更改。
编译器不会对您的代码进行深入分析来找出答案。这将花费很长时间,而且(请参阅停止问题),完美在数学上几乎是无法实现的。
因此,编译器有一个它应用的简单操作列表。
在这种情况下,str =
赋值是在 if
中,它在 while
循环中,因此,它可以 运行 0 次,1 次,或多次。它既不推断 属性 设置了 str
(可能 运行 0 次),也不允许触及最终变量(可能 运行 2 次或更多次).
关于不能多次赋值给最终变量的规则,写得更正式一些(JLS 4.12.4):
It is a compile-time error if a final variable is assigned to unless it is definitely unassigned immediately prior to the assignment.
Java 语言规范用了整个 chapter 16 来讨论什么是“明确分配”和“绝对未分配”。变量可以是“明确赋值”或“明确未赋值”,两者,或两者都不.
在这种情况下,如果您在代码中应用有关 for 循环的规则,您将看到只有一条规则讨论 for 循环后的明确 [un] 赋值:
V is [un]assigned after a for statement iff both of the following are true:
Either a condition expression is not present or V is [un]assigned after the condition expression when false.
V is [un]assigned before every break statement for which the for statement is the break target.
让我们尝试证明 str
在 for 循环之后肯定是未赋值的。
在一个迷人的 for 循环中,显然有一个条件表达式(检查迭代器是否 hasNext
)。该条件不涉及 str
,因此 str
像以前一样保持未分配状态。我们遇到了第一个要点。 str
是在休息前分配的,所以我们不符合第二个要点。
我们无法使用第 16 章中的规则证明 str
在 for 循环之后绝对未赋值。(事实上我们也无法证明它在 for 循环之后被赋值,但那是无关紧要的。 ) if (!found)
语句也没有做任何赋值,所以这意味着 str
在 str = "none";
之前 而不是 绝对未赋值。因此编译器错误根据 4.12.4.
如果您仍想使用 final 变量,请尝试使用基本的 for 循环来获取找到的元素的索引,然后将其分配给 str
:
final List<Long> idList = List.of(1L,2L,3L);
final String str;
int index = -1;
for (int i = 0; i < idList.size(); i++) {
Long id = idList.get(i);
if (id == 2) {
index = i;
break;
}
}
if (index < 0) {
str = "none";
} else {
str = "id" + idList.get(index);
}
或者,使用流:
final String str =
idList.stream()
.filter(x -> x == 2)
.findFirst()
.map(x -> "id" + x)
.orElse("none");