I'm new to Java and I have struck with some doubt. For example, consider the expression like:
a.method()
a.method("string")
people call them "dynamic dispatch". But I'm sure that the type checker makes sure that the methods named method(),method(String a) available for object a.
But why does it called "dynamic"? It isn't static call? Since the compiler found that already?
The example that you have posted will not use dynamic dispatch. You have posted an example of Method Overloading. And decision of method invocation in case of Overloading is done at compile time. It is on the compiler to decide which method will be invoked based on the formal parameters and the actual arguments passed.
Dynamic Binding comes into play when you are working with Method Overriding, where the decision of which method will actually be invoked is delayed till runtime.
For e.g: -
class A {
public void demo() { }
}
class B extends A {
public void demo() { }
}
public class Test {
public static void main(String[] args) {
A a = new B();
a.demo(); // B class method will be invoked.
A obj = new A();
obj.demo(); // A class method will be invoked.
}
}
The decision of which method is invoked is decided on the basis of which class instance the particular reference is pointing to, And that is only known at runtime. And hence Dynamic Dispatch.
The code you are showing doesn't express dynamic dispatch(binding).look at the below code.
class Super {
public void method() {}
}
class Sub extends Super {
public void method() {}
public static void main(String... args) {
Super inst = new Sub();
inst.method(); //Sub's method() would be invoked.(Express's Dynamic Dispatch)
}
}
Related
Given two classes, a parent class and a child class:
class A {
private void greet() {
System.out.println("Class A");
}
}
class B extends A {
public void greet() {
System.out.println("Class B");
}
}
One has a method called greet(), which is private, and the other one defines a method with the same name, only that it is public. Now, as far as I know, the child's greet() method doesn't override the parent's method, because it 'hides' it? (given the fact that private methods can't be overridden?)
Now, given the following class and method (considered to be in the same package as A and B):
public class Main {
public static void main(String[] args) {
B b = new B();
b.greet();
}
}
This should compile. But this one:
public class Main {
public static void main(String[] args) {
A a = new B();
b.greet();
}
}
This one up here doesn't compile, because it's missing a typecast.
My question would be: why? If the greet() method was public in both places, it would have shown Class B both times. I'm pretty confused about why Java doesn't figure at runtime, for the second case, that a is actually referencing to an object of type B, and directly calls the method from class B.
Tried reading more about polymorphism in an OCA-preparation book, but the authors didn't seem to be so specific about it.
In your snippet that doesn't compile, the compiler sees that the compile time type of a is class A.
Therefore, it will only allow you to call accessible methods of class A (or of super-classes of A). greet is a private method of A, and therefore not accessible. Therefore the compiler doesn't allow calling it.
The fact that the runtime type of a would be class B, which has an accessible greet method, makes no difference, since the compiler doesn't try to figure out what the runtime type of a variable would be.
At compile time A.greet() is not accessible.
Hence does not compile.
For overriding, the method must be accessible at compile time, but the decision to call the method is done at runtime.
I have created anonymous class by implementing interface I inside public static void main() method. So, by java 8 for the abstract method test(), the implementation is provided from imple() method of class C.
So, inside public static void main() method, printing _interface.getClass(), I got
package_path.Main$$Lambda$1/310656974 which is absolutely fine. Bacause it print's the anonymous class name.
Also, _interface is pointing to an anonymous object in heap and hence I'm doing _interface.test();
So, the first statement that test() method has now is to print the class name,
But eventually what it print was,
package_path.C (telling me C is the class name). How is that possible? Shouldn't package_path.Main$$Lambda$1/310656974 be printed again? Because 'this' means anonymous inside the test method right?
#java.lang.FunctionalInterface
interface I {
void test();
}
class C {
void imple() {
System.out.println(this.getClass());
System.out.println("Inside Implementation");
}
}
class Main {
public static void main(String[] args) {
I _interface = new C()::imple;
System.out.println(_interface.getClass());
_interface.test();
}
}
Hopefully, this might help you understand, that when you declare
I _interface = new C()::imple;
you've actually implemented the interface somewhat similar to (though not same as):
I _interface = new I() {
#Override
public void test() {
new C().imple(); // creating an instance of class `C` and calling its 'imple' method
}
};
Hence when the test method is called, it first creates an instance of C which prints
class x.y.z.C
as the class.
Because 'this' means anonymous inside the test method right?
Now as you can see above, there is no more anonymous class from which imple
is being called from, hence this is not representing the anonymous class anymore.
As Holger clarified in comments further, despite the representation as lambda or anonymous class at the calling site, the this.getClass() inside a method of class C will evaluate to C.class, regardless of how the caller looks like.
Recommend: Continue to read and follow on Is there any runtime benefit of using lambda expression in Java?
I have below scenario :
class C {
static void m1() {}
}
interface I {
default void m1() {}
}
//this will give compilation error : inherited method from C cannot hide public abstract method in I
class Main extends C implements I {
}
Below are my questions:
I am aware that instance method will override the default methods but what if static methods in class have same signature as default method in Interface?
If static method m1() in class C would be public then compilation error will be :
static method m1() conflicts with abstract method in I.
so when the access modifier was default it was trying to hide and when it is public it is conflicting. why is this difference? what is the concept behind it?
Ultimately that boils down to the fact that when you have something like this:
class Me {
public static void go() {
System.out.println("going");
}
}
These both would be allowed:
Me.go();
Me meAgain = new Me();
meAgain.go(); // with a warning here
Intersting is that this would work too for example:
Me meAgain = null;
meAgain.go();
Personally I still see this as design flaw that could not be retracted due to compatibility - but I wish the compiler would not allow me to access the static method from an instance.
Your first question is not related to java-8 per-se, it has been like this before java-8:
interface ITest {
public void go();
}
class Test implements ITest {
public static void go() { // fails to compile
}
}
default methods just follow the same rule here. Why this happens is actually detailed quite a lot on stack overflow - but the underlying idea is that potentially this would cause confusion on which method to call (imagine ITest would be a class that Test would extends and you do ITest test = new Test(); test.go(); -> which method are you calling?)
I think that for the same reasons this is not allowed also (which is basically your second question, otherwise you would have a static and non-static method with the same signatures)
static class Me {
static void go() {
}
void go() {
}
}
It's interesting that this is sort of fixed (I guess that they realized it would be really bad to do the same mistake again) in method references:
static class Mapper {
static int increment(int x) {
return x + 1;
}
int decrement(int x) {
return x - 1;
}
}
Mapper m = new Mapper();
IntStream.of(1, 2, 3).map(m::increment); // will not compile
IntStream.of(1, 2, 3).map(m::decrement); // will compile
Answering your 1st question:
Both the "static method in class" and the "default method in interface" are available to the class Main, and hence if they have the same signature, it will create ambiguity.
For example:
class C{
static void m1(){System.out.println("m1 from C");}
}
public class Main extends C{
public static void main(String[] args) {
Main main=new Main();
main.m1();
}
}
Output: m1 from C
Similarly,
interface I{
default void m1(){System.out.println("m1 from I");}
}
public class Main implements I{
public static void main(String[] args) {
Main main=new Main();
main.m1();
}
}
Output: m1 from I
As you can see, both these can be accessed similarly. So this is also the reason for conflict when you implement I and extend C.
Answering your second question:
If your classed and interfaces are in the same package, the default and public access modifier should work similarly.
Also, m1() in C is static which cannot be overridden, and hence it cannot be considered as implementation of m1() in I and so the compilation issue.
Hope that helps!
I will answer your first question since the second is already answered
I am aware that instance method will override the default methods but
what if static methods in class have same signature as default method
in Interface?
I am assuming you are using JDK 1.8 and hence the confusion. default modifier in an interface method is not talking about its access specifications. Instead it mentions that the interface itself need to implement this method. Access specification for the method is still public. Starting from JDK8 , interfaces allow you specify methods with default modifer to allow to extend interfaces in a backward compatible way.
In your interface you had to give default void m1() {} for the compilation to be successfull. Normally we simply define them in an abstract way like void m1(); in an interface You had to implement the method because you specified the method as default. Hope you understand.
Because class methods in java can also be called using instance variables, this construct will lead to ambiguities:
Main m = new Main();
m.m1();
It is unclear if the last statement should call the class method C.m1() or the instance method I.m1().
public class B extends A{
public static void main(String[] args) {
new B().privateMethod();//no error -output B-privateMethod.Sounds like overriding
new B().staticMethod(); //no error -output B-StaticMethod.Sounds like overriding
}
private void privateMethod() {
System.out.println("B-privateMethod.");
}
static void staticMethod() {
System.out.println("B-StaticMethod.");
}
}
class A{
private void privateMethod() {
System.out.println("A-privateMethod.");
}
static void staticMethod() {
System.out.println("A-StaticMethod.");
}
}
On R&D I found in case of privateMethod()- since this method was not available on object of child class so child class's and parent class's privateMethod() are separate method and they have no relationship so this is not overriding.
but in case of staticMethod()- parent class's method was available on object of child class ,and when we define this in child class, object of child class start pointing to child class method.this looks like method overriding but not,since static method does not override.
how does static method handle by java developement kit?
new B().privateMethod();
this is not overriding, since B doesn't see A's privateMethod().
new B().staticMethod();
this is not overriding, calling a static method via an instance is allowed, though it can be confusing. It is exactly the same as calling it via the class name - B.staticMethod(). If a super class A of B has a static method visible from B, you can call that method from B (and it doesn't matter if you write B.staticMethod() or A.staticMethod() or new B().staticMethod().
If later you define a static method of the same name in B, that method hides the method of the same name in A, so calling B.staticMethod() or new B().staticMethod() now invokes B's static method. However, calling A.staticMethod() will still invoke A's static method.
Polymorphism is not for static methods. Static methods are called with JVM instructions invokestatic, whereas polymorphism is achieved with invokevirtual. The calls to static methods are determined at compile time, and polymorphic methods are dynamically dispatched at runtime.
You can easily tweak your code so that A.staticMethod() is called, by just assigning new B() to a variable of type A.
public static void main(String[] args) {
new B().privateMethod();
A b = new B(); // change here.
b.staticMethod(); // A.staticMethod() is called here.
}
Never speak about static and override in the same sentence.
The whole concept of overridable methods is to dynamically bind at runtime which method is to be executed. Consider this:
class A { void print() { out.println("A"); }
class B extends A { void print() { out.println("B"); }
A obj = new B();
obj.print();
Although the variable obj is of type A, it still prints out "B".
Static methods on the other hand are bound at compile time. This means the compiler uses the type of the variable (or the expression) to determine what method to execute:
class A { static void print() { out.println("A"); }
class B extends A { static void print() { out.println("B"); }
A obj = new B();
obj.print();
This now yields "A". Unfortunately the Java language allows to call static methods on variables or expressions. This is not recommended! Better call static methods on the type itself:
A.print();
B.print();
In the first example - obj.print(); - the compiler automatically translates the statement into A.print(). The actual object does not count. In fact you could write the following:
A obj = null;
obj.print();
Or:
((A) null).print();
That still prints "A".
If Method calls are dynamically binded then why the Compiler complains
The method run() is undefined for the type B
Why is compiler checking for the presence of method run in Class b
Here is the code
import java.lang.*;
public class Program
{
public static void main(String [] args)
{
B a = new A();
a.run();//compiler complains at this line.
a.p(10);
a.p(10.0);
}
}
class B {
public void p(int i)
{
System.out.println(i*2);
}
}
class A extends B{
public void p(int i)
{
System.out.println(i);
}
public void run(){
}
}
Java is by design a statically typed language, meaning that the compiler must know and be able to guarantee that an implementation of that method exists in every concrete object. (Maxim Shoustin's answer very nicely demonstrates the reason behind this design decision with an example.)
If the compiler were to assume without any guarantees that an unknown object will happen to have a specific method, it would make Java a duck typed language. This could have its own advantages, but it wasn't in accordance with the design goals of Java.
In practice, in statically typed languages, virtual (meaning non-final) methods (such as your run() method) are resolved dynamically but the strategy used to resolve them is still written at compile time. That strategy may, for example, involve reading the correct offset of the vTable (a table containing the addresses of the actual implementations of the virtual methods of that object), in many implementations of polymorphism - leveraging the type safety of the language to gain some performance during the dynamic dispatch.
The method run() is undefined for the type B
The error is self explanatory. The type B doesn't have a method called .run()
B a = new A() means that your variable a is of type B and that is all the system knows about the variable a.
If you did Object s = new String() and then did s.toLowerCase() it would fail as well, because the variable s is of type Object not of type String.
whatever type your variable is, is the only behaviors that you can call on that type.
Its easy to show:
Let me change your code a bit:
B = Animal
A = Cow
after replace:
public class Program
{
public static void main(String [] args)
{
Animal a = new Cow();
a.sayMooo();//compiler complains at this line. You try to animal to say "moo"?
a.speed(10);
a.speed(10.0);
}
}
class Animal {
public void speed(int i)
{
System.out.println(i*2);
}
}
class Cow extends Animal{
public void p(int i)
{
System.out.println(i);
}
public void sayMooo(){
}
}
Not all animals are Cows
and sure
Not all animals say "mooo"
but
all Cows are animals