For example can I have
class Dog
{
int legs;
Bone chewy;
Bone squeeky;
public Dog (Bone chewyO, Bone squeekyO)
{
this.legs = 4;
chewy = chewyO;
squeeky = squeekyO;
}
...
For example can I have
class Dog
{
int legs;
Bone chewy;
Bone squeeky;
public Dog (Bone chewyO, Bone squeekyO)
{
this.legs = 4;
chewy = chewyO;
squeeky = squeekyO;
}
...
Definitely.
Just bear in mind the parameter list should be separated by ,
instead of ;
public Dog (Bone chewy, Bone squeekyO)
Try this:
// Dog.java
class Bone {
private String name;
Bone( String name ){
this.name = name;
}
public String toString() {
return this.name;
}
}
class Dog {
int legs;
Bone chewy;
Bone squeeky;
public Dog (Bone chewyO, Bone squeekyO) {
this.legs = 4;
this.chewy = chewyO;
this.squeeky = squeekyO;
}
public String toString() {
return String.format("This dogs has: %d legs, and two bones: %s, %s", legs, chewy, squeeky );
}
public static void main( String [] args ) {
Bone a = new Bone("chewy");
Bone b = new Bone("squeeky");
Dog dog = new Dog( a, b );
System.out.println( dog );
}
}
$javac Dog.java
$java Dog
This dogs has: 4 legs, and two bones: chewy, squeeky
If you have a constructor that takes arguments, those arguments will be fully evaluated before the constructor is invoked. This means that if those arguments are non-null
references to objects, those objects already exist (or else you couldn't possibly have gotten references to them!).
From JLS 15.9.4 Run-time Evaluation of Class Instance Creation Expressions:
Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.
Next, the selected constructor of the specified class type is invoked.
Note that this also applies to regular methods. Java does not have lazy evaluation: all arguments must be fully evaluated before any method/constructor using them is invoked. A lazy evaluation can be a nice feature, but Java keeps it simple by just evaluating all arguments before invoking any method/constructor.
So now that we got that out of the way, let's talk about your question:
Can an object be created whose instance variables are already existant objects?
First let's clarify something: your instance variables (also referred to as "fields" in Java) will actually be references to those objects! So, the real question to ask is the following:
Can an object be created whose fields are references to already existent objects?
The answer is of course yes. If your constructor takes reference type arguments and they're not null
, then those objects already exist, and it's up to you what you do with them. You may invoke methods on them, you may access their fields, and yes, you most certainly can assign their references to your fields.
However, you should be aware that if those objects are "mutable", then they may be modified by other entities without your knowledge! Now, perhaps this is what you want (e.g. several Dog
instances can share the same perishable Bone
), but sometimes you don't need to keep the references, but rather the value, which you do not want others to modify without going through you. In this case, you can make what is called a "defensive copy" of the object. You will end up with your own private copy of the object, having the same "value" (as defined for the type), so that any mutation to the original copy will not affect your own.
Now this goes to the subject of defensive programming, possibly cloning (deep vs shallow), the merits of immutable objects for value types, etc, and it's way too much to be discussed on this already long answer, but do search stackoverflow on some of these topics: your questions may already have been asked (and answered!).
I also couldn't help but notice how you rather awkwardly named chewyO
and squeekyO
.
The following is an excerpt from Java Puzzlers, Glossary of Name Reuse.
A variable, method, or type shadows all variables, methods, or types, respectively, with the same name in a textually enclosing scope. If an entity is shadowed, you cannot refer to it by its simple name; depending on the entity, you cannot refer to it at all (JLS 6.3.1 Shadowing Declarations).
Although shadowing is generally discouraged, one common idiom does involve shadowing. Constructors often reuse a field name from their class as a parameter name to pass the value of the named field. This idiom is not without risk, but most Java programmers have decided that the stylistic benefits outweigh the risks.
What the second paragraph is saying is that the following is a common idiom:
public Dog (Bone chewy, Bone squeeky) {
this.legs = 4;
this.chewy = chewy;
this.squeeky = squeeky;
}
As the quote says, however, this idiom is not without risk. You will find, for example, that if you had written just chewy = chewy;
, the code will compile! Of course, it would have a different meaning then: the right hand side will be the value of method argument chewy
as expected, but instead of the shadowed field chewy
, the left hand side is also the method argument chewy
! It's essentially a silly do-nothing assignment, which is legal in Java!
Thankfully the compiler still gives a warning ("The assignment to variable chewy
has no effect"), but if the programmer failed to pay attention, he/she wouldn't immediately know that a mistake is made since the code compiles.
So do use this idiom, but always make sure you use the this
keyword to refer to the shadowed fields. (I noticed that you're already using this
even when it's not necessary (i.e. this.legs = 4;
): GOOD JOB! It makes the intention more explicit, so keep doing it!)
Also, always pay attention to compiler warnings. Always try to understand what they mean, and always check and double-check before you suppress such warnings. The compiler is doing its best to assist you; listen to it.
this
keyword
chewy = chewy;
compiles because Java is smart enough to know what was meant, and therefore this
isn't necessary for this name shadowing idiom. WRONG!!