[JAVA] @Overriding và tính đa hình

  • 20/04/2017
  • Bởi
  • trong Java

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.

Tham khảo các khóa học lập trình online, onlab, và thực tập lập trình tại TechMaster

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

Java và Cái máy giặt Java và Cái máy giặt Techmaster team Blog Home Nhập môn lập trình Java (Phần 7: Thực thi điều kiện) Nhập môn lập trình Java (Phần 7: Thực thi điều kiện) Techmaster team