我正在尝试JAVA 10中引入的新功能,在其中我发现了一个非常有趣的事实,即您不能使用null声明变量。
在您编写以下代码后,
var a = null;
它抛出一个错误:
变量初始值设定项为空
现在,众所周知,我们不能将原始类型声明为null,因此下面的语句没有任何意义。
int a = null;
这意味着,如果开发人员正在使用null初始化var,则它肯定要在其中放置一个Object,而不是文字val。如果是这样,我的问题是
为什么编译器不认为它是一个对象变量,而是 引发错误。
另一方面,如果您编写以下语句,则效果很好:
var a = (Object)null;
用null声明var的原因是什么
考虑以下情况,我想初始化var并想在条件块外使用它:
var a = null;
if(some condition) Initialize with some arguements
else Initialize with some other arguements
//Use a varialble here
因此,在这种情况下,由于我们希望a的范围在条件块之外,因此我们有必要将其初始化为if块,否则它将在null之外为空,这是无法使用var的。
答案 0 :(得分:13)
(至少)三种编译器可应用于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()
的{{1}},则它将编译。但是代码很脆弱,因为更改最后一行可能会导致在第一行中选择不同的类型,从而在第二行中编译错误。不好!</ p>
我们已经习惯了这样的事实,即更改类型的声明可能会导致错误,但是这里的更改(第3行),其影响(第1行)和随之而来的错误(第2行)可能相距甚远,这会使开发人员更难以理解或更好地预测会发生什么。
通过简化类型推断规则,开发人员可以更轻松地形成一个简单但正确的思维模式。
令人怀疑的是,从以后的初始化中推断类型的选项3在技术上是否确实可行。我的看法是基于对JEP 286的理解,具体是:
另一方面,我们可以扩展此功能,以包括“空白”决赛的本地等效项(即,不需要初始化程序,而是依赖于明确的赋值分析。)我们选择了“仅包含初始化程序的变量”的限制。 ”,因为它涵盖了很大一部分候选者,同时保持了功能的简单性并减少了“远距离操作”错误。
类似地,我们在推断类型时也可以考虑所有分配,而不仅仅是初始化器。虽然这会进一步增加可利用此功能的本地人的比例,但也会增加“远距离行动”错误的风险。
答案 1 :(得分:6)
我要问的是在那种情况下我们如何扩展范围。因此,例如,如果我想在条件块内初始化变量,但又想扩展范围,以便可以在块外使用同一变量,那么在使用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();
我的示例只是分享我们可能需要将var声明为null的所有地方。似乎没有明确的方法可以做到这一点。
不。您不需要这样做。您想要这样做...但是“想要”和“需要”不是同一回事。
此外,这是一个非常基本的问题,好像我们可以做
Object x = null;
,为什么我们不能做var x = null;
因为JLS禁止使用它。而已。故事结束。
如果您不喜欢它,那很好。但这不是讨论论坛。无论如何,我们不是需要说服的人。
答案 2 :(得分:3)
人们可能会认为,对泛型类型的处理并不是那么牵强的问题。
让我们以List<T> list;
为例进行检查。如果未指定通用类型(List list;
),则编译器将使用raw type处理它。然后,您可以将add
的所有内容list
-当然是null
。将原始类型和type erasure的概念应用于var
可以使您声明var o;
并稍后将所有内容分配给o
。但是,由于不鼓励使用原始类型,因此显然没有选择var
的工作方式。
关于案例List<?> list;
并将其应用于var o = null;
,您可能会想到这样的情况:? o
。但这什么也没有。对于List<?> list;
,唯一保留的选项是add(null);
。遵循理论示例并尝试与当前概念保持一致,这将意味着? o
只能通过以下方式初始化:? o = null;
。因此,您打算在语法上对var
进行下标处理并在以后进行初始化,但是从逻辑上讲,这是没有意义的,因为它将始终以与null
相同的方式进行初始化。因此,它不会提供任何附加价值,但会增加复杂性。
答案 3 :(得分:1)
您不能只使用var语法来声明一个不带变量的变量 值 您也不能将var变量初始化为null。确实不是 明确类型,因为可能打算晚些时候使用 初始化。
因此,基本上,您必须具体确定所需的数据类型,编译器不能仅假设您需要Object
或任何其他类型。
因此,以下所有内容都会导致编译失败:
var x;
var x = null;
var x = () -> {}
答案 4 :(得分:0)
使用关键字var
声明变量时,它只是编写的快捷方式。在编译时,关键字将被此变量的类型替换。为了知道变量的类型,初始化必须是明确的。如果使用null初始化变量,则编译器将无法知道变量的类型,因此无法替换var
。所以这是禁止的。
答案 5 :(得分:0)
在JLS Sec 14.4.1中,这被明确列为局部变量的非法初始化:
如果T为空类型,则是编译时错误。
- ...
- var g = null; //非法:空类型
还有一个特殊的null类型,即表达式null的类型
但是,不可能声明这种类型的变量(例如,您无法以引用该类型的方式编写null g;
或Null g;
),即使可以,唯一可能的值是null
:
空引用是空类型表达式的唯一可能的值。
因此,让您声明这种类型的变量毫无意义。
答案 6 :(得分:0)
您不能将var变量初始化为null。通过分配null,不清楚类型应该是什么,因为在Java中,任何对象引用都可以为null。在下面的示例中,由于没有用于null值的预定义数据类型,因此编译器无法解释类型“ test”,这将导致并发症。
var test=null;
//Compilation error because compiler cannot infer type for local variable count since any Java object reference can be null
答案 7 :(得分:-1)
在Java 10 Local Variable Type Inference页上:
您也无法将var变量初始化为
null
。确实,尚不清楚类型应该是什么,因为它可能打算用于后期初始化。| Error: | cannot infer type for local variable x | (variable initializer is 'null') | var x = null; | ^-----------^
null
不是类型,因此编译器无法推断RHS表达式的类型。
答案 8 :(得分:-2)
var
用作惰性类型。当您执行var a = new Foo();
时,编译器会知道a
是Foo
类型的,这使您可以在编译时使用Foo
来调用a
的成员。
var a = null;
不会告诉编译器任何信息。如果编译器将a
视为Object
类,则使用var
类型没有任何目的-您可以轻松使用Object a = null;
。
我认为您认为它的工作方式就像Javascript中的var
,这是绝对错误。
从您在其他地方的评论之一:
我要问的是在编译时,编译器可以简单地更改 较安全的是“对象(上传)”类型,如果在程序中, 变量已由某些特定的类Object初始化,然后 编译器可以将其更改为特定类型。
这显然意味着您正在寻找与Javascript一样的var
功能。
为什么在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。