Why is interface default method shadowed by parent private method? [duplicate] - java

Consider below class hierarchy.
class ClassA {
private void hello() {
System.out.println("Hello from A");
}
}
interface Myinterface {
default void hello() {
System.out.println("Hello from Interface");
}
}
class ClassB extends ClassA implements Myinterface {
}
public class Test {
public static void main(String[] args) {
ClassB b = new ClassB();
b.hello();
}
}
Running the program will give following error :
Exception in thread "main" java.lang.IllegalAccessError: tried to access method com.testing.ClassA.hello()V from class com.testing.Test
at com.testing.Test.main(Test.java:23)
This is all because I marked ClassA.hello as private.
If I mark ClassA.hello as protected or remove the visibility modifier(i.e. making it default scope), then it shows a compiler error as :
The inherited method ClassA.hello() cannot hide the public abstract method in Myinterface
However, as per exception stacktrace above, I get a runtime IllegalAccessError.
I couldn't get why this is not detected at compile time. Any clues ?

Update: Seems like it's really a bug.
A class or super-class method declaration always takes priority over a default method!
default hello(...) method from the Myinterface allows you to write without errors:
ClassB b = new ClassB();
b.hello();
Until runtime, because at runtime hello(...) method from the ClassA takes the highest priority (but the method is private). Therefore, IllegalAccessError occurs.
If you remove the default hello(...) method from the interface, you get the same illegal access error, but now at compile time.

Related

Why doesn't Java automatically access the overridden method?

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.

static method in class have same signature as default method in interface

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().

Are private instance method not bounded at run time?

Please have a look at following code.
class TestClass{
private void privMethod()
{
System.out.println("TestClass Method");
}
public static void main(String... args)
{
TestClass obj=new SubClass();
obj.privMethod();
}
}
class SubClass extends TestClass
{
private void privMethod()
{
System.out.println("SubClass Method");
}
}
when I compile this code its gets compiled fine.
when I run TestClass, the output is: TestClass method
The ref. variable obj is of TestClass type but it is referring to a Sub class' object so at run time it should bind with Sub's privMethod().
Can you explain this?
private methods are not overriden by methods in a child class. Only public, protected and package-private ones can be overriden. Therefore polymorphism behavior at runtime is not applied.
If you add the annotation #Override to the declared method in the subclass, you would get a compilation error:
#Override // error
private void privMethod()
{
System.out.println("SubClass Method");
}
private modifier—the field/method is accessible only within its own class.
For more on access specifier.
So although the SubClass extends TestClassbut privMethod() of TestClass is not visible to the SubClass at all. So when you are implementing the privMethod() method in SubClass, it is not overriding the method of TestClass.
To make sure that you are overriding the method of the superclass, you can use #Override Java annotation.If the method does not match a method in the superclass, the compiler will give you the below error
The method privMethod() of type SubClass must override or implement a supertype method

Overriding private method

We can not override a private method then why does the followng code does not give an error. Instead it produces the output.
class A {
private void fun() {
System.out.println("ths is a private method");
}
}
class B extends A {
void fun() {
System.out.println("ths is wrng");
}
}
class C {
public static void main(String args[]) {
B ob = new B();
ob.fun();
}
}
private methods are not inherited. In your main method you're invoking the fun() method on a variable of type B. The fun() method of type B seems to be accessible, assuming your class B and class C are in the same package.
Had you done this
A ob = new B();
ob.fun();
Then you would have gotten your compilation error.
B#fun() is completely unrelated to A#fun().
Both are completely different methods and not related to each other. As private methods are not inherited there is no concept of overriding here.
Method fun() is not overridden in B class because it was never inherited from class A.
What you are invoking in your code is method of B class and that has no relation with class A and method A#fun().
To check this, Add a #Override annotation and you will get compile time error.
// Compilation error.
Class B extends A{
#Override
void fun(){
System.out.println("ths is wrng");
}
}
Now this code will not compile.
First of all, private methods are not inherited in child classes. If you still try to write a method with same name as that of parent class, java compiler will consider the method in child class as a completely new method and will not throw any exception. Its like the new method in child class shadows the private method in parent class.

Java ensure class is resolved

I have come into a situation where I want to conditionally load and resolve a class.
Loading it is as easy as getting a handle to the correct ClassLoader and calling the loadClass method. after calling the loadClass method, I get a valid handle to the class I wanted to load. However, the target class' static intializers are not getting invoked because the class is not being resolved until later. Unfortunately, the classes static initializers register important callbacks. An example of this is shown below.
public class ClassA
{
public static void main(String[] args) throws Throwable
{
Class<?> classB = ClassA.class.getClassLoader().loadClass("ClassB");
System.out.println(classB.getName());
}
}
public class ClassB
{
static
{
System.out.println("Class B initializer.");
}
}
The output of running ClassA in this example is of course:ClassB
I have found a temporary workaround which is (in my opinion) a bit of a hack
and that is to call an unimportant static function in ClassB which forces the the class to be resolved. This method is shown below.
public class ClassA
{
public static void main(String[] args) throws Throwable
{
Class<?> classB = ClassA.class.getClassLoader().loadClass("ClassB");
classB.getMethod("something").invoke(null);
System.out.println(classB.getName());
}
}
public class ClassB
{
static
{
System.out.println("Class B initializer.");
}
public static void something(){}
}
The output from this method is (as expected):
Class B Initializer.
ClassB
This method has many problems with it such as I have to depend on the class having a public static function, and worry about any side effects of calling that function. Is there a more legitimate way to force classes to resolve?
You can use Class.forName(..) to have your class initialized and get a reference to its Class object.
Class<?> classB = Class.forName("ClassB");
The javadoc states
A call to forName("X") causes the class named X to be initialized.
Related:
What purpose does Class.forName() serve if you don't use the return value?

Categories