In the following program, I have one single method in class A overloaded 3 times and then in subclass B, all 3 overloaded methods are overridden.
obj3 is an object with reference type A(superclass) and object type B(subclass) and it calls the method from B on execution, which is expected behavior.
Since overloading and overriding both exist in this code, does that mean that it performed static binding at compile time (to the matching method in class A) and then dynamic binding at run time (to method in class B). Can they both occur together?
My assumption is that this is a classic case of dynamic binding as I believed "binding" is meant to be a permanent action, but a peer suggests that it is both together(static first, then dynamic).
class A{
public void method(Integer n){
System.out.println("Integer: "+n);
}
public void method(String s){
System.out.println("String: "+s);
}
public void method(String s, Integer n){
System.out.println("String: "+s+" Integer: "+n);
}
}
class B extends A{
public void method(Integer n){
System.out.println("Integer(from B): "+n);
}
public void method(String s){
System.out.println("String(from B): "+s);
}
public void method(String s, Integer n){
System.out.println("String(from B): "+s+" Integer(from B): "+n);
}
}
public class Test{
public static void main(String[] args){
A obj1 = new A();
B obj2 = new B();
A obj3 = new B();
System.out.println("Integer form of method");
// Integer form of method
System.out.println("Ref A Obj A");
// Ref A Obj A
obj1.method(1);
// Integer: 1
System.out.println("Ref B Obj B");
// Ref B Obj B
obj2.method(2);
// Integer(from B): 2
System.out.println("Ref A Obj B");
// Ref A Obj B
obj3.method(3);
// Integer(from B): 3
}
}
Since overloading and overriding both exist in this code, does that mean that it performed static binding at compile time (to the matching method in class A) and then dynamic binding at run time (to method in class B)
Right. The compiler chose the matching signature, and this is static, based on the type of the variable (A in this case).
At runtime, Java finds the implementation of the signature selected by the compiler. This is dynamic, based on the runtime class of obj3 (B, in this case).
You right. Compiler is statically choose between overloads in class A and put that information into .class file in form of method FQN.
Then runtime dynamically choose between implementation of that method.
One more attempt to make it clear:
Overloading
Overloading means that a single class has multiple methods with different parameter types (aka. signatures), and you just happened to give them the same name. Your program will work the very same if you change to individual names for the methods, e.g. methodI(Integer n), methodS(String s), and methodSI(String s, Integer n).
Or, you can imagine the compiler to internally always append such a types list to the method name.
Overloading is resolved by the compiler, based on the compile-time types of the parameter expressions.
E.g. if you write
Object par = "Test";
a.method(par);
you get a compiler error. Even though we all see it's a String that you are passing into the method, the compiler only sees an Object, and finds no matching method. Only if you were to introduce an additional method(Object o), the compiler would choose that one. And runtime would call that method and not the String version!
Overriding
Overriding means that at runtime the JVM calls the method implementation depending on the runtime class of the "object before the dot".
And in this case, "method" is to be read as the overloaded method version that the compiler found to match the parameter list. So, runtime already knows whether methodI(), methodS(), or methodSI() is meant, and only decides from which class to take the implementation.
Personal opinion
Allowing multiple methods to share the same name, but differ in parameter lists (aka overloading), produces too much confusion for its benefit.
Related
Studying "cracking the coding interview" in Java, on page 51 I came across:
void permutation(String str){
permutation(str,"");
}
void permutation(String str, String prefix){
if(str.length()==0){
System.out.println(prefix);
} else{
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1);
permutation(rem,prefix+str.charAt(i));
}
}
}
I get that the first permutation function takes a string and calls the second permutation function which does all the work. However, isn't the second permutation a redeclaration of the first permutation function? How will Java recognize and use the first permutation function and not overwrite it?
How will java recognize and use the first permutation function?
When you call the method, Java will see what you're trying pass into it. Based on the arguments you pass, it will decide which 'version' of the method you are trying to use.
Like others have said - this is method overloading
Unlike in Python, in Java these two declarations live side-by-side -- the second doesn't replace the first. In Java, the rule is roughly that when you call a method with multiple definitions (aka an "overloaded" method), Java will look for the one that best matches the arguments you called it with and run that method. So permutation("hi") invokes the first version, and permutation("hi", "") calls the second.
The fundamental difference here is that in Python you can imagine the interpreter reading the definitions one at a time and replacing its overall definition of permutation every time it gets a new definition. In Java, you have to think of it as reading all the definitions of permutation at once, and calling the most appropriate one for any given invocation.
(A consequence of this is that Java also checks at compile-time that every overloaded version of a method is callable: for instance, if you wrote two versions of permutation that both took just a string as their argument, the compiler would give you an error and wouldn't compile your program at all. In python you'd just get the second definition.)
To explain what the semantics are, we need to take a look at how Java methods are differentiated.
In Java, a method is identified by its signature. JLS ยง8.4.2 specifies that
Two methods have the same signature if they have the same name and argument types.
Important to note is that the return type of a method is not part of a method's signature. Thus if one would write:
public class Foo {
void bar(String baz) {
}
String bar(String baz) {
}
}
Both methods would have the same signature. In Java, this would lead to a compilation error since it is not allowed to have two methods with the same signature in the same class.
The behaviour changes if we take inheritance into the picture:
public class Foo {
void bar(String baz);
}
public class Zoo extends Foo {
#Override
void bar(String baz);
}
In this case, class Zoo overrides method bar(...) of class Foo. Note that the annotation is not responsible for the behaviour and merely a compile-time check to ensure that there is a method void bar(String baz) in at least one parent-class.
The code presented has two method with same name, but different signatures. This is called Overloading in Java. Thus, the method are treated as not "equal". You could rename one of those method and they would not be more or less "equal".
To make things even weirder, if methods are overloaded, the signature for the method to call is made at compile-time. That means that only the static types of parameters can be considered. Let us look at the following code and figure out what the result is:
public class Test {
public static void main(final String... args) {
final String s = "foo";
final Object o = s;
print(s);
print(o);
}
private static void print(final String s) {
System.out.println("Called with String parameter");
}
private static void print(final Object o) {
System.out.println("Called with Object parameter");
}
}
Ideon demo
Now what is the static type of s? It is the type to the left, where s was declared, thus print(final String s) is called and "Called with String parameter" is printed. What is the static type of o? Again, it is the type to the left, where o was declard, and thus print(final Object o) is called and "Called with Object parameter" is printed out. One could argue that in this trivial example, the compiler could figure out that the type of o can only be String, but basing this behaviour on the ability of the compiler to recognize types at compile-time makes it only more confusing.
In java, the whole class is loaded before a method is executed.
This means that the second method is loaded/ready before the first method is executed and the first method is loaded/ready before the second method is executed.
This allows to call a method recursively, and to call a method that will be declared later.
Also, the method is overloaded.
In java, it's possible to create multiple methods with the same name in the same class if the parameters are different. The methods will be treated as different, deoending of the argument that are passed to the method.
In other words, the name alone does not define which method is called but the signature, including the parameters(not the return value)
My included code's output is pretty ugly, but it's just a code for understanding how different things may work in Java. The questioned line is marked with comments on the bottom half of the code.
class CsTorta extends Torta{
public CsTorta retegez(CsTorta a){
....
}
public CsTorta retegez(Torta a){
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
}
public class NewClass {
public static void main(String[] args) {
Torta tt=new Torta(5);
Torta tcs=new CsTorta(3);
CsTorta cs=new CsTorta(4);
System.out.println("");
System.out.println(tcs.retegez(tcs)); //The line in question uses the cstorta retegez method (marked with "This method")
}
}
While the tcs's type in coding-time is the reference type, in runtime when i call the tcs.retegez method it recognizes its a cstorta type, but the parameter which is the same tcs remains the reference type (thats why it uses the cstorta marked method).
My question is: Is my conclusion correct: that the program only checks the "real" type of the object if it calls a method, and uses the reference type if it does not?
That's pretty much correct. What is needed here is understanding the difference between overloading and overriding.
Overriding occurs when you have a class that declares an instance method, and a subclass that declares the same method (same name, same parameters--the result type is usually the same but could be a subclass). There are multiple methods to choose from, but the exact method is determined at run time.
public class A {
public void method1(String s1, int s2) { ... }
}
public class B extends A {
#Override
public void method1(String s1, int s2) { ... }
}
A object = new B();
object.method1("xxx",2);
The decision about which method1 is run isn't made until run time. object's real type is B, so the method1 declared in B is called.
Overloading is when two methods with the same name, but different parameters, are both present. By different parameters, I mean that the number of parameters is different, or the number of parameters is the same but the types are different. That is, they have different signatures. In that case, the decision on which method to call is made at compile time. (You can have a case where both overriding and overloading occur. The decision about which parameter signature to choose is made at compile time; but if there are multiple overriding methods with the same signature, the choice between those methods is made at run time.)
The key thing to remember is that if the decision is made at compile time, the compiler will not know what the "real" type of the object is. All it knows is how you've declared it. Thus:
public CsTorta retegez(CsTorta a){ // Number 1
....
}
public CsTorta retegez(Torta a){ // Number 2
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
These are overloaded methods.
Your code looks like:
Torta tcs = // the compiler doesn't care
System.out.println(tcs.retegez(tcs));
The compiler has to decide whether to call Number 1 or Number 2. All the compiler knows is that the parameter is a Torta. The actual value could be an object of Torta, CsTorta, or any other class. Or it could be null (all right, you can't call tcs.retegez if it's null, but if you said tcs.retegez(tcs2), then tcs2 could be null.) The compiler doesn't know, and doesn't care. All it knows is that it was declared as a Torta, so it chooses the overloaded method with the Torta parameter.
(To clarify further: the compiler will choose the deepest subclass it can. Example:)
class AnotherTorta extends Torta { ... }
class YetAnotherTorta extends CsTorta { ... }
AnotherTorta t3 = // whatever
YetAnotherTorta t4 = // whatever
tcs.retegez(t3);
// since AnotherTorta can't be cast to CsTorta, it chooses the Torta parameter
tcs.retegez(t4);
// here, t4 can be cast to either a Torta or CsTorta parameter, so it chooses the subclass, CsTorta
I have implemented the following code in order to understand the difference between static and dynamic binding:
class A {
int met(A a) {
return 0;
}
int met(B b) {
return 1;
}
int met(C c) {
return 2;
}
}
class B extends A {
int met(A a) {
return 3;
}
int met(B b) {
return 4;
}
int met(C c) {
return 5;
}
}
class C extends B {
int f() {
return ((A)this).met((A)this);
}
}
public class Test {
public static void main(String[] args) {
C x = new C();
System.out.println(x.f());
}
}
The result I get is 3, but I don't understand why, since the first cast is made to A.
So, let's look at the call:
((A)this).met((A)this);
That's equivalent to:
A target = this;
A argument = this;
target.met(argument);
So, for the last line, the compiler looks up the signature based on the compile-time types involved - it will look in A (and superclasses) for a method called met which has a parameter that is compatible with A (the compile-time type of the argument). Overload resolution finds that the answer is:
int met(A a)
That's determined the signature at compile-time. However, the implementation of that method is determined at execution time, based on the execution-time target of the method call. That type is C here - because this is a reference to an instance of C. (The f method is called on an instance of C.)
Now C doesn't override int met(A a), but B (its superclass) does - so that's the implementation that's used. It doesn't matter that B also overrides met(B b) and met(C c) because the compiler has already determined that it's the met(A a) method which is being called.
This is two questions for the price of one. (1) Why do we get the implementation from class B, and (2) why do we get the version of the method with the parameter of type A.
For question (1), the thing to remember is that the class of an object doesn't change when you cast it, or assign it to a variable whose type is something different. So in your example, the class of this is always C, because that's what you created. Class C inherits its version of met(A a) from class B because it doesn't have its own version, and because class B has overridden the version in class A. This is what polymorphism is all about - the version of the method depends on the class of the object that you call it on, not on the type of the expression that you use to call it.
For question (2), it's a wee quirk of Java that method signatures are evaluated at compile time. So the compiler sees that you're passing an expression of type A into your method, so it chooses the signature met(A a). Because this decision is made at compile time, the actual class of the argument doesn't make any difference - the compiler has already chosen the method, based on the type of the expression. In other words, Java doesn't give you polymorphism of method parameters.
I have the following codes:
1. public class Tester
2. {
3. public static void main(String[] args)
4. {
5. A a = new B();
6. System.out.println(a.getClass()); //Prints class B
7. System.out.println(a instanceof A); //Prints true
8. System.out.println(a instanceof B); //Prints true
9. System.out.println(a.valA); //Prints 1
10. System.out.println(a.valB); //Compilation error
11.
12. }
13. }
14.
15. class A
16. {
17. int valA=1;
18. }
19.
20. class B extends A
21. {
22. int valB=2;
23. }
At line 6 it shows that a is of type class B. However when it reaches line 10, the compiler produces an error: location: variable a of type A.
So my question is: What exactly is the class type of a now? Why getClass() shows that it is of type class B, yet the compiler complains it as type A during compilation?
Further more, since a instanceof B is true, why can't I access valB?
To make things clearer:
EDIT: I ran this statement: System.out.println(a); and the output was B#36d98810 which somehow proves that the toString() method of class B was executed. Since variable a can access the toString() method within class B, why can't it access valB which also resides in class B?
Professor Jonathan Shewchuk from UC Berkley explains about shadowing over here. Start at 18 minutes. (If the link changes just google search for CS 61B Lecture 15: More Java)
To answer your question in short there are two types for a variable, static type and dynamic type.
Static type is its Type at compile time
Dynamic type is its Type at run time.
In your example
A a = new B();
The static type of a is A and the dynamic type of a is B.
In Java a variable gets its non static methods from dynamic type
(if the method exists in both the parent and child class)
and
its fields and static methods from the static type.
This is true in C# only if the method is overridden in the sub class
Update:
The line
a instanceof A
tells you whether the dynamic type of a is of type A OR a subclass of A
Update 2:
AN example that illustrates this
public class PlayGround {
public static void main(String[] args) {
Animal a = new Dog();
System.out.print(a.name);// displays animal
System.out.print("\r\n");
a.MakeStaticSound();// displays static animal sound
System.out.print("\r\n");
a.MakeSound();// displays bow wow
}
}
class Animal {
public String name = "animal";
public void MakeSound() {
System.out.print("animal sound");
}
public static void MakeStaticSound() {
System.out.print("static animal sound");
}
}
class Dog extends Animal {
public String name = "dog";
public void MakeSound() {
System.out.print("bow wow");
}
public static void MakeStaticSound() {
System.out.print("static bow wow");
}
}
Please note that the more readable and preferred way to call a.MakeStaticSound() is Animal.MakeStaticSound()
a is not an object. It's a variable.
The type of the variable is A. The type of the object that the value of the variable refers to at execution time is B.
The compiler resolves everything against the compile-time type of the expressions involved - the variable in this case. When trying to resolve the name valB within the compile-time type of A, it fails to find anything - hence the error.
You need to keep in mind that compilation and execution are two different processes that happen at different times and have different kinds of information available to them. The compiler has to predict the future -- it has to decide whether it can guarantee that your code will make sense in the future, at runtime. It does this by analyzing the types of the objects in your code. The runtime, on the other hand, just has to inspect the current state of things.
When you read the line A a = new B(), you are inferring more information about the a local variable than the compiler is. The compiler basically just sees this as A a = <some expression>. It does not take note of the contents of the expression that's used to produce the value for a.
The fact that you've said A a = ... is you telling the compiler: "hey, this a thing I'm going to deal with in the rest of my program, it's just an A, don't assume anything more about it." If you had instead said B a = ..., then you're telling the compiler that it's a B (and the compiler also sees B extends A elsewhere in your code, so it knows it's also an A).
The subsequent expressions a instanceof A, a instanceof B, a.getClass(), and a.toString() are legal, from the compiler's point of view, regardless of the type of a: the instanceof operator and the getClass() and toString() methods are defined for all Objects. (The compiler does not need to predict what value those expressions will produce at runtime, just that they will produce either true or false, some Class<?>, and some String, respectively.)
But then when you come to a.valA and a.valB, the compiler actually has to do some real work. It needs to prove or guarantee that the a object will have a valA and a valB field at runtime. But since you've explicitly told it earlier to just assume that a is an A, it can not prove that it will have a valB field at runtime.
Now, later on, at execution time, the JVM has more information. When it evaluates a.getClass(), it actually looks up the concrete class that's "under the hood" of a and returns it. Similarly for instanceof B -- it looks up the concrete class and thus the result of that expression is true.
a.toString() works similarly. At runtime, the JVM knows that the thing referenced by a is actually a B, so it executes B's toString method.
This is a fundamental property of class inheritance, interfaces, etc.
Class "A" does not have a variable "valB".
If you want to use the variable "valB" in class "B" either, you should first cast Class "A" to "B"
Try :
System.out.println(((B)a).valB);
You should know the difference between object type and instance type. First is determined at compile type and at runtime it's doing the best to keep that type safe. Instance type is a class which object is instantiated.
A a; //this is an object type
new B(); //this is an instance type
A a = new B(); //all together, but a is of type A, having instance of type B.
Consider this:
class A {
int x =5;
}
class B extends A{
int x =6;
}
public class CovariantTest {
public A getObject() {
return new A();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
CovariantTest c1 = new SubCovariantTest();
System.out.println(c1.getObject().x);
}
}
class SubCovariantTest extends CovariantTest {
public B getObject(){
return new B();
}
}
As far as I know, the JVM chooses a method based on the true type of its object. Here the true type is SubCovariantTest, which has defined an overriding method getObject.
The program prints 5, instead of 6. Why?
The method is indeed chosen by the runtime type of the object. What is not chosen by the runtime type is the integer field x. Two copies of x exist for the B object, one for A.x and one for B.x. You are statically choosing the field from A class, as the compile-time type of the object returned by getObject is A. This fact can be verified by adding a method to A and B:
class A {
public String print() {
return "A";
}
}
class B extends A {
public String print() {
return "B";
}
}
and changing the test expression to:
System.out.println(c1.getObject().print());
Unless I'm mistaken, methods are virtual in java by default, so you're overriding the method properly. Fields however (like 'x') are not virtual and can't be overriden. When you declare "int x" in B, you are actually creating a totally new variable.
Polymorphism doesn't go into effect for fields, so when you try and retrieve x on an object casted to type A, you will get 5, if the object is casted to type B, you will get 6.
When fields in super and subclasses have the same names it is referred to as "hiding". Besides the problems mentioned in the question and answer there are other aspects which may give rise to subtle problems:
From http://java.sun.com/docs/books/tutorial/java/IandI/hidevariables.html
Within a class, a field that has the
same name as a field in the superclass
hides the superclass's field, even if
their types are different. Within the
subclass, the field in the superclass
cannot be referenced by its simple
name. Instead, the field must be
accessed through super, which is
covered in the next section. Generally
speaking, we don't recommend hiding
fields as it makes code difficult to
read.
Some compilers will warn against hiding variables