为什么var in JAVA10不能初始化为null?
Why var in JAVA 10 can not be initialized to null?
我正在研究 JAVA 10 中引入的新功能,在那里我发现了一个非常有趣的事实,即 你不能用 null 声明一个变量.
一旦你写下一段代码,
var a = null;
它抛出一个错误:
variable initializer is null
现在,我们都知道我们不能将原始类型声明为 null,所以下面的语句没有任何意义。
int a = null;
这意味着,如果开发人员正在用 null 初始化一个 var,他肯定想在其中放入一个 Object,而不是文字 val。如果是这样的话,我的问题是
为什么编译器不认为它是一个对象变量而是
抛出错误。
另一方面,如果你写下面的语句,它工作得很好:
var a = (Object)null;
声明一个变量为null的原因是什么
考虑以下情况,我想初始化 var 并想在条件块之外使用它:
var a = null;
if(some condition) Initialize with some arguments
else Initialize with some other arguments
//Use a variable here
因此,在这种情况下,因为我们希望 a 的范围在条件块之外,所以我们需要在 if 块之外初始化它为 null,这使用 var 是不可能的。
当你用关键字var
声明一个变量时,它只是一种书写的快捷方式。在编译时,关键字将替换为该变量的类型。为了知道变量的类型,初始化必须是显式的。如果用 null 初始化变量,编译器无法知道变量的类型,因此无法替换 var
。所以禁止了。
来自 Java 10 Local Variable Type Inference 页面:
You cannot initialise a var variable to null
either. Indeed it is not clear what the type should be as it’s probably intended for late initialisation.
| Error:
| cannot infer type for local variable x
| (variable initializer is 'null')
| var x = null;
| ^-----------^
null
不是类型,因此编译器无法推断 RHS 表达式的类型。
var
用作惰性类型。当您执行 var a = new Foo();
时,编译器知道 a
是 Foo
类型,这将使您能够在编译时使用 a
调用 Foo
的成员。
var a = null;
什么都不告诉编译器。如果编译器将 a
视为 Object
class,那么使用 var
类型就没有任何意义 - 您可以轻松使用 Object a = null;
.
更新
我认为您认为这就像 Java 脚本中的 var
一样工作,这是 绝对 错误的。
来自您在其他地方的评论之一:
What I am asking is at time of compilation, compiler can simply change
the type to Object (Upcast) on the safer side and if in program ,
variable has been initialized by some specific class Object, then
compiler can change it that specific type.
这显然意味着您正在寻找 var
功能,就像 Java 脚本所做的一样。
为什么 Java 是错误的?考虑一下:
var a = null; // Compiler makes 'a' an Object
if (checkSomething()) {
a = new Foo(); // Compiler makes 'a' a Foo
}
else {
a = new Bar(); // Compiler makes 'a' a Bar
}
test(a); // Should this be allowed???
public void test(Foo foo) {}
编译器应该允许这样做吗?不,您只会在运行时知道 var
是什么类型。编译器永远无法在编译时猜出这是什么。
这就是为什么 var
永远不会 完全取代定义显式类型的传统方式的原因。永远记住,var
只是一个 syntactic sugar.
这在 JLS Sec 14.4.1 中被明确列为局部变量的非法初始化:
It is a compile-time error if T is the null type.
- ...
- var g = null; // Illegal: null type
There is also a special null type, the type of the expression null
但是,不可能声明这种类型的变量(例如,您不能以引用该类型的方式编写 null g;
或 Null g;
),即使您可以,它唯一可能的值是 null
:
The null reference is the only possible value of an expression of null type.
所以允许您声明这种类型的变量是没有意义的。
您不能将 var 变量初始化为 null。通过分配 null,不清楚类型应该是什么,因为在 Java 中,任何对象引用都可以是 null。在下面的示例中,由于没有为空值预定义的数据类型,编译器无法解释类型 'test',这会导致复杂化错误。
var test=null;
//Compilation error because compiler cannot infer type for local variable count since any Java object reference can be null
you cannot just use the var syntax to declare a variable without a
value
You cannot initialise a var variable to null either. Indeed it is not
clear what the type should be as it’s probably intended for late
initialisation.
所以基本上,你必须具体说明你想要的数据类型,编译器不能假设你想要 Object
或任何其他类型。
所以下面的都会编译失败:
var x;
var x = null;
var x = () -> {}
What I am asking how can we extend the scope in that case. So for example, if I want to initialize a variablee inside a condition block but want to expand the scope so that the same variable can be used outside the block, what can be done in case of var.
答案是您要么使用:
var a = (RealType) null;
或者(明智地)您使用常规的类型化声明:
RealType a = null;
var
形式只是为了避免必须编写特定类型而提供的便利。当您使用 null
初始化时,它根本不起作用。在绝大多数情况下,将 Object
推断为 a
的类型是 无用的行为 。
例如,如果(假设地)可以将 var
分配给 null
:
var a = null;
if (something) {
a = someMethodReturningRealType();
}
a.someRealTypeMethod(); // Compilation error ... because the inferred
// type is java.lang.Object. Oops!!
这是正确的代码编写方式:
RealType a = null; // Just use a classic variable declaration
if (something) {
a = someMethodReturningRealType();
}
a.someRealTypeMethod();
My example is just to share where all we may need to declare a var as null. And it seems there is no clear way to do that.
没有。您 不需要 这样做。你想要去做...但是“想要”和“需要”不是一回事。
Moreover, this is a very basic Question as if we can do Object x = null;
why cant we can do var x = null;
因为JLS禁止。而已。故事结束。
如果你不喜欢,没关系。但这不是讨论论坛。反正我们也不是需要被说服的人。
从泛型类型的处理来看,您可能会认为这个问题并不牵强。
让我们以List<T> list;
为例来研究一下。如果您不指定泛型类型 (List list;
),编译器将使用 raw type. You can then add
everything to the list
- of course null
. Applying the concepts of raw type and type erasure 来处理它,到 var
可能允许您声明 var o;
并稍后将所有内容分配给 o
。但是由于不鼓励使用原始类型,很明显没有选择 var
应该如何工作。
关于案例 List<?> list;
并将其应用于 var o = null;
你可能会想到这样的事情:? o
。但这没有结果。在 List<?> list;
的情况下,唯一剩下的选项是 add(null);
。按照理论示例并尝试与当前概念保持一致,这意味着 ? o
只能以这种方式初始化:? o = null;
。所以你打算 decalre a var
并在以后初始化它在语法上是可能的,但在语义上它没有意义,因为它总是以与 null
相同的方式初始化。所以它不会提供任何额外的价值,但会增加复杂性。
编译器可以应用(至少)三种可能的类型推断策略 var o = null
:
- 选择
Void
- 选择
Object
- 查找稍后的初始化并选择该类型
所有这些在技术上都是可行的,所以问题出现了,哪个对开发人员来说最有意义。
显然,Void
毫无用处,我认为 Object
也没有多大用处。虽然正确,但选择这两种类型中的任何一种都不太可能帮助开发人员编写更好、更易读的代码。
最后一个选项,寻找初始化,没有被采用故意以避免所谓的action-at-a-distance errors:
var parent = null;
// imagine some recursion or loop structure, so this makes more sense
processNode(parent); // expects a parameter of type `Node`
parent = determineParent(); // returns a parameter of type `Node`
如果编译器推断 Node
为 parent
因为 determineParent()
returns 它,这将编译。但是代码很脆弱,因为对最后一行的更改可能会导致在第一行中选择不同的类型,从而在第二行中出现编译错误。这样不好!
我们已经习惯了更改类型的声明可能会导致错误的事实,但这里的更改(第 3 行)、它的效果(第 1 行)和随之而来的错误(第 2 行)可能相当不错相距甚远,这使得开发人员理解或更好地预测会发生什么变得更加复杂。
通过保持类型推断规则简单,开发人员可以更轻松地形成一个简单但正确的心智模型来了解正在发生的事情。
附录
选项 3(从稍后的初始化中推断类型)在技术上是否确实可行存在疑问。我的观点(确实如此)是基于我对 JEP 286 的理解,具体来说:
On the other hand, we could have expanded this feature to include the local equivalent of "blank" finals (i.e., not requiring an initializer, instead relying on definite assignment analysis.) We chose the restriction to "variables with initializers only" because it covers a significant fraction of the candidates while maintaining the simplicity of the feature and reducing "action at a distance" errors.
Similarly, we also could have taken all assignments into account when inferring the type, rather than just the initializer; while this would have further increased the percentage of locals that could exploit this feature, it would also increase the risk of "action at a distance" errors.
我正在研究 JAVA 10 中引入的新功能,在那里我发现了一个非常有趣的事实,即 你不能用 null 声明一个变量.
一旦你写下一段代码,
var a = null;
它抛出一个错误:
variable initializer is null
现在,我们都知道我们不能将原始类型声明为 null,所以下面的语句没有任何意义。
int a = null;
这意味着,如果开发人员正在用 null 初始化一个 var,他肯定想在其中放入一个 Object,而不是文字 val。如果是这样的话,我的问题是
为什么编译器不认为它是一个对象变量而是 抛出错误。
另一方面,如果你写下面的语句,它工作得很好:
var a = (Object)null;
声明一个变量为null的原因是什么
考虑以下情况,我想初始化 var 并想在条件块之外使用它:
var a = null;
if(some condition) Initialize with some arguments
else Initialize with some other arguments
//Use a variable here
因此,在这种情况下,因为我们希望 a 的范围在条件块之外,所以我们需要在 if 块之外初始化它为 null,这使用 var 是不可能的。
当你用关键字var
声明一个变量时,它只是一种书写的快捷方式。在编译时,关键字将替换为该变量的类型。为了知道变量的类型,初始化必须是显式的。如果用 null 初始化变量,编译器无法知道变量的类型,因此无法替换 var
。所以禁止了。
来自 Java 10 Local Variable Type Inference 页面:
You cannot initialise a var variable to
null
either. Indeed it is not clear what the type should be as it’s probably intended for late initialisation.| Error: | cannot infer type for local variable x | (variable initializer is 'null') | var x = null; | ^-----------^
null
不是类型,因此编译器无法推断 RHS 表达式的类型。
var
用作惰性类型。当您执行 var a = new Foo();
时,编译器知道 a
是 Foo
类型,这将使您能够在编译时使用 a
调用 Foo
的成员。
var a = null;
什么都不告诉编译器。如果编译器将 a
视为 Object
class,那么使用 var
类型就没有任何意义 - 您可以轻松使用 Object a = null;
.
更新
我认为您认为这就像 Java 脚本中的 var
一样工作,这是 绝对 错误的。
来自您在其他地方的评论之一:
What I am asking is at time of compilation, compiler can simply change the type to Object (Upcast) on the safer side and if in program , variable has been initialized by some specific class Object, then compiler can change it that specific type.
这显然意味着您正在寻找 var
功能,就像 Java 脚本所做的一样。
为什么 Java 是错误的?考虑一下:
var a = null; // Compiler makes 'a' an Object
if (checkSomething()) {
a = new Foo(); // Compiler makes 'a' a Foo
}
else {
a = new Bar(); // Compiler makes 'a' a Bar
}
test(a); // Should this be allowed???
public void test(Foo foo) {}
编译器应该允许这样做吗?不,您只会在运行时知道 var
是什么类型。编译器永远无法在编译时猜出这是什么。
这就是为什么 var
永远不会 完全取代定义显式类型的传统方式的原因。永远记住,var
只是一个 syntactic sugar.
这在 JLS Sec 14.4.1 中被明确列为局部变量的非法初始化:
It is a compile-time error if T is the null type.
- ...
- var g = null; // Illegal: null type
There is also a special null type, the type of the expression null
但是,不可能声明这种类型的变量(例如,您不能以引用该类型的方式编写 null g;
或 Null g;
),即使您可以,它唯一可能的值是 null
:
The null reference is the only possible value of an expression of null type.
所以允许您声明这种类型的变量是没有意义的。
您不能将 var 变量初始化为 null。通过分配 null,不清楚类型应该是什么,因为在 Java 中,任何对象引用都可以是 null。在下面的示例中,由于没有为空值预定义的数据类型,编译器无法解释类型 'test',这会导致复杂化错误。
var test=null;
//Compilation error because compiler cannot infer type for local variable count since any Java object reference can be null
you cannot just use the var syntax to declare a variable without a value You cannot initialise a var variable to null either. Indeed it is not clear what the type should be as it’s probably intended for late initialisation.
所以基本上,你必须具体说明你想要的数据类型,编译器不能假设你想要 Object
或任何其他类型。
所以下面的都会编译失败:
var x;
var x = null;
var x = () -> {}
What I am asking how can we extend the scope in that case. So for example, if I want to initialize a variablee inside a condition block but want to expand the scope so that the same variable can be used outside the block, what can be done in case of var.
答案是您要么使用:
var a = (RealType) null;
或者(明智地)您使用常规的类型化声明:
RealType a = null;
var
形式只是为了避免必须编写特定类型而提供的便利。当您使用 null
初始化时,它根本不起作用。在绝大多数情况下,将 Object
推断为 a
的类型是 无用的行为 。
例如,如果(假设地)可以将 var
分配给 null
:
var a = null;
if (something) {
a = someMethodReturningRealType();
}
a.someRealTypeMethod(); // Compilation error ... because the inferred
// type is java.lang.Object. Oops!!
这是正确的代码编写方式:
RealType a = null; // Just use a classic variable declaration
if (something) {
a = someMethodReturningRealType();
}
a.someRealTypeMethod();
My example is just to share where all we may need to declare a var as null. And it seems there is no clear way to do that.
没有。您 不需要 这样做。你想要去做...但是“想要”和“需要”不是一回事。
Moreover, this is a very basic Question as if we can do
Object x = null;
why cant we can dovar x = null;
因为JLS禁止。而已。故事结束。
如果你不喜欢,没关系。但这不是讨论论坛。反正我们也不是需要被说服的人。
从泛型类型的处理来看,您可能会认为这个问题并不牵强。
让我们以List<T> list;
为例来研究一下。如果您不指定泛型类型 (List list;
),编译器将使用 raw type. You can then add
everything to the list
- of course null
. Applying the concepts of raw type and type erasure 来处理它,到 var
可能允许您声明 var o;
并稍后将所有内容分配给 o
。但是由于不鼓励使用原始类型,很明显没有选择 var
应该如何工作。
关于案例 List<?> list;
并将其应用于 var o = null;
你可能会想到这样的事情:? o
。但这没有结果。在 List<?> list;
的情况下,唯一剩下的选项是 add(null);
。按照理论示例并尝试与当前概念保持一致,这意味着 ? o
只能以这种方式初始化:? o = null;
。所以你打算 decalre a var
并在以后初始化它在语法上是可能的,但在语义上它没有意义,因为它总是以与 null
相同的方式初始化。所以它不会提供任何额外的价值,但会增加复杂性。
编译器可以应用(至少)三种可能的类型推断策略 var o = null
:
- 选择
Void
- 选择
Object
- 查找稍后的初始化并选择该类型
所有这些在技术上都是可行的,所以问题出现了,哪个对开发人员来说最有意义。
显然,Void
毫无用处,我认为 Object
也没有多大用处。虽然正确,但选择这两种类型中的任何一种都不太可能帮助开发人员编写更好、更易读的代码。
最后一个选项,寻找初始化,没有被采用故意以避免所谓的action-at-a-distance errors:
var parent = null;
// imagine some recursion or loop structure, so this makes more sense
processNode(parent); // expects a parameter of type `Node`
parent = determineParent(); // returns a parameter of type `Node`
如果编译器推断 Node
为 parent
因为 determineParent()
returns 它,这将编译。但是代码很脆弱,因为对最后一行的更改可能会导致在第一行中选择不同的类型,从而在第二行中出现编译错误。这样不好!
我们已经习惯了更改类型的声明可能会导致错误的事实,但这里的更改(第 3 行)、它的效果(第 1 行)和随之而来的错误(第 2 行)可能相当不错相距甚远,这使得开发人员理解或更好地预测会发生什么变得更加复杂。
通过保持类型推断规则简单,开发人员可以更轻松地形成一个简单但正确的心智模型来了解正在发生的事情。
附录
选项 3(从稍后的初始化中推断类型)在技术上是否确实可行存在疑问。我的观点(确实如此)是基于我对 JEP 286 的理解,具体来说:
On the other hand, we could have expanded this feature to include the local equivalent of "blank" finals (i.e., not requiring an initializer, instead relying on definite assignment analysis.) We chose the restriction to "variables with initializers only" because it covers a significant fraction of the candidates while maintaining the simplicity of the feature and reducing "action at a distance" errors.
Similarly, we also could have taken all assignments into account when inferring the type, rather than just the initializer; while this would have further increased the percentage of locals that could exploit this feature, it would also increase the risk of "action at a distance" errors.