What do you get when you try to compile that code? Do you know why?
Once you can answer that, it should help you eliminate B c = new A() from consideration.
As for the other two: B b = new B() A a = new B() If B IS-An A, then we should be able to use B wherever we us A. So unless there is a specific reason to keep referring to b as a B, why not just refer to it as an A? We don't lose anything.
We do gain things though. Let's actually use the objects we created. If I change my Main class to do some work, it might look like this:
Here, the method work() requires an object of type B. All it is doing is calling b.doSomething(). But I could have written the code like this:
What happens here? How are the results different?
Once you try it you should see the results are exactly the same, so you should see we haven't lost anything by referring to B as if it were an A. But what can we gain by it?
Well, perhaps later I want to add another class that extends A, we call this one D:
Back to my main class, if I wanted to use D to do the work() method, if I tried to use the first method I wouldn't be able to because D is not a B.
But if I tried to use the second approach, where I pass an A as the parameter into the work() method, then I can use the same work() method for Bs and Ds, and any other class which extends A:
Polymorphism will let me use the same code to handle different types of objects, so I don't need separate work(A), work(B), work(D) methods. I get to reuse the same method and get more bang for my line of code. It makes it easier to debug, too, because I find I have an error in work() I can fix it once and it will be fixed for all my objects that extend A, I don't have to go back and fix it for each type.