为什么JAVA 10中的var无法初始化为null?

时间:2018-06-20 07:58:10

标签: java java-10

我正在尝试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的。

9 个答案:

答案 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)

来自Oracle JDK 10 page

  

您不能只使用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 is a type in Java

  

还有一个特殊的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();时,编译器会知道aFoo类型的,这使您可以在编译时使用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