Constructor Chaining and Visibility in Java - java

Consider this class:
public class Passenger {
public Passenger() {
}
public Passenger(int freeBags) {
this(freeBags > 1 ? 25.0d : 50.0d);
this.freeBags = freeBags;
}
public Passenger(int freeBags, int checkedBags) {
this(freeBags);
this.checkedBags = checkedBags;
}
private Passenger(double perBagFee) {
this.perBagFee = this.perBagFee;
}
}
Passenger fred = new Passenger(2);
If I understand correctly, 'fred' is a new instance of Passenger. 'fred' calls the constructor with one parameter which is public Passgener(int freeBags). Then this line this(freeBags > 1 ? 25.0d : 50.0d) gets called.
Here is my question: How does the compiler know that the conditional statement in the first constructor to chain to the 'private' constructor? My guess would be that the 'd' in the conditional statement points to the double parameter in the private constructor. But what if there was another constructor with a double parameter? What would it chain to then? There is no mention of perBagFee in the first constructor. I'm a little confused.

EDIT
How does the compiler know that the conditional statement in the first constructor to chain to the 'private' constructor?
Since either values returned by your ternary expression are double values, the compiler knows that this call refers to your constructor method that received one double argument.
My guess would be that the 'd' in the conditional statement points to the double parameter in the private constructor.
The .0d suffix merely says that those are double values.
There is no mention of perBagFee in the first constructor
Method arguments names have no relevance whatsoever outside the method itself. The compiler will always check for what kind of object does your expression evaluate to in order to figure out which method you want to call.
But what if there was another constructor with a double parameter?
Try it :) Inside the same class, you cannot define two methods (constructor or not) with the same signature, regardless of the visibility. Official docs on methods overloading:
Overloaded methods are differentiated by the number and the type of the arguments passed into the method.
You cannot declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart.

What you can see is called Overloading
Overloading is a concept used to avoid redundant code where the same
method name is used multiple times but with a different set of
parameters. The actual method that gets called during runtime is
resolved at compile time, thus avoiding runtime errors.
Java does not remember variable names of the method to identify which overloaded constructor to be called, instead, Java tries to match the variable type
Passenger(int) -> Passenger(10)
Passenger(int, int) -> Passenger(10,10)
Passenger(double) -> Passenger(2.5d)
so, if you defined another constructor of the same pattern as Passenger(double)
. Java will throw compile time error
constructor Passenger(double) is already defined in class Passenger

Related

In Java, from where can a constructor be invoked?

Is this statement true or false?
"The only way a constructor can be invoked is from within another constructor."
I thought a constructor could be invoked from within a method (and other places) if the call to the constructor is preceded with the keyword 'new'.
How about this statement? True or false?
"The only way a constructor can be invoked is from within another constructor (using a call to super() or this()), or from within static or instance methods, static or instance initializer blocks, or even constructors, if the call to the constructor is preceded by the keyword 'new'." Trying to invoke a constructor like you would invoke a method (using only its name) is not allowed."
Is this more accurate? "Where the call to the constructor is not preceded by the keyword 'new', the only way a constructor can be invoked is from within another constructor using this() or super() as the first statement."
Let's just go straight to the JLS, §8.8:
Constructors are invoked by class instance creation expressions (§15.9), by the conversions and concatenations caused by the string concatenation operator + (§15.18.1), and by explicit constructor invocations from other constructors (§8.8.7). [...]
Constructors are never invoked by method invocation expressions (§15.12).
Therefore, the first statement you quoted is technically false, as the JLS defines using new as invoking the constructor.
Note that your paragraph-length statement is a combination of true and false information; you can't invoke a constructor from static or instance methods except via creating a new object.
Rather than focusing on what the author of the original questions means by "invoke"1, here are the different ways that a constructor will be called.
By using the new operator with the class name and parameters that match the constructor signature:
Foon f = new Foon(1, 2);
It is also possible to do the same thing using reflection and the Constructor object equivalent to a new expression, or by using the relevant JNI or JNA callbacks in native code equivalent to a new expression. However, in all cases, the constructor invocation is conceptually happening at same point in the object creation. Hence I will treat them as identical.
By explicitly chaining to the constructor from another constructor in the same class using this(...).
By explicitly chaining to the constructor from a direct subclass constructor using super(...), or by implicitly chaining to a no-args constructor via an implied super() in a direct subclass constructor (declared or implied).
The implicit cases are merely "syntactic sugar" for the explicit super chaining cases.
There are a few other places where new is invoked behind the scenes by Java from regular Java code. A couple that spring to mind are string concatenation, auto-boxing, and the JVM throwing certain exceptions; e.g.
null.toString() // implicitly creates a NullPointerException obj.
1 - ... which is unknowable unless we understand the context, including how the OP's lecturer (or text book) is using the word "invoke".
It's true. You can't write code that actually calls a constructor as follows:
class Vehicle {
Vehicle() { } // Constructor
void doSomething() {
Vehicle(); // Illegal
}
}
The famous illusive constructor.
It's there when it's there, and even when it's not there it's still there.
The constructor is called at object creation.
So yeah the constructor can, or more precisely will be called if you create a object using new , na matter where you use it.
That quote of yours seems to be incomplete
"The only way the constructor of the superclass of the current class can be invoked is from within the current class constructor."
You can use the constructor when you use the class as an object in other class.
MyClass mc = new MyClass();
This statement is false.
'Invoking a constructor' can mean three different things:
You can invoke a constructor by creating a new object with the new operator:
String s = new String("abc");
In which case you will first allocate an object and then invoke the constructor:
NEW java/lang/String // allocate String instance
LDC "abc" // push the String constant on the stack
INVOKESPECIAL "java/lang/String"."<init>" : "(Ljava/lang/String;)V" // invoke constructor
The second way is to invoke another constructor from the same class:
class Super
{
Super(int i) { }
}
class Test extends Super {
Test() { this(1); }
// Bytecode:
INVOKESPECIAL "Test"."<init>" : "(I)V"
// ---------
The third way to invoke a constructor is to invoke one from the super class:
Test(int i) { super(i); }
// Bytecode:
INVOKESPECIAL "Super"."<init>" : "(I)V"
// ---------
}
Either way, the generated bytecode will contain an INVOKESPECIAL instruction. This means you are literally invoking the constructor in three cases, so if you define 'invoke' by that instruction, there is more than one way to invoke a constructor.
If you mean, directly invoking another constructor of the same object using the this(...) or super(...) construct (called explicit constructor invocation), the answer is yes.
It is probable that this is what the question meant but you have to be really precise in the wording and this question isn't.
Because if by "invoke" you mean, "causing a constructor to run", then you can "invoke" a constructor with the new keyword, which first allocates and creates the object, and then a constructor is run. In this, rather looser sense the answer is no.
But, importantly, new does a lot more than just calling the constructor. So in the most literal sense, calling new is not the same as invoking a constructor. Just like making tea involves pouring hot water but is not the same as simply pouring hot water.
Whether you want to make that distinction or not is up to you, or in this case the author of the question, you need to ask them.

Incorrect type of input in constructor

class Target {
String name;
int amount;
double weight;
Target (String targetName, int targetAmount, double targetWeight) {
name = targetName;
amount = targetAmount;
weight = targetWeight;
}
}
A completely arbitrary constructor which is not part of something bigger.
Suppose I create an instance of this somewhere
If I input the wrong type of argument
Target object1 = new Target("Apple", 4.32, "who knows?")
How can I make the constructor check if the type of argument is correct and if it isn't, ask to declare object1 again.
Is it acceptable to write while loops inside the constructor? (using instanceof checks as long as the type is incorrect and ask to reinput)
Another idea is to create a separate method which deals with the instanceof checks, but the arguments have different types, is there a way to return the declared type of the argument?
OR am I completely over-thinking this and there is an easier way to do this?
You don't need to, because Java compiler will already give you an error during compile-time. It is Strong typed, meaning that a variable will only have one type and ONE TYPE ONLY.
The code you gave will not compile
Firstly, if you give wrong parameters to constructor, java compiler will return an error. Answering your second question it is acceptable to use while loop inside the constructor.
Target object1 = new Target("Apple", 4.32, "who knows?") will give you a compile time error. The method calls are resolved during compile time but the actual calls are done during runtime.
So, once it encounters the above statement, the compiler checks if a constructor which is capable of taking these arguments is present, if no, then you get an error.
How can I make the constructor check if the type of argument is correct and if it isn't, ask to declare object1 again. -
You shouldn't do it. A constructor is invoked when new is called i.e when an object is being created, If the arguments don't match, then the constructor itself won't be called. Use a static helper method for this (if needed).
Is it acceptable to write while loops inside the constructor? (using instanceof checks as long as the type is incorrect and ask to reinput)
You should not have any business logic inside a constructor. That's all. instanceof checks may be acceptable depending on context (case-to-case basis)
am I completely over-thinking this and there is an easier way to do this - YES. Don't call a constructor with invalid arguments. Don't put business logic in it. Validation logic is acceptable.

Which constructor is called first while passing null in the class having overloaded constructor?

Below is the java class having 3 overloaded constructors :
public class Test {
public Test(Object i){
System.out.println("Object invoked");
}
public Test(String i){
System.out.println("String invoked");
}
public Test(int k){
System.out.println("Integer invoked");
}
public static void main(String[] args) throws Exception {
Test t = new Test(null);
}
}
If null value is passed while creating the new instance of class, which constructor will be invoked ? What is the reason ?
Java always chooses the most specific method (or constructor) that would be applicable for the argument you pass. In this case, that's the String constructor -- String is a subclass of Object.
Think about what would happen if you had
new Test("some string")
Both the Object and String constructors would be applicable here. After all, the argument is both an Object and a String. However, it is clear that the String constructor will be invoked because it is more specific than the Object constructor and still is applicable given the argument.
null is no different; these two constructors are still applicable, and the String constructor is still chosen over the Object one for the same reason.
Now, if there were two equally "specific" constructors present (e.g. if you had an Integer constructor) there would be a compilation error when you call Test(null).
This is outlined in more detail in JLS §15.12.2:
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is one used at run time to perform the method dispatch.
The explicit process of determining which method is the most specific is outlined in JLS §15.12.2.5.
The answer is: Test(String) is invoked.
Why?
When determining which of a set of overloaded methods will be invoked, the Java compiler will attempt to match the most concrete type. And it will first attempt to match a signature before employing autoboxing. (#arshajii provided a perfect reference into the Java Language Spec on this)
Here, Object is the most abstract class in the type system. String subclasses from Object and is therefore more specific/concrete.
The logic behind this is that if you are overloading a method with a more specific-typed parameter, you're likely wanting to do more with that object (when you subclass, you typically add methods). If method signature determination worked the other way (i.e. the more abstractly-typed signature winning; here, Test(Object)), then none of the more concrete signatures would ever get called.

Understanding which constructor is chosen and why

Why following program every time prints I'm string and not I'm object. or I'm int.?
public class Demo {
public Demo(String s){
System.out.println("I'm string");
}
public Demo(int i){
System.out.println("I'm int.");
}
public Demo(Object o){
System.out.println("I'm object.");
}
public static void main(String[] args) {
new Demo(null);
}
}
Also if I replace int with Integer. It gives error as The constructor Demo(String) is ambiguous. Why?
null can be converted to Object or String, but not int. Therefore the second constructor is out.
Between the conversion to Object or the conversion to String, the conversion to String is more specific, so that's what's picked.
The JLS section 15.12.2 describes method overload resolution, and I believe the same approach is used for constructor resolution. Section 15.12.2.5 describes choosing the most specific method (constructor in this case):
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
This about the constructor invocation with Object or String arguments - any invocation handled by new Demo(String) could also be passed on to new Demo(Object) without a compile-time type error, but the reverse is not true, therefore the new Demo(String) one is more specific... and thus chosen by the overload resolution rules.
To answer your second question (since Jon Skeet has already covered the first), when you have both a String constructor and an Integer constructor the compiler doesn't know what you mean by null in new Demo(null): it could be either a String or an Integer.
Because String can't be cast to Integer (and vice versa) the compiler gives up and reports the ambiguous error. This is in contrast to the String vs Object choice when you don't have the Integer constructor.
When you have constructors or methods like the above, the compiler generally tries to find the closest match possible and uses that.
The way to think of null in these circumstances is it acts as a subtype of all types (yes, it's not strictly speaking true but it provides a good platform for thinking about what happens). So using this rule it fits string more closely than object, hence this is the constructor that is executed.
The fact the String constructor is mentioned in by the compiler in your second error:
The constructor Demo(String) is ambiguous.
is not significant. This is because the Constructor which takes a String is the first declared constructor, so the compiler uses it in its error message. Change to have the Object constructor first and you get:
The constructor Demo(Object) is ambiguous.
What it's trying to say is there's ambiguity between the constructor which takes the Integer and the Object, so you have to be more specific, as null can be applied to each. Integer IS-NOT-A String, so the two types are not compatible. You need to be more specific so the compiler can bind the constructor call.
See #Jon Skeet answer for why the compiler raises an error in some instances and not in others.

can methods be overloaded based on access in java?

For example a class has public do, packaged do, protected do, and private do. If an instance of the class calls do it gets the private one, if a subclass calls it, then it gets protected, if a same package calls it, then it gets packaged and if anything else calls it, it gets public?
No.
It is a compile-time error to declare
two methods with override-equivalent
signatures (defined below) in a class.
Two methods have the same signature if
they have the same name and argument
types.
(From JLS §8.4.2)
class A
{
public Object do() { ... }
protected Object do() { ... }
Object do() { ... }
private Object do() { ... }
}
No. This will not compile with or without a subclass. Nor should it be expected too. How would the compiler have any idea which method to invoke? It's impossible.
To maybe make it a little clearer, a more distinctive overload--one that returns some other type than Object is not even an acceptable overload because the compiler would still have trouble determining which method to call. A weaker form of overloading would be even less acceptable.
No. This is an element that comprises its identity (just as you can't overload the name or return type of the method). You can only overload which variables are passed to the method.
If you tried to make a method public whose super method was private, you would get a compiler error, and your program would not run.
No, because this methods all have the same method signature:
Definition: Two of the components of a method declaration comprise the method
signature — the method's name and the parameter types.
Compiler can only distinguish between methods based on their signature.

Categories

Resources