Khi một class kế thừa class khác qua từ khóa extend , ta hiểu nó là Class con còn class được kế thừa là class cha, và việc class con này có thể định nghĩa lại một bản thể mới của method trong class cha được gọi là method Overriding. "Định nghĩa lại" ở đây tức là class con chỉ được thay đổi nội dung bên trong method chứ không được thay đổi 1 trong 4 yếu tố sau: return type, parameter type, số lượng parameter, thứ tự parameter. 4 yếu tố này còn được gọi là Method Signature. Method Overriding sẽ tận dụng được runtime polymorphism.
#1 Thế nào là đa hình - polymorphism?
Khái niệm "đa hình" trong lập trình hướng đối tượng có thể hiểu là mỗi đối tượng thể hiện những "hành động" khác nhau trong từng hoàn cảnh khác nhau.
#2 Runtime Polymorphism là gì ?
Runtime Polymorphism còn được biết đến với cái tên: Dynamic Method Dispatch. Khái niệm này được định nghĩa tương ứng với tình huống một method (đã được override) được gọi theo object, không phải theo reference type.
#3 Ứng dụng của runtime polymorphism?
Đầu tiên ta cần phân biệt giữa static và dynamic polymorphism.
Các biến và method đi kèm với từ khóa private, static, final sẽ sử dụng tính năng static binding được liên kết bởi compiler. Trong khi dó virtual method thì được liên kết trong quá trình runtime dựa trên runtime object.
Sử dụng @Override
Sử dụng Annotation này cho phép ta tận dụng cơ chế làm việc của compiler để đảm bảo chắc chắn ta @Override và gọi đúng method.
Nguyên tắc của Dynamic Polymorphism
Thay đổi Method signature
Nếu ta sử dụng tính năng Overriding, method đã qua @Override phải có cùng phần signature (4 yếu tố đã liệt kê ở đầu bài viết).
Ta vẫn có thể thay đổi một số yếu tố của method signature (thay đổi số lượng parameter, thứ tự..) nhưng hành động này là Overloading, không phải Overriding.
package com.me.overriding;
class Parent_params {
void method(String a, String b, int v, float d) {
System.out.println("parent");
}
void method2(String a, String b, int v) {
System.out.println("parent");
}
void method3(String a, int v) {
System.out.println("parent");
}
}
class Child_Params extends Parent_params {
// Nếu muốn @Override method trong class cha, method trong class con cần có method signature
//giống với class cha (tên method, return type, số lượng parameter, thứ tự parameter)
//Như thế này là sai:
/*@Override
void method(String a){
System.out.println("child");
}*/
//Như thế này cũng sai vì sai thứ tự param:
//WRONG
/*@Override
void method(String a,float d,int a,String b){
System.out.println("parent");
}*/
// như thế này là đúng :
@Override
void method(String a, String b, int v, float d) {
System.out.println("child");
}
// Còn như thế này sẽ là Overloading, không phải @Overriding
//We can define a method like this
void method(String a, String b, int v) {
System.out.println("child");
}
void method2(String a, int b, String v) {
System.out.println("child");
}
void method3(int v, String a) {
System.out.println("child");
}
}
public class Override_Params {
public static void main(String[] args) {
String temp = "Monday";
Child_Params child_Params = new Child_Params();
/*Parent_params params=new Child_Params();
child_Params=(Child_Params) params;
child_Params.method(temp,temp,2);*/
Parent_params params2 = new Parent_params();
child_Params = (Child_Params) params2;
child_Params.method(temp, temp, 2);
}
}
Return type của method
Return type của method
package com.me.overriding;
class Parent1 {
public void method(String string) {
System.out.println("Parent :" + string);
}
public void method2(String string) {
System.out.println("Parent :" + string);
}
}
class Child1 extends Parent1 {
//The return type is incompatible with Parent1.method(String)
@Override
public int method(String string) {
System.out.println("Child :" + string);
}
}
public class OverrideAllMethods {
public static void main(String[] args) {
Child1 child1 = new Child1();
child1.method("Me");
}
}
Co-Variant Return Type
Co-variant return nghĩa là return type của overriding method(method ở class con) có thể trở thành subtype của return type của overrided method (method ở class cha).
package com.me.overriding;
class ParentCustomClass {
public Object m1() {
System.out.println(" parent m1()");
return null;
}
public ParentCustomClass m2() {
System.out.println(" parent m2()");
return null;
}
}
class ChildCustomClass extends ParentCustomClass {
@Override
public String m1() {
System.out.println("child m1()");
return null;
}
@Override
public ChildCustomClass m2() {
System.out.println(" child m2()");
return null;
}
}
public class CoVarientTypes {
public static void main(String[] args) {
ParentCustomClass class1 = new ChildtCustomClass();
class1.m1();
class1.m2();
/*
phần khai báo method d1 với return type là R1 có thể thay thế bởi method có phần khai báo d2
và return type là R2 nếu như thỏa mãn các điều kiện sau:
- Nếu R1 là void thì R2 phải là void
- Nếu R1 là primitive type thì R2 phải giống với R1
- Nếu R1 là reference type thì: R1 là subtype của R2 hoặc R1 có thể được convert ra subtype của R2
bởi unchecked conversion
*/
}
}
Static method Overriding (hoặc) Method binding
package com.me.overriding;
class ParentStatic {
public static void method(String string) {
System.out.println("Parent :" + string);
}
public void method2(String string) {
System.out.println("Parent :" + string);
}
}
class ChildStatic extends ParentStatic {
public static void method(String string) {
System.out.println("Child :" + string);
}
}
public class StaticMethodOverriding {
public static void main(String[] args) {
/*
1. Với static method (class method) thì method tương ứng với kiểu reference sẽ được gọi.
Lời gọi hàm được quyết định tại compile time.
2. Với non-static method (instance method), method được gọi tương ứng với kiểu object
chứ không phải kiểu reference. Trong trường hợp này, lời gọi hàm được quyết định tại runtime.
*/
ChildStatic.method("Me");
ParentStatic parentStatic = new ChildStatic();
parentStatic.method("Me");
}
}
Static variable binding
package com.me.overriding;
class Dad {
private static final String me = "dad";
protected String getMe() {
return me;
}
public void printMe() {
System.out.println(getMe());
}
}
class Son extends Dad {
private static final String me = "son";
@Override
protected String getMe() {
return me;
}
}
class StaticVariableOverriding {
public static void main(String[] args) {
Dad dad = new Son();
dad.printMe();
}
}
Final & Private Method
package com.me.overriding;
class Parent_2 {
public final void m1() {
System.out.println("m1()");
}
private void m2() {
System.out.println("m2() parent");
}
}
//Không thể Override final method từ class Parent_2
class Child_2 extends Parent_2 {
public final void m1() {
System.out.println("m1()");
}
private void m2() {
System.out.println("m2() of child");
}
}
public class FinalandprivateOverriden {
public static void main(String[] args) {
Parent_2 child_2 = new Child_2();
child_2.m1();
//object child_2 không thể sử dụng được method m2() từ class Parent_2
child_2.m2();
}
}
Override Access Levels (mức truy nhập)
package com.me.overriding;
class Parent_Access {
protected void method(String a, String b, int v, float d) {
System.out.println("parent");
}
}
class Child_Access extends Parent_Access {
//Access levels cannot be restrictive
/*private void method(String a,String b,int v,float d){
System.out.println("parent");
}*/
public void method(String a, String b, int v, float d) {
System.out.println("parent");
}
}
public class Override_AccessLevels {
public static void main(String[] args) {
}
}
Override với method super()
package com.me.overriding;
class SuperParent {
public void m1() {
System.out.println(" super m1()");
}
protected void m2() {
System.out.println(" super m2()");
}
private void m3() {
System.out.println(" super m3()");
}
}
class SuperChild extends SuperParent {
public void m1() {
super.m1();
super.m2();
System.out.println("m1()");
}
protected void m2() {
System.out.println("m2()");
}
private void m3() {
System.out.println(" m3()");
}
}
public class OverridingWithSuper {
public static void main(String[] args) {
SuperParent parent = new SuperChild();
parent.m1();
parent.m2();
}
}
@Override với Abstraction
package com.me.overriding;
interface Interface {
public void m1();
}
/*
Nếu một abstract class implement interface này
thì nó không nhất thiết phải Override method move()
*/
abstract class AbstractClass implements Interface {
public abstract void m2();
public void m3() {
System.out.println("m3()");
}
}
/*
Tuy nhiên một concrete class (non-abstract) có implement một abstract class hoặc interface
thì nó phải override tất cả method từ abstract class hoặc interface kia.
*/
class ConcreteClass extends AbstractClass {
@Override
public void m1() {
// TODO Auto-generated method stub
System.out.println("m1()");
}
@Override
public void m2() {
// TODO Auto-generated method stub
System.out.println("m2()");
}
@Override
public void m3() {
// TODO Auto-generated method stub
System.out.println("m3()");
}
}
public class OverridingWithAbstraction {
public static void main(String[] args) {
ConcreteClass class1 = new ConcreteClass();
class1.m1();
class1.m2();
class1.m3();
}
}
@Override với Exceptions
package com.me.overriding;
import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
class ExceptionParent {
public void m1() throws IOException {
System.out.println("m1()");
}
}
//Exception Exception is not compatible with throws clause in ExceptionParent.m1()
//It should be sam or narrowed or unchecked exception
//CE
/*class ExceptionChild extends ExceptionParent{
public void m1() throws Exception{
System.out.println("m1()");
}*/
//CE
//IllegalArgumentException is Un-checked
/*class ExceptionChild extends ExceptionParent{
public void m1() throws Exception,IllegalArgumentException{
System.out.println("m1()");
}*/
//OK bccz of Un-Checked Exception
/*class ExceptionChild extends ExceptionParent{
public void m1() throws IllegalArgumentException{
System.out.println("m1()");
}*/
/*//CE bcz of Checked
class ExceptionChild extends ExceptionParent{
public void m1() throws IOException,InterruptedException{
System.out.println("m1()");
}*/
class ExceptionChild extends ExceptionParent {
public void m1() throws IOException {
System.out.println("m1()");
}
}
public class OverridingExceptions {
public static void main(String[] args) throws IOException {
ExceptionParent exceptionParent = new ExceptionChild();
exceptionParent.m1();
}
}
@Override từ Inner Private Class
package com.me.overriding;
public class OverrideprivateInnerclass {
private String msg = "GeeksforGeeks";
private void fun() {
System.out.println("Outer fun()");
}
class Inner extends OverrideprivateInnerclass {
private void fun() {
System.out.println("Accessing Private Member of Outer: " + msg);
}
}
public static void main(String args[]) {
// để tạo instance của Inner class, ta cần instance của outer class...
// instance có thể dịch ra là "bản thể"
OverrideprivateInnerclass o = new OverrideprivateInnerclass();
Inner i = o.new Inner();
// dòng code dưới đây gọi hàm fun() của instance thuộc Inner class nhằm chứng minh
// các thành phần private của Outer class có thể được truy cập thông qua instance của Inner class
i.fun();
// dòng code dưới đây gọi hàm fun() của instance thuộc outer class
// trường hợp này không xảy ra run-time polymorphism
o = i;
o.fun();
}
}
Overriding và Overloading
package com.me.overriding;
class ParentStatic_1 {
public void method(String string) {
System.out.println("Parent :" + string);
}
public void method2(String string) {
System.out.println("Parent :" + string);
}
}
class ChildStatic_1 extends ParentStatic_1 {
public static void method(String string, int b) {
System.out.println("Child :" + string);
}
public static void method2(String string, int b, float c) {
System.out.println("Child :" + string);
}
}
public class OverrideAndOverloadMethods {
public static void main(String[] args) {
// trong subclass ( hoặc derived class), ta có thể overload các method được kế thừa từ superclass
// các method được overload này là method mới, không liên quan gì tới method trong superclass
ParentStatic_1 parentStatic = new ChildStatic_1();
parentStatic.method("Me");
}
}
Multi-Level Override (Override đa cấp)
package com.me.overriding;
class ParentStatic_2 {
public void method(String string) {
System.out.println("Parent :" + string);
}
public void method2(String string) {
System.out.println("Parent :" + string);
}
}
class ChildStatic_2 extends ParentStatic_2 {
public void method(String string) {
System.out.println("Child -1 :" + string);
}
}
class ChildStatic_3 extends ChildStatic_2 {
public void method(String string) {
System.out.println("Child -2 :" + string);
}
}
class ChildStatic_4 extends ChildStatic_3 {
public void method(String string) {
System.out.println("Child -3 :" + string);
}
}
public class MultilevelOverriding {
public static void main(String[] args) {
//We no need to overwrite all the methods that are there in super class.
ParentStatic_2 parentStatic_2 = new ChildStatic_3();
parentStatic_2.method("hey........!");
}
}
Instance vs static method override
package com.me.overriding;
class ParentStatic2 {
public static void method(String string) {
System.out.println("Parent :" + string);
}
public void method2(String string) {
System.out.println("Parent :" + string);
}
}
class ChildStatic2 extends ParentStatic {
public void method(String string) {
System.out.println("Child :" + string);
}
//This static method cannot hide the instance method from ParentStatic
public static void method2(String string) {
System.out.println("Child :" + string);
}
}
public class InstanceVsStaticOverriding {
public static void main(String[] args) {
// Một instance method không thể override static method
// một static method không thể "làm ẩn" đi instance method
ParentStatic2 parentStatic = new ChildStatic2();
parentStatic.method("Me");
}
}
Instance vs static variable Override
package com.me.overriding;
class B
{
int a=10;
public void print()
{
System.out.println("inside B super class");
}
}
class C extends B
{
int a=20;
public void print()
{
System.out.println("inside C sub class");
}
}
public class InstanceVariableOverriding {
public static void main(String[] args) {
B b=new C();
b.print();//it will print inside c sub class
System.out.println(b.a);//it will print super class variable value=10
/*
Trong Java ta KHÔNG THỂ override instance variable
*/
}
}
Constructor Override
package com.me.overriding;
class One {
public One() { // Super Class constructor
}
One(int a) { // Super Class Constructor Overloading
}
}
class Two extends One {
/* {
//One() { // this is a method not constructor
// because name should not match with Class name
}*/
Two() { // sub class constructor
}
Two(int b) { // sub class constructor overloading
}
}
public class ConstructorOverriding {
/*
Trong Java, ta KHÔNG THỂ override constructor. Lý do:
- Constructor nhìn có vẻ giống method nhưng tên của nó lại là tên của class
và nó không có return value.
- Trong khi đó, Override một method nghĩa là chúng ta cần giữ nguyên
method signature từ class cha (chắc chắc class cha phải khác tên class con nên
constructor của class con sẽ khác tên class cha, điều này vi phạm nguyên tắc Override)
*/
}
Constructor với mehtod super()
package com.me.overriding;
class Superclass {
public Superclass(int x) {
System.out.println("000000");
}
public Superclass() {
System.out.println(" ty");
}
public Superclass(String y) {}
}
class Subclass extends Superclass {
public Subclass() {
// super(5); // chain to Superclass(int) constructor
}
}
public class ConstructorWithSuper {
public static void main(String[] args) {
new Subclass();
}
}
Override trong cùng package và khác package
package com.me.overridingtest;
import com.me.overriding.PackageParent;
public class Child extends PackageParent {
@Override
public void m1() {
System.out.println("m1()");
}
@Override
protected void m2() {
System.out.println("m2()");
}
//Defalut methods are allowed only ininterface
/*default void m3(){
System.out.println("m3()");
}*/
// class con thuộc package khác chỉ có thể override non-final method được khai báo với từ khóa
// public hoặc protected
@Override
private void m4() {
System.out.println("m1()");
}
}
package com.me.overriding;
import com.me.overridingtest.Parent;
public class OtherPackageOverride {
public static void main(String[] args) {
Parent parent = new com.me.overridingtest.Child();
parent.m1();
parent.m2();
}
}
package com.me.overriding;
public class PackageParent {
public void m1() {
System.out.println("m1()");
}
protected void m2() {
System.out.println("m2()");
}
//Defalut methods are allowed only ininterface
/*default void m3(){
System.out.println("m3()");
}*/
private void m4() {
System.out.println("m1()");
}
}
Instance method được ưa chuộng hơn Interface default method
package com.me.overriding;
class Horse1 {
public String identifyMyself() {
return "I am a horse.";
}
}
interface Flyer {
default public String identifyMyself() {
return "I am able to fly.";
}
}
interface Mythical {
default public String identifyMyself() {
return "I am a mythical creature.";
}
}
public class Pegasus extends Horse1 implements Flyer, Mythical {
public static void main(String...args) {
Pegasus myApp = new Pegasus();
System.out.println(myApp.identifyMyself());
}
}
//The method Pegasus.identifyMyself returns the string I am a horse.
Techmaster via Dzone
Bình luận
rất hữu ích nhưng có 2 3 cái chưa hiểu lắm