3

To my understanding, the following code should print a as per my knowledge of run time polymorphism.

However, when I run the following code it is printing b:

Per JLS 8.4.8.1, B1.m1 does not override A1.m1, and so when A1.m1 is invoked, B1.m1 should not be selected

package a;

public interface I1 {
    public Object m1();
}

public class A1 {
    Object m1() {
        return "a";
    }
}

public class C1 extends b.B1 implements I1 {
    public static void main(String[] args) {
        a.A1 a = new a.C1();
        System.out.println(a.m1());
    }
}

package b;

public class B1 extends a.A1 {
    public String m1() {
        return "b";
    }
}

Can some one help me understand this behavior.

T-Bag
  • 10,916
  • 3
  • 54
  • 118
  • In java, method in superclass is not automatically invoked before overridden method. But constructor in superclass is called before overriden constructor. – Jay Smith Jun 28 '17 at 07:01
  • 1
    The `Test1` class inherits the `m1` method from `B1`. So if you call `m1` on any `Test1` object, it will print `"b"`. If you say `new Test1()`, then you've created a `Test1` object, so it will print `b`. It doesn't matter that the variable `a` is declared as an `A1`--the object it refers to is still a `Test1`. All the `A1` says is that `a` can be a reference to _any_ object of class `A1` or a subclass. It doesn't change the type of the actual object. – ajb Jun 28 '17 at 07:01
  • 1
    Just looking at it, it looks like it should print `b`. Why do you think it should print `a`? – Kevin Krumwiede Jun 28 '17 at 07:01
  • I miss the point here. Why mixing inheritance and interfaces? Anyway, did you read what polymorphism is? – Mario Santini Jun 28 '17 at 07:02
  • Place breakpoints so you will understand it – Akshay Jun 28 '17 at 07:02
  • @Akshay how would that help? He would find out that it executes the `return "b"` statement, but he already knows that. He didn't understand why the language semantics say it should execute that statement. Breakpoints won't help him understand that. Neither will a debugger. – ajb Jun 28 '17 at 07:04
  • 1
    `a`'s real type is not `A1`, it's `Test1` (which inherits from `B1`) – Oneiros Jun 28 '17 at 07:05
  • Please check its not overriden, why we all are in rush to answer. – T-Bag Jun 28 '17 at 07:08
  • I don't see why you think [8.4.8.1](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8.1) would say it doesn't override. In `A1`, `m1` has "package access" (which is what you get when you don't say `public`, `protected`, or `private`), and it's in the same package as the subclass. – ajb Jun 28 '17 at 07:09
  • @ajb- Ok got it give me 5 mins i will update the question with package names. – T-Bag Jun 28 '17 at 07:09
  • @ajb- Please check and share your knowledge. – T-Bag Jun 28 '17 at 07:17
  • Check the doc referenced in this answer: https://stackoverflow.com/questions/23081671/why-can-i-override-a-protected-method-with-public-method you can override a default (package private) access method with a public method. – riskop Jun 28 '17 at 07:19
  • its simple `class` takes preference over `interface` so its returning `b` – bananas Jun 28 '17 at 07:21
  • Now `a`'s type is `C1`, which inherits from `B1` which inherits from `A1`. But `B1` overrides the behavior of `A1.m1()` so the output will be `"b"` – Oneiros Jun 28 '17 at 07:21
  • public class `C1 extends b.B1` (will be prefered instead of -->) implements I1 – bananas Jun 28 '17 at 07:22
  • 1
    why did you change the code from `Test1` to `C1` when so many comments and answer refer to you original code? Hard to read or understand now. – P.J.Meisch Jun 28 '17 at 07:32
  • Please, be careful about your edit next time, see [Is it OK to make a clarifying edit to a question if it will invalidate existing answers?](https://meta.stackoverflow.com/questions/266946/is-it-ok-to-make-a-clarifying-edit-to-a-question-if-it-will-invalidate-existing). I know this is a bit complicated to monitor, but your question was valid at first so this is borderline, **but the question is really interesting now** – AxelH Jun 28 '17 at 08:22

6 Answers6

2

The expected output is indeed b.

When you declare your object a as being of the type A1, that class defines only the interface of the methods. It defines that m1 returns a String, but the implementation of that method is defined by the Class used to build the object, which is Test1. And Test1 extends B1, which overrides the method m1, so that is the implementation of m1 used for your object.

The output of that call m1() must be indeed the B1's.

EDIT: This answer was written for the first version of the question. OP changed a lot of the code, but the root of the explanation is still the same.

Alberto Trindade Tavares
  • 10,056
  • 5
  • 38
  • 46
  • 1
    @AlbertoTrindadeTavares I don't think your last statement is correct. Different access modifiers doesn't mean no overriding. – ajb Jun 28 '17 at 07:10
  • @ajb To be honest I am not quite sure now if the access modifier is part of the signature of the method – Alberto Trindade Tavares Jun 28 '17 at 07:11
  • see http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2 which tells you when two methods have the same signature. Access modifiers aren't mentioned. – ajb Jun 28 '17 at 07:12
  • That is true, but a subclass overriding a method by making it public, making it more visible, maybe is not allowed. – Alberto Trindade Tavares Jun 28 '17 at 07:13
  • Removed that last statement to avoid misleading – Alberto Trindade Tavares Jun 28 '17 at 07:13
  • Making it more visible doesn't cancel out overriding. (Making it less visible is illegal, if it overrides.) – ajb Jun 28 '17 at 07:14
  • **A agree that your edit is necessary, this was valid at first.** But the last part is not true, this explanation lack some information as this doesn't explain how, now, a non-overriding method (`B1.m1` don't override the `A1.m1` method, as this is not accessible to it), is called with that declaration. – AxelH Jun 28 '17 at 08:18
2

After adding the packages, the question is much more difficult. I've tried this, and I changed your main program to

public class C1 extends b.B1 implements I1 {
    public static void main(String[] args) {
        a.A1 a = new a.C1();
        System.out.println(a.m1());
        a.A1 b = new b.B1();
        System.out.println(b.m1());
    }
}

(I actually used different package names to avoid conflicts with the variable names. So my code looks a bit different from the above.)

I've confirmed that this prints "b" and "a". That is, if I create a new B1, its m1 method does not override the one in A1. Thus if I print b.m1(), since b is of type A1, polymorphism doesn't kick in, and the method declared in A1 is called. So what's going on with C1?

C1 inherits the m1 method from B1. But even though the m1 method in B1 doesn't override the one in A1, the m1 method in C1, which it inherits from B1, actually does override the one in A1. I think it has to do with this clause in 8.4.8.1:

mA is declared with package access in the same package as C, and either C declares mC or mA is a member of the direct superclass of C.

Here C is your C1 class. mC is the m1 that's inherited from B1. In this case, "C declares mC" is false, because C1 doesn't declare m1, it inherits it. However, I believe "mA is a member of the direct superclass of C" is true. As I understand it, B1 has all the members that A1 has. B1 declares its own m1, and since it's not overriding, it's a new m1 that causes the m1 it inherits from A1 to be hidden. But even though it's hidden, it's still a member. Thus the condition that mA is a member of the direct superclass of C (which is B1) is satisfied, and thus all the conditions of 8.4.8.1 are satisfied and thus the inherited m1 in C1 overrides the one in A1.

ajb
  • 31,309
  • 3
  • 58
  • 84
  • 1
    Yep, that's what I think too (the hidden part) even if this is complicated to explain, your second example prove that his is not a overriding. This only works because of the interface `I1` that render the `B1` a valid `m1` definition in `C1`, giving the override to `B1` and hidding `A1`. – AxelH Jun 28 '17 at 08:03
  • @AxelH When I tried this, I forgot to try it without `implements I1`. I just tried it, and confirmed that this changes the behavior and there is no longer an override. – ajb Jun 28 '17 at 14:19
1

The following line A1 a = new Test1(); simply means build a Test1 instance and store it in a A1 box.

So the instance will be a Test1, but you will only have access to the method/variable of A1, but every overriden method in Test1 will be accessed.

This is polymorpish.

By reading the JLS about 8.4.8.1. Overriding (by Instance Methods) about the accesor

An instance method mC declared in or inherited by class C, overrides from C another method mA declared in class A, iff all of the following are true:

  • A is a superclass of C.
  • The signature of mC is a subsignature (§8.4.2) of the signature of mA.
  • mA is public.

You can find more information about the access modifiers in 8.4.8.3. Requirements in Overriding and Hiding

The access modifier (§6.6) of an overriding or hiding method must provide at least as much access as the overridden or hidden method, as follows:

  • If the overridden or hidden method is public, then the overriding or hiding method must be public; otherwise, a compile-time error occurs.
  • If the overridden or hidden method is protected, then the overriding or hiding method must be protected or public; otherwise, a compile-time error occurs.
  • If the overridden or hidden method has package access, then the overriding or hiding method must not be private; otherwise, a compile-time error occurs.

EDIT :

Now, with your package added.

Having C1 to implement m1 (because of the interface), you are hiding the method of A1 with the implementation you find in B1, this method is indeed a valid definition for the interface contract.

You can see you are not overriding the method (you can't call super.m1 or even add @Override on the B1.m1. But the call a.m1() is valid as it is define in the class itself.

Community
  • 1
  • 1
AxelH
  • 14,325
  • 2
  • 25
  • 55
0

You are Overriding. Include the @Override annotation and you can see that. As long as your class extending can override the parent class method, you can increase the access, but not decrease the access.

If you tried to make B#m1 private, then somebody could just cast to an A and use the method.

Conversely, if you make A#m1 private, then B cannot override it and you can end up with an object having two methods with the same signature.

static class A{

    private String get(){
        return "a";
    }

}

static class B extends A{

    public String get(){
        return "b";
    }
}
public static void main (String[] args) throws java.lang.Exception
{
    A b = new B();
    System.out.println(b.get());
    System.out.println(((B)b).get());
    // your code goes here
}

This will output:

  • a
  • b
matt
  • 10,892
  • 3
  • 22
  • 34
  • There are no `private` methods in the OP's question. So this doesn't solve his problem. – ajb Jun 28 '17 at 07:37
  • @ajb What is the problem? OP claims they are not overriding. They are, it is easily seen by adding `@Override`, because the code still compiles. AxelH included the information. – matt Jun 28 '17 at 07:45
  • The problem is that OP could see that it was overriding, but his understanding of the rules said it shouldn't. His question is about the language rules, and _why_ the rules say it's overriding. Showing that it is overriding by adding `@Override` doesn't answer that question. Showing an example using `private` also doesn't answer the question, because the rules are very different where `private` is involved. – ajb Jun 28 '17 at 14:22
  • The question has been significantly altered, Op claimed it wasn't overriding initially, so I answered the question as it was and pointed out it was overriding. The private part was a bit of a tangent although it does make it closer to the package private part. Does it matter if you hide the method by making it package private and place it in a different package, versus making it actually private? – matt Jun 28 '17 at 14:37
  • To answer the last question: yes, and the question itself shows why. If you "hide" the method by making it package private, it can "come out of hiding" when you have a chain of subclasses A -> B -> C where A and C are in the same package but B is not. You can't do that with a private method. The language rules appear to have been written carefully to make sure it behaves this way. I'm guessing that this is so that you can write something like `class C extends T { ... }` and make sure C can access package-private stuff even if `T` can't. – ajb Jun 29 '17 at 03:54
0

You have an interface I1, which is implemented by A1 Class B1 extends A1 Class C1 extends B1 (and therefore implicitly extends A1).

So an instance of C1 is also of type B1, A1 & I1, however it remains an instance of C1 regardless of whether you assign it to one of the other types.

If you have:

 I1 instance = new C1();
    String value = instance.m1();

The first m1 method going up the inheritance tree from the real type (C1) will be called, which will be in B1 and return "b".

bananas
  • 1,176
  • 2
  • 19
  • 39
MartinByers
  • 1,240
  • 1
  • 9
  • 15
0

All comments and answers are mostly correct. They explain things in term of language mechanisms. I think, instead, that to realise the real meaning of inheritance and polymorphism and how to use them, you have to take a more conceptual approach.

First of all inheritance is a relationship between two things and the relationship is of the type “is a”. In other words when you write the statement class C1 extends B1 you mean C1 is a B1. Of course this won’t work with A1, B1 and C1. Let me change them in something more real. For example:

A1 = Animal

B1 = Feline

C1 = Cat and C2 = Lion (polymorphism)

At this point you will have class Cat extends Feline, and you can conceptually read it as: Cat is a Feline. I suggest to challenge your inheritance formal correctness using the “is a” test. If it doesn't work, it is better to reconsider or rethink the inheritance. Your resulting code will be like the following:

public interface IAnimal {
    public Object saySome();
}

public class Animal {
    Object saySome() {
        return "I am an animal";
    }
}

public class Feline extends Animal {
    public String saySome() {
        return "I am a feline";
    }

public class Cat extends Feline implements IAnimal {
    Object saySome() {
        return "meow";
    }
}

public class Lion extends Feline implements IAnimal {
    Object saySome() {
        return "roar";
    }
}

class Aplication {
    public static void main(String[] args) {
        Animal anAnimal = new Cat();
        Animal anotherAnimal = new Lion();
        System.out.println(anAnimal.saySome());
        System.out.println(anotherAnimal.saySome());
    }
}

And clearly the output will be

meow

roar

I hope this will help.

RobertoN
  • 16
  • 3
  • 1
    It would have been valid in the first step of the question, but now, the relation is much more complex than that, so simply explaining the "correct" polymorphism isn't enough. This doesn't explain why OP's code print "b". – AxelH Jun 28 '17 at 08:29