You can now use aTask to do anything that's valid for a Task. Later you coiuld change the PracticeTest ofr a FinalTest or any other subclass of Task without breaking any code.
A better example:
List myList = new ArrayList();
myList has to be a List because of the logic of the program, but the code doesn't care whether it's an ArrayList or a LinkedList. Eg you can try both to see which is fastest without changing any other code.
So which mean aTask can execute any methods from PracticeTest class, in which the method also must be appear in Task class?
If i still get it wrong. Perhaps code of picture will be the better understanding.
If the PracticeTest class inherits from Task, then you can use it to initialize a Task variable. However, you won't be able to use PracticeText methods unless they were also inherited from Task. This is where interface classes can be useful.
Back up one step...
You declare a reference variable of type Task. That variable can refer to any instance of Task, or any instance of any subclass of Task. (Because any subclass of Task has all the methods that a Task has). You can use that variable to access any method defined in the Task class. If the variable refers to a subclass of Task then those accesses will still be valid, regardless of whether the method's implementation is inherited from Task or overridden in the subclass.
OK, rather than getting caught up in the details of the specific matter, let's talk basic principles for a bit.
The main purpose of inheritance is to allow common elements of two or more classes to be defined in a way that the child classes can share what is defined in the parent class. Let's say you have a class Vehicle, which defines - without regard to the mechanism - a method move(). Now, if you want a class Automobile that describes the properties and behaviors of a car, you need not define an entirely new class; you can inherit most of what you need from Vehicle, including the move() method. You can override the existing move() method or not, depending on how you want it to behave. You can also add new methods, such as brake(), to extend the abilities of an Automobile beyond those of the parent Vehicle class.
Now, here's the important part in regards to your question: since an Automobile is an instance of a Vehicle (given our definition of Automobile), you can have any Vehicle instance hold an Automobile as a kind of Vehicle. Because it is a Vehicle, any method that is common to all Vehicles can be applied to the variable, including move(), and it will correctly use the version of the method for the actual object's class. In other words, even though it is a Vehicle variable, since the object is an Automobile, it will use the move() for an Automobile, if it is overridden.
Now, you have to note that if the variable is declared as the parent class, it will only support those methods that are in the parent class, so you can't have it use brake() even if the object is really an Automobile - a Vehicle doesn't have that method. But any method defined in the parent class will (in accordance with the Liskov Substitution Principle) work regardless of whether the object is of that class or a child class.
aTask is defined as being of type Task, so you can use it to refer to any methods defined in the Task class. If, and any particular time, it happens to refer to a PracticeTask, then it will use the method definition from the PracticeTask class - which may be defined in that class or inherited from Task.
What you can't do is refer to any methods that are declared in PracticeTask but not in Task
Okay. According to your sentence.
aTask is able to refer any methods from Task class. But it is also possible to refer particular methods from PracticeTask class if and only if, the method define in PracticeTask is kind of overriding the Task class method.
Am i correct?