Simple question, strange result. I have two classes A and B:
public class A
{
protected int num;
public A(int n)
{
num = n;
}
public boolean f(A a)
{
return num == a.num * 2;
}
}
public class B extends A
{
public B(int n)
{
super(n);
}
public boolean f(B b)
{
return num == b.num;
}
}
Why does y1.f(y2) call the f() method in A instead of in B?
A y1 = new B(10);
B y2 = new B(10);
System.out.println(y1.f(y2));
Is it not supposed to call f() in B as B is more specific than A?
Why does y1.f(y2) calls the f() method in A instead of in B?
Because the compile-time type of y1 is A.
Overloading is performed at compile-time... the execution-time type of the object you call the method on is only relevant for overriding.
So the compiler is choosing the method f(A) as that's the only f method it's aware it can call on y1 (and it's checked that it's applicable given the argument list). That method isn't overridden in B, therefore at execution time, the implmenetation in A is called.
As a starker example, consider this code:
Object x = "foo";
int length = x.length();
This won't even compile, because Object doesn't contain a length() method. String does, but the compiler doesn't consider that, because the compile-time type of x is Object, not String - even though we can tell that at execution time, the value of x will be a reference to a String object.
Related
I have multiple classes that extends an abstract class A.
The abstract class has the following two methods:
public abstract int methodOne (int n);
public abstract int methodTwo (int n);
One of the classes, class B, that extends the class A overloads one of the methods for instance:
public int methodOne (int n) {
return n * 2;
}
public int methodOne (int n, int k) {
return n * k;
}
Now we are working with an instance of the class B, is there a way to check if the method methodOne has been overloaded and if so make a condition.
A ourTest = new B();
If methodOne has two arguments then use the method with two arguments, otherwise use the methodOne with one argument.
You can use reflection to inspect what methods B declares. Note there are 2 flavors:
B.getClass().getMethods()
Will give you an array of Method objects representing all of the public methods of the class, including those declared by the class or interface and those inherited from superclasses and superinterfaces.
You can also call
B.getClass().getDeclaredMethods()
Will give you an array containing Method objects reflecting all the declared methods of the class, including public, protected, default (package) access, and private methods, but excluding inherited methods.
Not that in your case, class A doesn't have a 2 argument form of methodOne so it technically cannot be inherited or overridden in B.
So, you could call getDeclaredMethods() and loop over the array of Method objects to see if B has a 2 argument form of methodOne.
This works even if you declared an object of type A but instantiate it with new B().
The following sample code shows it in action:
public abstract class A {
public abstract int methodOne (int n);
public abstract int methodTwo (int n);
}
public class B extends A {
#Override
public int methodOne(int n) {
return n;
}
public int methodOne(int n, int k) {
return n + k;
}
#Override
public int methodTwo(int n) {
return n * 2;
}
}
// test it out (note that this can throw several different exceptions
// so you'll have to handle them..I left that out to keep the code
// concise.
A a = new B();
Method[] methods = a.getClass().getDeclaredMethods();
for(Method m : methods) {
System.out.println(m.toGenericString());
if(m.getName().equals("methodOne") && m.getParameterCount() == 2) {
int result = (int)m.invoke(a, 3, 2);
System.out.println("Result = " + result);
}
}
This will print the following:
public int B.methodTwo(int)
public int B.methodOne(int,int)
Result = 5
public int B.methodOne(int)
You have no same method with one or two arguments. Both methods are different although have the same name (methodOne). If you want to find how many arguments a method has, or if it has been overloaded, there is Java Reflection API available. But you are trying to do seems to make no sense.
If methodOne has two arguments then use the method with two arguments, otherwise use the methodOne with one argument.
No, you cannot do that, because Java is statically typed.
Since A defines only the one-argument version of methodOne, that is all you'll ever have for code that uses variables of type A.
Any method added to subclasses are not visible in plain Java code for variables of type A. You can only call such "extra" methods by either casting to the subclass in question, or by using reflection.
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.
consider the following code:
public class A{
private int num;
public A(int n){
num = n;
}
public int getNum(){
return num;
}
public boolean f(A a){
return num == a.num * 2;
}
}
public class B extends A {
public B(int n) {
super(n);
}
public boolean f(B b) {
return getNum() == b.getNum();
}
}
public class Main
{
public static void main(String[] args){
A y1 = new B(10);
B y2 = new B(10);
System.out.println("y1.f(y2) is: "+y1.f(y2));
}
}
What I don't understand is why the method f is running for class A (and printing false) and not B, cause in run time y1 is of type B, and should go down to method f in class B?
cause in run time y1 is of type B, and should go down to method f in class B?
No:
B.f() doesn't override A.f() because the parameter types are different. It overloads it.
Overloads are picked at compile-time, not at execution time
If you change B.f() to accept a parameter of type A rather than B, you'll see it get executed. That doesn't depend on the execution-time type of the argument, but on the execution-time type of the target of the call.
The method implementation which is chosen never depends on the execution-time type of an argument.
pay attention that the parameters in class B & class A in f function are diffrent. that is the reason that the "gravity low" dont exist here and this time it enter to the A.f function
I was wondering why this bit of Java yields 2, and not 3 :
public class Test {
private static class A {
int f(A a) {
return 1;
}
}
private static class B extends A {
int f(A a) {
return 2;
}
int f(B b) {
return 3;
}
}
public static void main(String[] astrArgs) {
A ab = new B();
B b = new B();
System.out.println( ab.f(b) );
}
}
I came across this in a test question, and couldn't get the logic behind it.
The compile-time type of ab is just A. Therefore, when the compiler sees this expression:
ab.f(b)
... it only considers method signatures declared on A and its superclasses (just Object in this case).
So, the compiler makes the decision to call the method with the signature f(A a).
Now at execution time, the VM chooses which implementation of that signature to execute based on the execution-time type of the target of the method call, which is B.
B overrides f(A a), so that overriding implementation is called - and returns 2.
Basically, overloading is determined at compile-time to work out what method signature to call based on the compile-time types of both the target of the call and the arguments, and overriding is determined at execution-time to work out the exact implementation to execute based on the execution-time type of the target object.
In this case, ab is of type A, but instantiated as B. A only knows method
int f(A a) {
return 1;
}
b is of type A, so it is valid. B overrides int f(A a), so this method is used.
int f(A a) {
return 2;
}
Hope that helps.
I have the following code:
import java.lang.*;
public class Program
{
public static void main(String [] args)
{
B a = new A();
a.p(10);
a.p(10.0);
}
}
class B {
public void p(double i)
{
System.out.println(i*2);
}
}
class A extends B{
public void p(int i)
{
System.out.println(i);
}
}
When I execute this code using B a = new A() , I get 20.0 in both cases which makes sense because overloading is handles during compile time where the compiler looks at the declared type and calls a function appropriately. Since our declared type was class B, class B's method was called in both cases. Now if I do A a = new A(); , I should be getting 10 in both answers but I am not. I am getting 10 for a.p(10) and 20.0 for a.p(10.0). Based on the concept of static binding and whole notion of overloading being done by static binding which looks at the declared type as opposed to the actual type, why is the result coming out this way ? I would very much appreciate your help.
An int can be widened to a double, but not the other way around. This means that 10 can call B.p(double) or A.p(int) but 10.0 is a double and will not be implicitly converted to an int i.e. only B.p(double) will be called.
Its because your method p is not an overridden method, it is just inhereted in your sub-class when you use
Super sup = new Sub();
sup.p(int);
sup.p(double);
In this case as your Super class has a method which takes double as a parameter and aa an int can fit into a double your Super-class's method is invoked the one which accepts double.
Sub sup = new Sub();
sup.p(int);
sup.p(double);
In this case however, as your subclass doesn't have a method which takes a double, for sup.p(double) call it uses the inherited method from super class if you pass double as an argument.
In your case, your are doing overloading which will get binded at compile time(static binding.).And static binding happens with type of reference rather than the type of object the reference is pointing.
In your first case you are using a reference variable of B and assigning an object of A to it.Since your reference is B, the method p(double) from B will get binded statically even if you use an int(since int can be widened to double).
In the second case you are using reference as A itself.In this case, you have two p() methods available.One is p(double) from B and other p(int) from A.So p(10) will call p(int) and p(10.0) will call p(double)
Try this:
class B {
public void p(String i)
{
System.out.println("parent:"+i);
}
}
class A extends B{
public void p(int i)
{
System.out.println(i);
}
}
public class Test1 {
public static void main(String args[]) {
A a = new A(); //arg
a.p(10);
a.p("sample");
}
}
If you change the line marked arg to B a = new A(), you will see compiler trying to call parent p in both the cases.
When you write A a = new A() you create a new object of type A, which will have 2 methods. A.p(int) and B.p(double), and when you call A.p(10.0), it will call B.p(double) due to lack of conversion.
This counter-example might help:
import java.lang.*;
public class X
{
public static void main(String [] args)
{
B c = new A();
c.p(10);
c.p(10.0);
c.p("AAA");
((A)c).p(10);
}
}
class B {
public void p(String s)
{
System.out.println("B: my string is " + s);
}
public void p(double i)
{
System.out.println("B: twice my double is: " + i*2);
}
}
class A extends B{
public void p(int i)
{
System.out.println("A: my number is " + i);
}
}
Output:
C:\temp>java X
B: twice my double is: 20.0
B: twice my double is: 20.0
B: my string is AAA
A: my number is 10
The issue is:
1) You're declaring the type as "B" (not "A")
2) B.p(10) can accept an int as a floating point argument
3) Consequently, that's what you're getting
It's really an issue of what argument types can be implicitly converted, than what methods are overloaded or overridden.
When the object has declared type B, the double version is invoked because it's compatible with an int argument, since in Java int is a subtype of double.
When the object is declared as a A, it has the method p() overloaded with two versions:
p(int arg);
p(double arg);
So, when you pass an int, the first version is picked because it's more accurate, and when you pass double the second one, because it's the most specific signature.
For reference, see the relevant JLS at ยง15.12.2 and this post by Gilad Bracha. BTW, don't try to figure out how the language should behave based on what you think is the most logical way, because every programming language is an engineering effort, and this means that there's a price you pay for whatever you take. The primary source of information for Java are the JLS, and if you read it carefully, you'll (surprisingly?) discover that there are even cases where the line in the source code is ambiguous and cannot be compiled.
To make the effect of widening of int to double more vivid I have created another example which is worth looking. Here, instead of double I have created a class called Parent and instead of int a Child class is created.
Thus,
double ~ Parent int~Child
Obviously child object can be widened to Parent reference.
package test;
public class OOPs {
public static void main(String[] args) {
Child ch = new Child(); // like int 10
Parent pa = new Parent();// like double 10.0
B a = new A(); // case 2 : A a = new A();
a.p(ch);// 10
a.p(pa);// 10.0
}
}
class B {
public void p(Parent i) {
System.out.println("print like 20");
System.out.println(i.getClass().getName());
}
}
class A extends B {
public void p(Child i) {
System.out.println("print like 10");
System.out.println(i.getClass().getName());
}
}
class Parent {
String name;
Parent() {
name = "Parent";
}
public String getName() {
return name;
}
}
class Child extends Parent {
String name;
Child() {
name = "Child";
}
public String getName() {
return name;
}
}
Case 1 - Output (B a = new A();)
print like 20
test.Child
print like 20
test.Parent
Case 2 - Output (A a = new A();)
print like 10
test.Child
print like 20
test.Parent