4

I was playing around new features that have been introduced in JAVA 10, where in I found quite interesting fact that you can't declare a variable with null.

As soon as you write below piece of code,

var a = null;

It throws an error :

variable initializer is null

Now, as we all know that we can't declare a primitive type to null, so the below statement doesn't make any sense.

int a  = null;

That means, if a developer is initializing a var with null, it definitely wants to put an Object in it, instead of a literal val. If that's the case, my questions here is

Why doesn't compiler consider it to be an Object var and instead throws an error.

On the other hand, if you write below statement, it works perfectly fine :

var a = (Object)null;

What is the reason to declare a var with null

Consider below case where I want to initialize var and want to use it outside condition blocks:

var a = null;
if(some condition) Initialize with some arguments
else Initialize with some other arguments
//Use a variable here

So, in this case, as we want scope of a to be outside of conditional blocks we have a requirement to intialize it will null outside if block, which is not possible using var.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Aman Chhabra
  • 3,824
  • 1
  • 23
  • 39
  • 12
    The compiler can't infer the correct type of the variable. – khelwood Jun 20 '18 at 07:59
  • @khelwood thats my question, why cant compiler consider it to be of type Object – Aman Chhabra Jun 20 '18 at 07:59
  • As we know, there are some requirements when we dont want to create an instance at the first place, but also to keep scope outside condition blocks, we have to initialize variables with null, which is not possible in case of var – Aman Chhabra Jun 20 '18 at 08:01
  • 4
    It could assume it is `Object`, but that's not that useful. If you just wanted an `Object` variable, you might as well declare it as `Object a`. `Object` type variables are not used that often because for most purposes, your variable needs to be some more specific type. – khelwood Jun 20 '18 at 08:01
  • @khelwood I agree, but in that case, cases where we need to expand the scope of a variable will be an issue – Aman Chhabra Jun 20 '18 at 08:02
  • 3
    If you need to initialise a variable to null, and assign something to it later, don't use `var`. – khelwood Jun 20 '18 at 08:07
  • 2
    This is one of the subtleties mentioned in the [**JEP-286**](http://openjdk.java.net/jeps/286)... "*If the initializer has the null type, an error occurs—like a variable without an initializer, this variable is probably intended to be initialized later, and we don't know what type will be wanted.*" – Naman Jun 20 '18 at 08:55
  • 1
    @All Thats very bad to see down vote on this question without any reason. Expect some suggestion when some one is down voting. Moreover, this is a very basic Question as if we can do Object x = null; why cant we can do var x = null; – Aman Chhabra Jun 20 '18 at 09:14
  • 6
    @khelwood Your statement that the compiler can't infer the correct type is wrong. What the compiler can't do is _read your mind_, and imagine what values (other than `null`) you might be planning to assign to this variable in the future. The compiler can correctly infer that the type is the `Null` type (which can only hold null), but because declaring a variable of `Null` type is so obviously useless, it issues an error instead of doing what is obviously not what the user meant. – Brian Goetz Jun 20 '18 at 13:56
  • @BrianGoetz So "The compiler can't infer the correct type of the variable *for your purposes*." I thought it was reasonably clear already. – khelwood Jun 20 '18 at 14:19
  • @BrianGoetz Who told you a `null` value should be inferred as `Void`? `Void` class is simply a class with private constructor and JLS does not do anything special with the class. I can write another class with private constructor which makes the only valid reference `null`. – Jai Jun 20 '18 at 14:34
  • 5
    @Jai I didn't say `Void`. – Brian Goetz Jun 20 '18 at 15:01

9 Answers9

14

There are (at least) three possible type inference strategies the compiler could apply to var o = null:

  • pick Void
  • pick Object
  • look for a later initialization and pick that type

All of them are technically feasible, so the question emerges, which one makes the most sense for developers.

Clearly, Void is pretty useless and I would argue that Object is not much more useful, either. While correct, picking either of these types is unlikely to help the developer write better and more readable code.

The last option, looking for an initialization, was not adopted on purpose to avoid so-called 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`

If the compiler inferred Node for parent because determineParent() returns it, this would compile. But the code is fragile because changes to the last line, might lead to a different type chosen in the first line and hence to compile errors on the second line. That's not good!

We're used to the fact that changing a type's declaration can lead to errors down the road but here the change (line 3), its effect (line 1), and consequent error (line 2) can be pretty far apart, this making it much more complicated for developers to understand or, better, predict what happens.

By keeping the type inference rules simple, developers have it easier to form a simple but correct mental model of what's going on.

Addendum

There are doubts whether option 3, inferring the type from a later initialization, is indeed technically feasible. My opinion (that it is) is based on my understanding of JEP 286, specifically:

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.

Nicolai Parlog
  • 47,972
  • 24
  • 125
  • 255
  • Thanks @Nicolai. That makes much more sense. – Aman Chhabra Jun 20 '18 at 09:47
  • Accepted as answer. – Aman Chhabra Jun 20 '18 at 09:48
  • 2
    I'm sorry, but this is incorrect. Or at least it is speculative ... unless you can point to an **existence proof**. You are asserting that one can define and implement type inference with strong subtyping. I have (in the past) tried to do this on three separate occasions, and gotten roadblocked on the problem. Now, admittedly, I may just not be smart enough. However, I want to see an working example of this in a real programming language implementation before I will believe it. – Stephen C Jun 20 '18 at 11:53
  • And here's the thing. If this was technically possible to do (in a way that gives that gives easy to understand semantics), why did the Java team make this restriction? They are not stupid. – Stephen C Jun 20 '18 at 11:59
  • 5
    @StephenC The (new) addendum explains how I came to that conclusion. – Nicolai Parlog Jun 20 '18 at 17:20
7

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.

The answer is that you either use:

var a = (RealType) null;

or (to be sensible about it) you use a conventional typed declaration:

RealType a = null;

The var form is just a convenience to avoid having to write a specific type. It simply doesn't work when you initialize with null. Inferring Object as the type of a is not useful behavior in the vast majority of cases.


For example, if (hypothetically) a var could be assigned with a null:

    var a = null;            
    if (something) {
        a = someMethodReturningRealType();
    }
    a.someRealTypeMethod();  // Compilation error ... because the inferred
                             // type is java.lang.Object.  Oops!!

And this is the correct way to write the code:

    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.

No. You do not need to do it. You want to do it ... but "want" and "need" are not the same thing.

Moreover, this is a very basic Question as if we can do Object x = null; why cant we can do var x = null;

Because the JLS forbids it. That's it. End of story.

If you don't like it, fine. But this is not a discussion forum. And we are not the people who need to be convinced anyway.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • If you will consider my scenario of conidtional block, even if a is initialized as null, at time of compilation , compiler would now what type to replace for this var. Also, as you mentioned, sensible thing would be to mention exact type, but in that case what is the use of var, if using exact types is more sensible – Aman Chhabra Jun 20 '18 at 08:57
  • 1
    Stephen, the example as shared doesn't quite justify/relate to the question IMHO. @AmanChhabra I would suggest to read http://openjdk.java.net/projects/amber/LVTIstyle.html for "what is the use of var" of yours. – Naman Jun 20 '18 at 08:58
  • 1
    Maybe you wanted to say `var a = (Object)null;` even when it does compile is not much of use either. @Stephen? – Naman Jun 20 '18 at 08:59
  • @nullpointer 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. – Aman Chhabra Jun 20 '18 at 08:59
  • @AmanChhabra - There you are. I have illustrated the flaw in your proposed approach *using your conditional example*. – Stephen C Jun 20 '18 at 11:40
  • @StephenC Thanks but then we are missing the use of var. I think this answer gives a good explanation : https://stackoverflow.com/a/50945044/1262248 – Aman Chhabra Jun 20 '18 at 11:46
  • 6
    @StephenC You (and nearly everyone else who talks about this feature) are using the term "syntactic sugar" incorrectly. The analysis required to compute the type is not merely syntactic; therefore, this is not syntactic sugar. – Brian Goetz Jun 20 '18 at 14:04
3

One could think the question isn't that far-fetched looking at the handling of genric types.

Let's examine it using List<T> list; as an example. If you do not specify the generic type (List list;) the compiler will handle it with the raw type. You can then add everything to the list - of course null. Applying the concepts of raw type and type erasure to var could allow you to declare var o; and assign later on everything to o. But since using the raw type is discouraged it is obvious that it wasn't choosen how var should work.

Regarding the case List<?> list; and applying it to var o = null; you could think of something like this: ? o. But this leads to nothing. In case of List<?> list; the only option that remains is add(null);. Following the theoretical example and trying to be consistent with present concepts this would mean ? o can only be initialized this way:? o = null;. So your intention to decalre a var and initialize it later would syntactically be possible, but symantically it makes no sense as it would be initialized always the same way with null. So it wouldn't provide any additional value but add complexity.

LuCio
  • 5,055
  • 2
  • 18
  • 34
1

From an Oracle JDK 10 page:

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.

So basically, you have to be specific as to the datatype you want, the compiler cannot just assume you want Object or any other type.

So the following will all fail compilation:

var x;

var x = null;

var x = () -> {}
achAmháin
  • 4,176
  • 4
  • 17
  • 40
  • Thanks for sharing the link, but that doesn't change the question of why and how to deal with expanding scope of variables. – Aman Chhabra Jun 20 '18 at 09:00
0

When you declare a variable with the keyword var, it's just a shortcut for writing. At the compilation, the keyword will be replace by the type of this variable. In order to know what is the type of the variable, the initialization must be explicit. If you initialise your variable with null, the compiler can't know what is the type of the variable and so can't replace var. So it's forbidden.

vincrichaud
  • 2,218
  • 17
  • 34
  • That is what my question is as if why the compiler is not considerinig it to be of type Object. – Aman Chhabra Jun 20 '18 at 08:52
  • Because `null` is not an Object. You can't do `x.toString()` with x assign to `null` – vincrichaud Jun 20 '18 at 08:53
  • if we go with your explanation, we can't even do this, Object x = null; x.toString(); as x is null, which is correct, then why can't we do var x = null; – Aman Chhabra Jun 20 '18 at 09:12
  • The goal of var is to declare faster local variable by avoiding to write the whole type of the object. If your object isn't going to be a specific type I can ask you : "why don'y you just declare it as Object ?". I think going through all answers and your comment, the best answer to your question is "Because designer decided that it won't behave like this" – vincrichaud Jun 20 '18 at 09:24
0

This is explicitly listed as an illegal initialization of a local variable in JLS Sec 14.4.1:

It is a compile-time error if T is the null type.

  • ...
  • var g = null; // Illegal: null type

Null is a type in Java:

There is also a special null type, the type of the expression null

However, it is impossible to declare a variable of this type (e.g. you couldn't write null g; or Null g; in a way that would refer to that type), and even if you could, the only possible value it could have would be null:

The null reference is the only possible value of an expression of null type.

So there's no point in allowing you to declare a variable of this type.

Naman
  • 27,789
  • 26
  • 218
  • 353
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Do you mean to say `var g` is of type `Null` if nothing else? – Naman Jun 20 '18 at 08:53
  • @AndyTurner I am sorry, but this is not what I am asking. 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. – Aman Chhabra Jun 20 '18 at 09:07
  • @AmanChhabra you literally ask "Why var in JAVA 10 can not be initialized to null?": you can't because the spec explicitly forbids it; and it explicitly forbids it because it serves no purpose, other than to mislead. Why would you expect the compiler to "know" that you mean "Object g = null;" if for all other cases it infers the most specific type? (e.g. for `var g = ""`, you wouldn't expect that to be `Object`). – Andy Turner Jun 20 '18 at 14:19
  • @AndyTurner Why do you think it will serve no purpose, if you think so, please look into the example I mentioned in the question as if how we could have extended the scope if null would have been allowed in var. – Aman Chhabra Jun 20 '18 at 15:47
  • @AndyTurner Moreover, we don't leave everything on design and try to understand why it has been decided to the max we can. As initializing a type variable with null was always possible, same was expected with var as well. – Aman Chhabra Jun 20 '18 at 15:49
  • 1
    @AmanChhabra I said that a variable of null type would serve no purpose: it has exactly one value, namely null, so you would never need to out its value into a variable. In terms of why it's not useful for `var g = null;` to work: this would be inconsistent with the behaviour of `var` elsewhere, since it takes the most specifically inferrable type: that type would be null, and there is neither a point to declaring a null-typed variable, nor is it allowed otherwise in the language. – Andy Turner Jun 20 '18 at 15:59
  • @AndyTurner I think you came back to the starting of the question. Question itself is why it is not picking the type wherever it is initialized after initializing from null and in worst case, if it is not initialized anywhere, why cant it be considered as an Object type? Moreover, how do you think issue to expand scope can be resolved as mentioned in my question – Aman Chhabra Jun 20 '18 at 16:01
  • @AndyTurner Moreover, I would suggest you to read below answer as that makes quite sense: https://stackoverflow.com/a/50945044/1262248 – Aman Chhabra Jun 20 '18 at 16:02
  • @AmanChhabra you didn't ask "why couldn't I have done this in Java": you asked "why *can't* I do this", to which the answer is because: they designed it this way. They could have done it differently, but decided this way met their design objectives. You may have made different choices, but this is how it is. – Andy Turner Jun 20 '18 at 16:06
  • @AndyTurner Thanks for your time. I am not sure what is happening here, but I am seriously struggling to ask the reason behind this decision. I already know that it is not allowed and infact I pasted the error as well which it give, I am more considered about "Why". It will be great if we all can work together to get to bottom of this and understand the reason in detail – Aman Chhabra Jun 20 '18 at 16:08
0

You cannot initialize a var variable to null. By assigning null, it is not clear what the type should be, since in Java, any object reference can be null. In the following example, because there is no predefined data type for a null value, the compiler is not able to interpret a type 'test', which would cause a complication error.

var test=null;

//Compilation error because compiler cannot infer type for local variable count since any Java object reference can be null
Rafał Sokalski
  • 1,817
  • 2
  • 17
  • 29
  • I have two points to share here: 1) Object is parent, so any case it can consume everything 2) Anyways till compile time, compiler will get to know if we tried to put any specific instance of any class which it can replace with. Hope that makes sense. – Aman Chhabra Jun 20 '18 at 09:02
-1

From the Java 10 Local Variable Type Inference page:

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 is not a type, so the compiler can't infer the type of the RHS expression.

Maroun
  • 94,125
  • 30
  • 188
  • 241
  • 3
    I didn't downvote, but: ["There is also a special null type"](https://docs.oracle.com/javase/specs/jls/se10/html/jls-4.html#jls-4.1), so `null` is a type (or, rather, it is the only value of the null type). – Andy Turner Jun 20 '18 at 08:08
  • @Maroun That is what my question is 1) Why can't it be changed to Object class? 2) At compile time, in any case compiler will get to know the type wherever this null var be initialized. – Aman Chhabra Jun 20 '18 at 09:10
-2

var is used as a lazy type. When you do var a = new Foo();, the compiler knows that a is of Foo type, this will enable you to call members of Foo using a at compile time.

var a = null; tells the compiler nothing. If the compiler treats a as Object class, then there is no purpose in using var type - you could have easily used Object a = null;.

Update

I think you are thinking this is working like the var in Javascript, which is absolutely wrong.

From one of your comments somewhere else:

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.

This clearly means that you are looking for a var capability that is just like what the Javascript is doing.

Why is it wrong in Java? Consider this:

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) {}

Should the compiler allow this? No. You will only know what type var is only at runtime. The compiler can never guess what this is at compile-time.

This is why var can never totally replace the traditional way of defining an explicit type. Always remember, var is just a syntactic sugar.

Jai
  • 8,165
  • 2
  • 21
  • 52
  • I have already shared a scenario in my question where we will be requiring to initialize a variable with null. Moreover, in any case, as till compile time , variable must have been initialized with specific class, compiler can change it to the same type – Aman Chhabra Jun 20 '18 at 09:09
  • @AmanChhabra `var` type is a lazy type. Its purpose is to make it easier for the programmers to use local variables so that we do not need to think about the type and write out the type. When converted to bytecodes, a `var` variable actually has a concrete type. You must tell the compiler what type it is. `var` is just a synthetic sugar, it does not have a separate function other than to make things easier to write. **Don't ever use it as if this is the Javascript `var`**, where the type changes dynamically at runtime. For your scenario you should use the traditional method. – Jai Jun 20 '18 at 09:41
  • Updated my answer. – Jai Jun 20 '18 at 09:53
  • Then in your case, effectively final is wrong, because developer can change that variable anywhere. That is what we expect compiler to do, in your case, it needs to throw a compilation saying both classes doesn't belong to a single parent. Practically its possible. My question is to understand the reason why it was not allowed, and I think below answer explains it well: https://stackoverflow.com/a/50945044/1262248 – Aman Chhabra Jun 20 '18 at 09:59
  • 4
    `var` is neither lazy, nor is it "just" syntactic sugar. – Brian Goetz Jun 20 '18 at 13:58
  • @BrianGoetz I'll be interested if you could tell me if there is anything `var` could do that would not be possible (or executes differently) without `var`. – Jai Jun 20 '18 at 14:19
  • 4
    @Jai Yes, there is. Try `var x = List.of("one", 2)`. Or `var x = new Object() { int a; }`. You can't write those types down. (Java has several categories of non-denotable types: the null type, anonymous class types, capture types, and intersection types.) – Brian Goetz Jun 20 '18 at 15:03
  • @BrianGoetz `List x = List.of("one", 2)` – Jai Jun 20 '18 at 15:10
  • 5
    @Jai Not the same type at all. `List` is a supertype of the actual type of the initializer; that type is not denotable. – Brian Goetz Jun 20 '18 at 15:29