Is there a case when an object is declared without a call to the constructor?
as in, for example:
ArrayList<Integer> grades;
Or is it always the case that ArrayList<Integer> grades
(as in our example here) would always be followed by a call to a constructor as in,
ArrayList<Integer> grades = new ArrayList<Integer>();
?
If the latter is the case i.e. a declaration of an object is always followed by its initialization with a constructor then why isn’t
ArrayList<Integer> grades;
already implies
ArrayList<Integer> grades = new ArrayList<Integer>();
Otherwise when is it the case that an object is declared without an immediate call to the constructor following it?
Is it the case that
ArrayList<Integer> grades;
can be initialized by anything other than
= new ArrayList<Integer>();
1
Consider two distinct concepts: objects and references.
References ‘point’ to objects in memory, but they are not the objects themselves. In Java, we always refer to and control objects through references to them. In Java, you never store the object itself in the variable, only it’s address in memory (not exactly – implementation dependent – but you get the idea).
So by declaring ArrayList<Integer> grades;
, what you do is create a reference, which is able to point to objects of type ArrayList<Integer>
(and any subtype).
And by declaring new ArrayList<Integer>()
, what you do is create a new object in memory, and have a ‘pointer’ to it returned to you (not the actual object data).
So by declaring ArrayList<Integer> grades = new ArrayList<Integer>();
, you create an ArrayList<Integer>
somewhere in memory, and assign its memory address (again, sort of) to the variable grades
.
Declaring only ArrayList<Integer> grades;
creates an empty reference that points nowhere.
0
Yes, references to objects can be initialized without constructors or visible assignments. This effect is often achieved by using Dependency Injection containers like Guice or Spring.
class Foo {
@Inject
Integer bar;
};
Foo foo = container.create(Foo.class);
assert foo.bar != null;
If this class is instantiated in Injection container context, its field bar will get a value without ever being directly assigned. These usually use reflection to set values, so while assignment is not explicit it still happens.
I don’t think we can do same trick for local variables.
For the sake of simplicity, Java designers decided Strings, despite not being primitives, can be declared and initialized as such.
String s = "test";
which is the same as
String s = String("test");
Something similar they did with arrays:
int[] numbers = {1,2,3};
Those are examples of objects being initialized without explicitly calling their constructors with new()
. but you ca be sure the constructor is being called anyway.
3
Is there a case when an object is declared without a call to the constructor?
There certainly is.
All of them, in fact.
Declaring an object variable creates a location (“address”) into which the program can later write a reference (“pointer”) to an instance of an object [of the specified Type].
Instantiating an object is the action that calls the relevant constructor.
The …
Type name = expression ;
… syntax is just a compact way of doing the declaration and instantiation as a “one-liner”, but it is functionally no different to …
Type name [ = null ] ;
name = new Type( argument, ...);
Secondly:
Is it the case that “
ArrayList<Integer>
grades” can be initialized by anything other than “=new ArrayList<Integer>()
“.
Again, Yes.
You can initialise the variable called grades to the value of any expression of the data Type “ArrayList”, for example.
ArrayList<Integer> grades = class.getStudentGrades();
then why isn’t
ArrayList<Integer> grades;
already implies
ArrayList<Integer> grades = new ArrayList<Integer>();
First, read @AvivCohn’s answer about references.
Second, besides @Basilev’s answer about dependency injection, there are also two reasons why an implicit call to new
may not be desired (what follows are Java-specific and not programming concepts in general):
-
Construction of a new object is “expensive”
- edit: As @MikePartridge mentioned helpfully in the comments, I don’t mean “expensive” in the sense of object creation per se, but whether an object can be created with just a
new MyObject(...)
one-liner or not. Do see this link that dispels with the myth that object creation is expensive. - Your
ArrayList
example will be a bad example of showing why this is so, but consider an object that requires many fields to be set, which may each require their own expensive construction. The fields’ values may have to be retrieved from a database or remote procedure calls. That is why we have the builder or factory patterns that builds the object for us, before assigning that to the reference. - This is related to dependency injection.
- Of course, implicitly, there will be a
new MyObject(...)
called in the builder/factory, but the object will be initialized properly before the assignment. - On a related note, sometimes the constructor then becomes a
private
constructor following the builder pattern, as callers to the instantiation of said class need not know how the object is initialized.
- edit: As @MikePartridge mentioned helpfully in the comments, I don’t mean “expensive” in the sense of object creation per se, but whether an object can be created with just a
-
There can be different ways of assigning an object to a reference
- Here are actually two examples to show this, but I’m just going to club them under the same reason. In both examples,
test
can only be set once due to itsfinal
modifier, which prevents simple programming mistakes such as over-writing the reference (and then possibly making the wrong comparisons). In any case, they (hopefully) show why there cannot be an implicit assignment tonew String()
, and why it is not desired. Again, the call tonew
(just declaring"..."
does that in Java) is done explictly during the assignment, instead of implicit as you have asked in your question. -
switch
/if
logic to determine value to setfinal String test; switch (value) { case A: test = "some-text"; break; case B: test = "more-text-please"; break; ... }
-
try-catch
to do alternative assignment in case of exceptionsfinal String test; try { test = someMethodThatReturnsAStringOrMayThrowAnException(); } catch (SomeException e) { logThisException(e); test = "alternate-value"; }
- Here are actually two examples to show this, but I’m just going to club them under the same reason. In both examples,
3