Skip to content

Classes, Superclasses, and Subclasses

Info

Semua file yang ada pada contoh pada catatan ini ada pada package Ch5/J_5_1_Classes_Superclasses_Subclasses

Mari kita kembali pada class Employee yang telah kita buat pada contoh sebelumnya. Katakan di sebuah perusahaan ada juga Manager. Jadi Manager adalah seorang Employee yang memiliki tunjangan bonus tambahan. Maka kita harus membuat sebuah Class Manager yang memiliki state dan behavior yang sama persis dengan Employee namun dengan tambahan adanya perhitungan bonus. Disinilah inheritance memainkan perannya. Manager adalah Employee, maka kita dapat menggunakan hubungan is-a, class Manager is-a class Employee.

Defining Subclasses

Dibawah ini adalah bagaimana kita mendefinisikan Manager class yang merupakan turunan dari Employee

Code

class Manager extends Employee {
    // Another method here
}

Kata kunci extends meeng indikasi bahwa kita membuat sebuah class baru yang menurunkan sebuah class yang sudah ada. Class yang sudah ada disebut dengan super class, base class atau parent class sedangkan class yang baru bisa disebut subclass, derived class atau child class. Satu class hanya bisa menurunkan (extend) satu buah class.

Class Employee adalah superclass, namun bukan berarti class tersebut superior atau memiliki fungsi yang lebih banyak sehingga dikatakan superclass. Justru kebalikannya. Subclass memliki fungsi yang lebih banyak dibandingka superclass-nya. Contoh, Manager memiliki data dan fungsi yang lebih banyak dibandingkan dengan superclass Employee.

Class Manager memiliki field baru bonus dan method baru untuk menyetel nilai pada field tersebut.

Code

class Manager extends Employee {

    ... // cutted
    BigDecimal bonus;

    public void setBonus(BigDecimal bonus){
        this.bonus = bonus;
    }

    // Another method here
}

Dari hasil diatas, jika kita memiliki objek Manager kita dapat memasang nilai bonus dengan memanggil method seperti dibawah ini.

Code

manager.setBonus(1500000);

Akan tetapi, jika kita memiliki object Employee, kita tidak dapat menggunakan method setBonus karena method tersebut tidak dimiliki oleh class Employee. Namun sebaliknya, pada subclass, yaitu class Manager kita dapat menggunakan semua method yang memiliki Accsess Modifier public, atau protected seperti method getName, getHireDay yang dimiliki oleh class Employee. Walaupun objek tersebut tidak secera eksplisit di definisikan pada class Manager, method tersbut akan secara otomatis diturunkan dari Superclass Employee

Semua object Manager memiliki empat fields, name, hireDay, salary, dan bonus. Semua field selain bonus diambil dari Superclass Employee.

Note

Kita telah mencoba membuat class menggunakan record. Class yang dibangung menggunakan record tidak dapat menjadi Superclass atau Subclass dari class lain.

Overriding Methods

Beberapa method dari superclass Manager tidak cocok dengan subclass Employee. Salah satunya adalah getSalary(). Method tersebut seharunsya mengembalikan gaji pokok di tambah dengan bonus. Kita harus membuat method baru yang meng-override superclass method.

Logika sangat mudah, yaitu dengan mengembalikan field salary ditambah dengan field bonus.

Code

public BigDecimal getSalary() {
    return this.bonus.add(this.salary); // error
}

Kode diatas tidak akan berhasil. Perlu diingat, hanya method dari Employee yang memiliki akses langsung ke private fields dari class Employee. Artinya method getSalary() dari class Manager tidak memiliki akses langsung untuk mengambil nilai dari field salary. Jika method dari class Manager ingin mengakses private field salary, maka kita harus menggunakan apa yang digunakan oleh method dari class lain, yaitu menggunakan public method, yaitu yang telah kita buat methodnya di class Employee, yaitu getSalary().

Code

public BigDecimal getSalary() {
    return this.bonus.add(getSalary()); // error
}

Namun masalahnya, fungsi getSalary() yang kita panggil memiliki nama method yang sama dengan nama method yang sedang kita implementasi. Kode diatas akan menyebabkan perulangan berantai akibat kita memanggil method itu sendiri didalam dirinya yang menyebabkan program akan berhenti.

Karena itu, kita harus mengindikasi atau memberitahu compiler bahwa kita ingin mengambil method getSalary() dari superclass Employee, bukan dari class saat ini. Kita dapat menggunakan keyword super untuk tujuan tersebut.

Code

@Override
public BigDecimal getSalary() {
    return super.getSalary().add(this.getBonus());
}

Anotasi Override opsional, anotasi tersebut digunakan oleh compiler untuk menunjukan bahwa class tersebut meniban method dari superclass. Compiler akan mengeluarkan error jika setidaknya kondisi dibawah terpenuhi;

  1. Method meniban atau mengimplementasi deklarasi method
  2. Method memiliki signature yang sama dengan yang dideklarasi pada superclass

Note

Beberapa orang berpikir bahwa super dianalogikan dengan this reference. Namun analogi tersebut kurang tepat. super bukan merujuk ke sebuah object. Akan tetapi ia adalah special keyword yang mengarahkan compiler untuk meminta superclass method.

Subclass Constructor

Sekarang kita buat constructor pada class Manager untuk membuat object.

Code

public Manager(String name, BigDecimal salary, LocalDate hireDay){
    super(name, salary,hireDay);
    this.bonus = new BigDecimal(0);
}

Dibawah ini adalah full kode dari penggalan kode diatas

Code

Kode dibawah ini berada pada Package Ch5/J_5_1_Classes_Superclasses_Subclasses/Example1

package Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1;

import java.math.BigDecimal;
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        var manager1 = new Manager("Farras",new BigDecimal(13000000), LocalDate.of(2023,6,5));
        manager1.setBonus(new BigDecimal(2000000));
        System.out.println(manager1);System.out.println();

        var employee = new Employee("Fulan 2", new BigDecimal(5900000), LocalDate.of(2017,7,3));
        System.out.println(employee);
    }
}
Output
Name            : Farras
Salary & Bonus  : 15,000,000.00
Hire Day        : 2023-06-05

Name            : Fulan 2
Salary          : 5,900,000.00
Hire Day        : 2017-07-03
package Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1;

import java.math.BigDecimal;
import java.time.LocalDate;

public class Manager extends Employee{
    private BigDecimal bonus;

    public BigDecimal getBonus() {
        return bonus;
    }

    public Manager(String name, BigDecimal salary, LocalDate hireDay){
        super(name, salary,hireDay);
        this.bonus = new BigDecimal(0);
    }

    public void setBonus(BigDecimal bonus) {
        this.bonus = bonus;
    }

    @Override
    public BigDecimal getSalary() {
        return super.getSalary().add(this.getBonus());
    }

    @Override
    public String toString() {
        return super.toString().replace("Salary\t\t","Salary & Bonus");
    }
}
package Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1;

import java.math.BigDecimal;
import java.time.LocalDate;

public class Employee{
    // instance
    private String name;
    private BigDecimal salary;

    private LocalDate hireDay;

    public Employee(String name, BigDecimal salary, LocalDate hireDay){
        this.name = name;
        this.salary = salary;
        this.hireDay = hireDay;
    }

    public String getName() {
        return name;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public LocalDate getHireDay() {
        return hireDay;
    }
    @Override
    public String toString() {
        return "Name\t\t\t: %s\nSalary\t\t\t: %,.2f\nHire Day\t\t: %s".
                formatted(this.getName(),this.getSalary(),this.getHireDay().toString());
    }
}

Preventing Inheritance: Final Classes and Methods

Class yang tidak dapat diturunkan (extend) disebut dengan final class dan kita harus menggunakan final modifier saat mendefinisikan class untuk mengindikasi bahwa class ini adalah final class. Katakan class Executive tidak bisa lagi diturunkan karena tidak ada yang lebih tinggi dari eksekutif.

Code

public final class Executive extends Manager{
    // method here
}

Kita juga dapat membat method tertentu pada sebuah class menjadi final. Jika kita melakukan itu, maka tidak ada subclass yang dapat meng-override method tersebut.

Note

Semua method didalam final class secara otomatis menjadi final method. Ingat juga bahwa field juga dapat dijadikan final field dimana field tersebut tidak akan pernah berubah lagi semenjak objek class tersebut telah terbuat. Namun class yang di set sebagai final hanya secara otomatis menjadi semua method final, namun fields tidak.

Code

public class Manager extends Employee {
    ...
    public final String getName(){
        return this.name;
    }
}

Casting

Konversi dari satu tipe data ke tipe data lain disebut dengan Casting. Java programming langguage memmiliki special notasi untuk casting.

Code

double pecahan = 4d;
int satuan = (int)pecahan;

Konversi nilai dari pecahan kedalam integer akan menghilangkan fraksi dari pecahan.

Mari sekarang kita lihat casting pada object reference. Mari kita gunakan lagi superclass Employee dan subclass Manager.

Code

package Ch5.J_5_1_Classes_Superclasses_Subclasses;

import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Employee;
import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Manager;

import java.math.BigDecimal;
import java.time.LocalDate;

public class J_1_Casting_Object_Reference {
    public static void main(String[] args) {
        Employee[] employees = new Employee[3];

        employees[0] = new Manager("farras",new BigDecimal(13000000), LocalDate.now());
        employees[1] = new Employee("Fulan2",new BigDecimal(5000000), LocalDate.now());
        employees[2] = new Employee("Fulan3",new BigDecimal(7500000), LocalDate.now());

        for (Employee data : employees){

            if (data instanceof Manager){ //(1)!
                Manager boss = (Manager) data;
                boss.setBonus(new BigDecimal(4500000));
                System.out.println(boss);
            }
            else {
                System.out.println(data);
            }
            System.out.println();
        }
    }
}
  1. Semenjak Java 16 kita dapat melakuakn seperti ini
    if (data instanceof Manager boss){ //(1)!
            boss.setBonus(new BigDecimal(4500000));
            System.out.println(boss);
        }
    
Output
Name            : farras
Salary & Bonus  : 17,500,000.00
Hire Day        : 2023-08-19

Name            : Fulan2
Salary          : 5,000,000.00
Hire Day        : 2023-08-19

Name            : Fulan3
Salary          : 7,500,000.00
Hire Day        : 2023-08-19

Penjelasan dari kode diatas sbb;

  1. Kita membuat array yang menampung object variabel yang berisikan object reference ke Class Employee
  2. Saat inisiasi data, ada satu yang diisi dengan objects reference Manager. Ini sah-sah saja, karena tidak semua Employee adalah karyawan, karena ada juga yang menjabat sebagai Manager. Ini disebut dengan Promosing less.
  3. Kita me-looping vairable employess dan melakukan pengecheckan apakah element dari Array yang berisikan object tersebut instance dari class Manager. Jika ia maka kita meng-casting element tersebut kedalam type Manager. Selanjutnya kita memanggil method setBonus() dan menampilkan toString() dari object Manager.
  4. Jika element bukan instace dari maka kita langsung menampilkan toString() dari object Employee.

Masih membahas tentang casting objects reference. Compiler melakukan pengujian apakah kita memberikan tipe yang lebih tinggi saat menyimpan nilai pada sebuah variable. Ada dua jenis, promosing less dan promosing more;

  • Jika kita memberikan subclass reference pada superclass variable maka kita melakukan prmissing less, dan compiler akan menganggap itu sah-sah saja (Sebagaimana cotoh diatas kita memberikan object reference subclass Manager pada variable superclass Employee).

  • Jika kita memberikan superclass reference pada sublcass variable maka kita melakukan promosing more. Jika demikian kita harus menggunakan cast sehingga promise daat di check saat runtime oleh compiler.

Code

package Ch5.J_5_1_Classes_Superclasses_Subclasses;

import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Employee;
import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Manager;

import java.math.BigDecimal;
import java.time.LocalDate;

public class J_2_Casting_Promosing_More_and_Less {
    public static void main(String[] args) {

        // Promosing less
        Employee realManager = new Manager("farras",new BigDecimal(13000000), LocalDate.now());

        // Promosing More
        Manager fakeManager = (Manager) new Employee("farras",new BigDecimal(13000000), LocalDate.now());
    }
}

Lalu bagaimana jika kita melakukan cast down pada rantai inheritance. Misalkan object reference di cast ke class Manager.

Code

package Ch5.J_5_1_Classes_Superclasses_Subclasses;

import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Employee;
import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Manager;

import java.math.BigDecimal;
import java.time.LocalDate;

public class J_3_Casting_to_Subclass {
    public static void main(String[] args) {

        try {
            // will raise ClassCastException
            Employee karyawan = new Employee("Fulan2",new BigDecimal(5000000), LocalDate.now());
            Manager manager = (Manager) karyawan;
        }
        catch (ClassCastException ex){
            System.out.println(ex.getLocalizedMessage());
        }

    }
}
Output
class Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Employee cannot be cast to class Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Manager

jika kita tidak menangkap exception catch, program kita akan berhenti. Maka dari itu, praktik yang baik kita kita mencari tahu dahulu apakah cast akan berhasil sebelum kita melakukan casting. Kita dapat menggunakan apa yang telah kita gunakan pada conoht diatas, yaitu menggunakan instaceof

Code

package Ch5.J_5_1_Classes_Superclasses_Subclasses;

import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Employee;
import Ch5.J_5_1_Classes_Superclasses_Subclasses.Example1.Manager;

import java.math.BigDecimal;
import java.time.LocalDate;

public class J_4_Casting_Save_With_Instanceof {
    public static void main(String[] args) {

        Employee karyawan = new Employee("Fulan2",new BigDecimal(5000000), LocalDate.now());

        if(karyawan instanceof Manager) {
            Manager manager = (Manager) karyawan;
        }else {
            System.out.println("Karyawan bukan isntace dari manager");
        }

    }
}
Output
Karyawan bukan isntace dari manager

Access Modifeir

Berbicara tentang akses field dan method dari sebuah class, dibawha ini adalah empat akses kontrol yang disediakan olhe Java

  1. Accessible in the class only (private).
  2. Accessible by the world (public).
  3. Accessible in the package and all subclasses (protected).
  4. Accessible in the package—the (unfortunate) default. No modifiers are needed.