C++ member variable semantics - java

I have a rather simple question with an inkling as to what the answer is.
My generalized question:
What is actually going on when you declare a member variable, be it public or private, and for all permutations of variable types, e.g. static vs const vs regular variables?
class some_class
{
private:
static const std::string str;
public:
...
}
I have kind of realized that in C++ there is no notion of a non-variable, that is, a non-constructed variable as I was kind of taught to believe exists with languages like Java. The same may also be true in Java, however it is not the way I was taught to think of things so I'm trying to come up with the correct way to think of these non-initialized variables.
public class main {
public static void main(String[] args) {
String str; // A kind of non-variable, or non-constructed variable (refers to null).
str = new String(); // Now this variable actually refers to an object rather than null, it is a constructed variable.
}
}
Since C++ allows you to initialize member variables in the constructor through initializer lists, and I have proven to myself via use of a debugger that the variable doesn't exist before it is initialized through the initializer list (either explicitly or by default), what is, then, actually going on behind the scenes when you declare the member variable?

Tricky question -- it's ambiguous depending on perspective.
From a pseudo-machine perspective, normally adding a non-static plain old data type to a class makes that class type bigger. The compiler also figures out how to align it and relative memory offsets to address it relative to the object in the resulting machine code.
This is pseudo-machine level because at the machine level, data types don't actually exist: just raw bits and bytes, registers, instructions, things like that.
When you add a non-primitive user-defined type, this recurses and the compiler generates the instructions to access the members of the member and so on.
From a higher level, adding members to a class makes the member accessible from instances (objects) of the class. The constructor initializes those members, and the destructor destroys them (recursively triggering destructors of members that have non-trivial destructors, and likewise for constructors in the construction phase).
Yet your example is a static member. For static members, they get stored in a data segment at the machine level and the compiler generates the code to access those static members from the data segment.
Some of this might be a bit confusing. C++ shares its legacy with C which is a hardware-level language, and its static compilation and linking affects its design. So while it can go pretty high-level, a lot of its constructs are still tied to how the hardware, compiler, and linker does things, whereas in Java, the language can make some more sensible choices in favor of programmer convenience without a language design that somewhat reflects all of these things.

Yes and no.
A variable of class type in Java is really a pointer. Unlike C and C++ pointers, it doesn't support pointer arithmetic (but that's not essential to being a pointer--for example, pointers in Pascal didn't support arithmetic either).
So, when you define a variable of class type in Java: String str;, it's pretty much equivalent to defining a pointer in C++: String *str;. You can then assign a new (or existing) String object to that, as you've shown.
Now, it's certainly possible to achieve roughly the same effect in C++ by explicitly using a pointer (or reference). There are differences though. If you use a pointer, you have to explicitly dereference that pointer to get the object to which it refers. If you use a reference, you must initialize the reference--and once you do so, that reference can never refer to any object other than the one with which it was initialized.
There are also some special rules for const variables in C++. In many cases, where you're just defining a symbolic name for a value:
static const int size = 1234;
...and you never use that variable in a way that requires it to have an address (e.g., taking its address), it usually won't be assigned an address at all. In other words, the compiler will know the value you've associated with that name, but when compilation is finished, the compiler will have substituted the value anywhere you've used that name, so the variable (as such) basically disappears (though if you have the compiler generate debugging information, it'll usually retain enough to know and display its name/type correctly).
C++ does have one other case where a variable is a little like a Java "zombie" object that's been declared but not initialized. If you move from an object: object x = std::move(y);, after the move is complete the source of the move (y in this case) can be in a rather strange state where it exists, but about all you can really do with it is assign a new value to it. Just for example, in the case of a string, it might be an empty string--but it also could retain exactly the value it had before the move, or it could contain some other value (e.g., the value that the destination string held before the move).
Even that, however, is a little bit different--even though you don't know its state, it's still an object that should maintain the invariants of its class--for example, if you move from a string, and then ask for the string's length, that length should match up with what the string actually contains--if (for example) you print it out, you don't know what string will print out, but you should not get an equivalent of a NullPointerException--if it's an empty string, it just won't print anything out. If it's a non-empty string, the length of the data that's printed out should match up with what its .size() indicates, and so on.
The other obviously similar C++ type would be a pointer. An uninitialized pointer does not point to an object. The pointer itself exists though--it just doesn't refer to anything. Attempting to dereference it could give some sort of error message telling you that you've attempted to use a null pointer--but unless it has static storage duration, or you've explicitly initialized it, there's no guarantee that it'll be a null pointer either--attempting to dereference it could give a garbage value, throw an exception, or almost anything else (i.e., it's undefined behavior).

Related

Do something when a variable is (re)assigned Java

This is a far-fetched question and I am not sure how to approach this problem, so I am open to other workarounds or proposals. As far as I am aware, what I am trying to do is impossible, but I'd like a second input.
Assume we have the following Java code:
int val = 4;
I am curious as to if some sort of function is called when this statement is executed. An overridable function that assigns a given memory location to this value, or something of that nature.
My objective would be to override that function and store this data here and in a file elsewhere as well.
This would need to work for all data types and for reassignments such as that shown below.
val = getNumber(); // Returns 6;
I would have some sort of direction if I was working with Python, but unfortunately, that is not the case.
My best idea for a solution is to call a function that simply returns a provided argument. Due to the application of this, I'd like to avoid this and keep the usage of this framework as conventional as possible.
Thanks!
I don't think any kind of function happens when we assign values. However when we assign a value to a primitive type(int, double...) variable the value is stored in the stack memory. If the data is reference type (String...), then it is stored in the heap memory. Only the reference address will be stored in the stack. Whenever you decide to change the state of that particular variable (field value) the new value will be stored in the stack overriding the previous value. So, you don't have to worry about methods to override using a method.
If you want to deny access to a variable outside the class, but still change the state of that variable, then you can use encapsulation concept of OOP in java.
For further clarification refer this article about stack vs. heap

Why doesn't C++ support smart analysis of uninitialised variables? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
Here's a use case Java covers:
int x; // UNINITIALISED
if (condition){
x = 0; // init x;
}
else return;
use(x); // INITIALISED
Note:
There is at no point uninitialized memory (for x)
There is no non-deterministic state
You can not use an uninitialised variable
The variable is truly uninitialized, not null or 0 - however this doesn't leak into the run-time; I presume the compiler restructures the code, at compile time so x is initialised at deceleration only when necessary. x may be uninitialised in the code, however at runtime it's guaranteed to be initialised (which is why there's an error when you try to use it before initialisation).
From what I've seen/heard, C++ does not support this and will always run a default constructor - if available - even when it's unnecessary.
Interestingly, when a default constructor is unavailable, you actually get the same behaviour with uninitialized variables in Java. So, it's clearly possible for the C++ compiler to handle this behaviour (because it is with classes with no default constructor). So, why is it not supported fully?
I think syntax like int x = delete or [[no-default]] int x for this (as to not break any existing code) would be quite intuitive.
Please note, there's a difference between int x and int x = 0 - in Java.
Contrary to #akuzminykhs comment, it's not "initialised to 0" - I don't know why people are upvoting it
In the example, if you comment out the init x, you get an error - this will not happen in C++ - you will get undefined behaviour, because you're using uninitialised memory.
int x; // UNINITIALISED
if (Math.random() > 0.5) {
// x = 0; // init x;
} else return;
use(x); // ERROR: Variable 'x' might not have been initialized
I understand Java uses pointers, that is irrelevant to the essence of this question. This has nothing to do with the run-time, it's compile-time analysis.
Another scenario:
bool valid = false;
Type lastValid; // unnecessary default initialisation (only in C++)
for (auto object : objects)
try {
lastValid = process(object);
valid = true;
} catch (std::invalid_argument) {}
if (valid) use(lastValid);
Another Scenario: Forcing uninitialised declaration of member with a default constructor
More info: someone used this code in an example comparison between Java and C++
Example.java
Type x; // Reserve stack space for a pointer.
Example.cpp
Type x; // Reserve stack space for the object (and initialize it).
One mistake everyone seems to be making is like the comment above // Reserve stack space for a pointer, this is not what happens in Java.
The variable doesn't exist at that point during run-time. There's no allocation, no reservation, simply no unnecessary computation. The compiler is smart enough to do this. Again, this is not to do with the run-time; comparisons between the differences of java and c++ are misleading.
The reason you get an error when trying to use the uninitialised variable, is because the variable literally does not exist (at that point) during run-time (as it wasn't initialised). This is done using analysis of the code tree
Java is a very different language to C++.
In Java:
Variables without an explicit initialiser are initialised to a default value.
Variables of class types do not hold the memory of the object in-place. Rather, a variable is a pointer/reference to an object stored in the heap (called free store in C++). The closest thing comparable to Java object variable in C++ is std::shared_ptr. It's not an exact equivalent, since (besides the different lifetime management strategy) indirection through a Java variable is implicit (like C++ reference) while indirection through a C++ shared pointer is explicit through the use of indirection operator.
The indirection makes it possible for Java variable to be null. A null variable doesn't point to any object. The default value for an object variable is null.
In case of primitive types such as int, the variable is not a pointer. The default value for integer is 0.
In C++:
Variables are not implicitly references or pointers. A pointer to a class is a different type from that class.
A variable that is not a reference nor a pointer does not involve indirection. It doesn't point to an object elsewhere and because there is no pointer, there is no null. If you create such variable, then an object of that type is created.
When given no initialiser, the way a variable is initialised depends on the type as well as the storage class of the variable. For example, fundamental types such as pointers and integers, as well as trivially default constructible classes are left with an indeterminate value unless they have static storage class. Construction of such object requires no instructions to be executed by the CPU. This is fast, but difficult and potentially dangerous because there is no way to distinguish whether a value is indeterminate or well defined.
To answer the question "why": Because that is the way the language was designed.
Using indirection (which Java does) is slower than not using indirection. Using heap allocation (which Java does) is slower than allocating in-place. Always initialising value to something such as integer to 0 (which Java does) is (marginally) slower than not initialising when that value would later always be over-written. Runtime performance is often a reason behind design choices that make a language more complex or harder to use. Runtime performance is the main reason why most people use C++.
You can use a std::optional for this. Example:
std::optional<Type> x;
if (condition) {
x.emplace(); // default construct Type
}
else return;
use(x.value()); // will throw std::bad_optional_access if x is not actually initialized
For built-in types, C++ does allow uninitialized variables, so your code will compile fine in C++ too.
For class types, Java is using pointers, so can you:
#include <memory>
class B {
public:
B(int);
};
bool condition();
void use(B const&);
void foo() {
std::unique_ptr<B> b{nullptr};
if (condition())
b = std::make_unique<B>(42);
else
return;
use(*b);
}
But I think you want to know is why C++ doesn't let you leave you a variable uninitialized when it is possible statically to prove that the variable will not be used without being initialized. I can't clearly answer the "why", you should probably ask that from someone on isocpp committee. That's a feature newer languages like Kotlin enjoy but C++ hasn't caught up with yet. Herb Sutter has mentioned it in a talk I know of:
You can find that talk here:
... what if you could simply declare and then initialize before and make sure your first use is an initialization but they don't have to be in the same line. Well, we have the same tools; we can say what the definite first use is in every path and require initialization there before the variable's used ...
Then he goes on to talk about how we can introduce that into C++ type-system if memory serves me right, but considering this talk is from 2020, even in the best-case scenario, that feature will not be in C++ for the foreseeable future.
You can also have a look into "immediately invoked lambda expressions" as they could help here and they can also help you make your code more const-correct.
First of all, semantics in Java and C++ are quite different.
In Java each local variable must go through mandatory declaration -> first assignment sequence before being read where first assignment performs initialization. There is no special concept or syntax for initialization. Writing int x = 0; is equivalent to int x; x = 0;.
In C++ things are completely different. There is no special concept or syntax for declaring local variables. There is no way to postpone initialization. But there are many different special syntaxes for initialization. int x = 0; is copy initialization syntax, not just declaration with first assignment on the same line. There is also so called vacuous initialization which happens in case of int x; - even though initialization is there it does not actually initialize value with anything.
But what is more important is that language capabilities in Java and C++ are quite different as well.
In Java there is no support for pass-by-reference or pass-by-pointer. There is no way to share local variable in between threads. Changes to local variable are always observable by compiler and figuring out whether it is being read without prior assignment requires only relatively straightforward control flow analysis of current scope.
In C++ there is support for both pass-by-reference and pass-by-pointer. Local variables can be accessed from different threads. But there is even more than that, local variables can be modified by means completely unknown to compiler. That's what happens when one deals with DMA (Direct Memory Access) capable devices all the time. So there is no way for analysis to be smart enough to cover all these cases.

Working of dot operator when applied to objects

I was going through the working principles of the dot operator, which is the same as the * operator in C++, that is they both solve our purpose of dereferencing. When we apply dot to some class, or some object, then we enter the heap of the class/object, and have an access to the static variables or methods of class, and instance variables as well in the case of methods.
Now my doubt is that while I'm applying some method to an object by using the dot operator, that is, now I'm able to use the data stored inside the instance variables inside my methods. But now when I try and change the data of those instance variables, it tends to change. From what I know I can't change the data inside the methods of languages like java where things work pass by value.
Can someone explain this to me?
From what I know I can't change the data inside the methods of languages like java where things work pass by value.
Java is pass by value ... but in the case of reference types (arrays or instances of classes), the values are references.
The dot "operator" is performing an operation on a reference.
In Java, you normally just forget about the fact that there is a reference there and (mentally) treat the object and the reference to the object as being the same thing. (There is a reference available, even in the case of instance method referring to the fields if the current object ... it is this, and you are using it implicitly.)
This is different to C++. In C++, object types and reference (pointer) typed are distinct, and their values (objects and references) are likewise distinct. You can create, pass, and use, an object without having a reference to it. And the compiler does not prevent you from having a reference that doesn't actually point to an object. The behavior of the latter is undefined ...
Also in C++, you can create and pass references to things that are not objects. For example, you can pass a reference to a variable. You can't do that in Java.

Why is assignment to 'this' not allowed in java?

The error I get from the compiler is "The left hand side of an assignment must be a variable". My use case is deep copying, but is not really relevant.
In C++, one can assign to *this.
The question is not how to circumvent assignment to this. It's very simple, but rather what rationale is there behind the decision not to make this a variable.
Are the reasons technical or conceptual?
My guess so far - the possibility of rebuilding an Object in a random method is error-prone (conceptual), but technically possible.
Please restrain from variations of "because java specs say so". I would like to know the reason for the decision.
In C++, one can assign to *this
Yes, but you can't do this = something in C++, which I actually believe is a closer match for what you're asking about on the Java side here.
[...] what rationale is there behind the decision not to make this a variable.
I would say clarity / readability.
this was chosen to be a reserved word, probably since it's not passed as an explicit argument to a method. Using it as an ordinary parameter and being able to reassign a new value to it, would mess up readability severely.
In fact, many people argue that you shouldn't change argument-variables at all, for this very reason.
Are the reasons technical or conceptual?
Mostly conceptual I would presume. A few technical quirks would arise though. If you could reassign a value to this, you could completely hide instance variables behind local variables for example.
My guess so far - the possibility of rebuilding an Object in a random method is error-prone (conceptual), but technically possible.
I'm not sure I understand this statement fully, but yes, error prone is probably the primary reason behind the decision to make it a keyword and not a variable.
because this is final,
this is keyword, not a variable. and you can't assign something to keyword. now for a min consider if it were a reference variable in design spec..and see the example below
and it holds implicit reference to the object calling method. and it is used for reference purpose only, now consider you assign something to this so won't it break everything ?
Example
consider the following code from String class (Note: below code contains compilation error it is just to demonstrate OP the situation)
public CharSequence subSequence(int beginIndex, int endIndex) {
//if you assign something here
this = "XYZ" ;
// you can imagine the zoombie situation here
return this.substring(beginIndex, endIndex);
}
Are the reasons technical or conceptual?
IMO, conceptual.
The this keyword is a short hand for "the reference to the object whose method you are currently executing". You can't change what that object is. It simply makes no sense in the Java execution model.
Since it makes no sense for this to change, there is no sense in making it a variable.
(Note that in C++ you are assigning to *this, not this. And in Java there is no * operator and no real equivalent to it.)
If you take the view that you could change the target object for a method in mid flight, then here are some counter questions.
What is the use of doing this? What problems would this (hypothetical) linguistic feature help you solve ... that can't be solved in a more easy-to-understand way?
How would you deal with mutexes? For instance, what would happen if you assign to this in the middle of a synchronized method ... and does the proposed semantic make sense? (The problem is that you either end up executing in synchronized method on an object that you don't have a lock on ... or you have to unlock the old this and lock the new this with the complications that that entails. And besides, how does this make sense in terms of what mutexes are designed to achieve?)
How would you make sense of something like this:
class Animal {
foo(Animal other) {
this = other;
// At this point we could be executing the overridden
// Animal version of the foo method ... on a Llama.
}
}
class Llama {
foo(Animal other) {
}
}
Sure you can ascribe a semantic to this but:
you've broken encapsulation of the subclass in a way that is hard to understand, and
you've not actually achieved anything particularly useful.
If you try seriously to answer these questions, I expect you'll come to the conclusion that it would have been a bad idea to implement this. (But if you do have satisfactory answers, I'd encourage you to write them up and post them as your own Answer to your Question!)
But in reality, I doubt that the Java designers even gave this idea more than a moment's consideration. (And rightly so, IMO)
The *this = ... form of C++ is really just a shorthand for a sequence of assignments of the the attributes of the current object. We can already do that in Java ... with a sequence of normal assignments. There is certainly no need for new syntax to support this. (How often does a class reinitialize itself from the state of another class?)
I note that you commented thus:
I wonder what the semantics of this = xy; should be. What do you think it should do? – JimmyB Nov 2 '11 at 12:18
Provided xy is of the right type, the reference of this would be set to xy, making the "original" object gc-eligible - kostja Nov 2 '11 at 12:24
That won't work.
The value of this is (effectively) passed by value to the method when the method is invoked. The callee doesn't know where the this reference came from.
Even if it did, that's only one place where the reference is held. Unless null is assigned in all places, the object cannot be eligible of garbage collection.
Ignoring the fact that this is technically impossible, I do not think that your idea would be useful OR conducive to writing readable / maintainable code. Consider this:
public class MyClass {
public void kill(MyClass other) {
this = other;
}
}
MyClass mine = new MyClass();
....
mine.kill(new MyClass());
// 'mine' is now null!
Why would you want to do that? Supposing that the method name was something innocuous rather than kill, would you expect the method to be able to zap the value of mine?
I don't. In fact, I think that this would be a misfeature: useless and dangerous.
Even without these nasty "make it unreachable" semantics, I don't actually see any good use-cases for modifying this.
this isn't even a variable. It's a keyword, as defined in the Java Language Specification:
When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed
So, it's not possible as it's not possible to assign a value to while.
The this in Java is a part of the language, a key word, not a simple variable. It was made for accessing an object from one of its methods, not another object. Assigning another object to it would cause a mess. If you want to save another objects reference in your object, just create a new variable.
The reason is just conceptual. this was made for accessing an Object itself, for example to return it in a method. Like I said, it would cause a mess if you would assign another reference to it. Tell me a reason why altering this would make sense.
Assigning to (*this) in C++ performs a copy operation -- treating the object as a value-type.
Java does not use the concept of a value-type for classes. Object assignment is always by-reference.
To copy an object as if it were a value-type: How do I copy an object in Java?
The terminology used for Java is confusing though: Is Java “pass-by-reference” or “pass-by-value”
Answer: Java passes references by value. (from here)
In other words, because Java never treats non-primitives as value-types, every class-type variable is a reference (effectively a pointer).
So when I say, "object assignment is always by-reference", it might be more technically accurate to phrase that as "object assignment is always by the value of the reference".
The practical implication of the distinction drawn by Java always being pass-by-value is embodied in the question "How do I make my swap function in java?", and its answer: You can't. Languages such as C and C++ are able to provide swap functions because they, unlike Java, allow you to assign from any variable by using a reference to that variable -- thus allowing you to change its value (if non-const) without changing the contents of the object that it previously referenced.
It could make your head spin to try to think this all the way through, but here goes nothing...
Java class-type variables are always "references" which are effectively pointers.
Java pointers are primitive types.
Java assignment is always by the value of the underlying primitive (the pointer in this case).
Java simply has no mechanism equivalent to C/C++ pass-by-reference that would allow you to indirectly modify a free-standing primitive type, which may be a "pointer" such as this.
Additionally, it is interesting to note that C++ actually has two different syntaxes for pass-by-reference. One is based on explicit pointers, and was inherited from the C language. The other is based on the C++ reference-type operator &. [There is also the C++ smart pointer form of reference management, but that is more akin to Java-like semantics -- where the references themselves are passed by value.]
Note: In the above discussion assign-by and pass-by are generally interchangeable terminology. Underlying any assignment, is a conceptual operator function that performs the assignment based on the right-hand-side object being passed in.
So coming back to the original question: If you could assign to this in Java, that would imply changing the value of the reference held by this. That is actually equivalent to assigning directly to this in C++, which is not legal in that language either.
In both Java and C++, this is effectively a pointer that cannot be modified. Java seems different because it uses the . operator to dereference the pointer -- which, if you're used to C++ syntax, gives you the impression that it isn't one.
You can, of course, write something in Java that is similar to a C++ copy constructor, but unlike with C++, there is no way of getting around the fact that the implementation will need to be supplied in terms of an explicit member-wise initialization. [In C++ you can avoid this, ultimately, only because the compiler will provide a member-wise implementation of the assignment operator for you.]
The Java limitation that you can't copy to this as a whole is sort-of artificial though. You can achieve exactly the same result by writing it out member-wise, but the language just doesn't have a natural way of specifying such an operation to be performed on a this -- the C++ syntax, (*this) doesn't have an analogue in Java.
And, in fact, there is no built-in operation in Java that reassigns the contents of any existing object -- even if it's not referred to as this. [Such an operation is probably more important for stack-based objects such as are common in C++.]
Regarding the use-case of performing a deep copy: It's complicated in Java.
For C++, a value-type-oriented language. The semantic intention of assignment is generally obvious. If I say a=b, I typically want a to become and independent clone of b, containing an equal value. C++ does this automatically for assignment, and there are plans to automate the process, also, for the comparison.
For Java, and other reference-oriented languages, copying an object, in a generic sense, has ambiguous meaning. Primitives aside, Java doesn't differentiate between value-types and reference-types, so copying an object has to consider every nested class-type member (including those of the parent) and decide, on a case-by-case basis, if that member object should be copied or just referenced. If left to default implementations, there is a very good chance that result would not be what you want.
Comparing objects for equality in Java suffers from the same ambiguities.
Based on all of this, the answer to the underlying question: why can't I copy an object by some simple, automatically generated, operation on this, is that fundamentally, Java doesn't have a clear notion of what it means to copy an object.
One last point, to answer the literal question:
What rationale is there behind the decision not to make this a variable?
It would simply be pointless to do so. The value of this is just a pointer that has been passed to a function, and if you were able to change the value of this, it could not directly affect whatever object, or reference, was used to invoke that method. After all, Java is pass-by-value.
Assigning to *this in C++ isn't equivalent to assigning this in Java. Assigning this is, and it isn't legal in either language.

Why does javac complain about not initialized variable?

For this Java code:
String var;
clazz.doSomething(var);
Why does the compiler report this error:
Variable 'var' might not have been initialized
I thought all variables or references were initialized to null. Why do you need to do:
String var = null;
??
Instance and class variables are initialized to null (or 0), but local variables are not.
See §4.12.5 of the JLS for a very detailed explanation which says basically the same thing:
Every variable in a program must have a value before its value is used:
Each class variable, instance variable, or array component is initialized with a default value when it is created:
[snipped out list of all default values]
Each method parameter is initialized to the corresponding argument value provided by the invoker of the method.
Each constructor parameter is initialized to the corresponding argument value provided by a class instance creation expression or explicit constructor invocation.
An exception-handler parameter is initialized to the thrown object representing the exception.
A local variable must be explicitly given a value before it is used, by either initialization or assignment, in a way that can be verified by the compiler using the rules for definite assignment.
It's because Java is being very helpful (as much as possible).
It will use this same logic to catch some very interesting edge-cases that you might have missed. For instance:
int x;
if(cond2)
x=2;
else if(cond3)
x=3;
System.out.println("X was:"+x);
This will fail because there was an else case that wasn't specified. The fact is, an else case here should absolutely be specified, even if it's just an error (The same is true of a default: condition in a switch statement).
What you should take away from this, interestingly enough, is don't ever initialize your local variables until you figure out that you actually have to do so. If you are in the habit of always saying "int x=0;" you will prevent this fantastic "bad logic" detector from functioning. This error has saved me time more than once.
Ditto on Bill K. I add:
The Java compiler can protect you from hurting yourself by failing to set a variable before using it within a function. Thus it explicitly does NOT set a default value, as Bill K describes.
But when it comes to class variables, it would be very difficult for the compiler to do this for you. A class variable could be set by any function in the class. It would be very difficult for the compiler to determine all possible orders in which functions might be called. At the very least it would have to analyze all the classes in the system that call any function in this class. It might well have to examine the contents of any data files or database and somehow predict what inputs users will make. At best the task would be extremely complex, at worst impossible. So for class variables, it makes sense to provide a reliable default. That default is, basically, to fill the field with bits of zero, so you get null for references, zero for integers, false for booleans, etc.
As Bill says, you should definitely NOT get in the habit of automatically initializing variables when you declare them. Only initialize variables at declaration time if this really make sense in the context of your program. Like, if 99% of the time you want x to be 42, but inside some IF condition you might discover that this is a special case and x should be 666, then fine, start out with "int x=42;" and inside the IF override this. But in the more normal case, where you figure out the value based on whatever conditions, don't initialize to an arbitrary number. Just fill it with the calculated value. Then if you make a logic error and fail to set a value under some combination of conditions, the compiler can tell you that you screwed up rather than the user.
PS I've seen a lot of lame programs that say things like:
HashMap myMap=new HashMap();
myMap=getBunchOfData();
Why create an object to initialize the variable when you know you are promptly going to throw this object away a millisecond later? That's just a waste of time.
Edit
To take a trivial example, suppose you wrote this:
int foo;
if (bar<0)
foo=1;
else if (bar>0)
foo=2;
processSomething(foo);
This will throw an error at compile time, because the compiler will notice that when bar==0, you never set foo, but then you try to use it.
But if you initialize foo to a dummy value, like
int foo=0;
if (bar<0)
foo=1;
else if (bar>0)
foo=2;
processSomething(foo);
Then the compiler will see that no matter what the value of bar, foo gets set to something, so it will not produce an error. If what you really want is for foo to be 0 when bar is 0, then this is fine. But if what really happened is that you meant one of the tests to be <= or >= or you meant to include a final else for when bar==0, then you've tricked the compiler into failing to detect your error. And by the way, that's way I think such a construct is poor coding style: Not only can the compiler not be sure what you intended, but neither can a future maintenance programmer.
I like Bill K's point about letting the compiler work for you- I had fallen into initializing every automatic variable because it 'seemed like the Java thing to do'. I'd failed to understand that class variables (ie persistent things that constructors worry about) and automatic variables (some counter, etc) are different, even though EVERYTHING is a class in Java.
So I went back and removed the initialization I'd be using, for example
List <Thing> somethings = new List<Thing>();
somethings.add(somethingElse); // <--- this is completely unnecessary
Nice. I'd been getting a compiler warning for
List<Thing> somethings = new List();
and I'd thought the problem was lack of initialization. WRONG. The problem was I hadn't understood the rules and I needed the <Thing> identified in the "new", not any actual items of type <Thing> created.
(Next I need to learn how to put literal less-than and greater-than signs into HTML!)
I don't know the logic behind it, but local variables are not initialized to null. I guess to make your life easy. They could have done it with class variables if it were possible. It doesn't mean you have to have it initialized in the beginning. This is fine :
MyClass cls;
if (condition) {
cls = something;
else
cls = something_else;
Sure, if you've really got two lines on top of each other as you show- declare it, fill it, no need for a default constructor. But, for example, if you want to declare something once and use it several or many times, the default constructor or null declaration is relevant. Or is the pointer to an object so lightweight that its better to allocate it over and over inside a loop, because the allocation of the pointer is so much less than the instantiation of the object? (Presumably there's a valid reason for a new object at each step of the loop).
Bill IV

Categories

Resources