Perkenalan Kelas Object

Mengenal Object sebagai leluhur semua kelas beserta metode toString dan equals bawaannya.

Daftar isi

Di balik setiap kelas Java, termasuk kelas yang ditulis sendiri, terdapat satu leluhur yang sama, yaitu kelas java.lang.Object. Setiap kelas mewarisinya secara implisit tanpa perlu pernyataan apa pun, sehingga perilaku dasar seperti mencetak diri dan membandingkan diri tersedia sejak objek pertama dibuat. Topik ini mengenal Object sebagai wadah universal untuk objek apa pun, mengurai pola toString bawaan, dan membuktikan bahwa equals bawaan membandingkan referensi, bukan isi.

Object sebagai leluhur semua kelas#

Kelas yang tidak menuliskan extends tetap mewarisi Object, dan sebab itulah metode seperti toString(), equals(), dan getClass() bisa dipanggil atas objek apa pun tanpa mendeklarasikannya. Konsekuensi lain bersifat praktis, yaitu variabel bertipe Object dapat menampung referensi menuju objek dari kelas mana pun. Wadah universal seperti ini dipakai luas di perpustakaan standar pada masa sebelum generics berkembang.

java UjiObject.java
public class UjiObject {

    public static void main(String[] args) {
        Object wadah = new Buku("Peta Jalur Pendakian", 240);
        System.out.println("Tipe asli : " + wadah.getClass().getSimpleName());
        Buku buku = (Buku) wadah;
        System.out.println("Judul     : " + buku.judul);
        String teksDefault = String.valueOf(wadah);
        System.out.println("Pola toString default (diawali Buku@) : " + teksDefault.startsWith("Buku@"));
    }
}

class Buku {
    String judul;
    int jumlahHalaman;

    Buku(String judul, int jumlahHalaman) {
        this.judul = judul;
        this.jumlahHalaman = jumlahHalaman;
    }
}
text Keluaran
Tipe asli : Buku
Judul     : Peta Jalur Pendakian
Pola toString default (diawali Buku@) : true

Variabel wadah bertipe Object, tetapi isinya tetap objek Buku, dan metode getClass() yang diwarisi dari Object membuktikannya dengan mengembalikan informasi tipe aslinya. Karena kompilator hanya melihat tipe variabel, penggunaan kembali objek tersebut sebagai Buku menuntut casting, yaitu penulisan (Buku) secara eksplisit sebelum anggota khusus Buku dapat diakses. Baris terakhir memeriksa teks hasil toString bawaan, dan pola yang ditemukannya diuraikan berikut ini.

toString dan pola bawaannya#

Metode toString bawaan menyusun teks dari nama kelas, tanda @, dan angka heksadesimal yang berasal dari hash code identitas objek, sehingga bentuknya seperti Buku@1b6d3586. Angka heksadesimal itu berbeda pada setiap eksekusi karena berasal dari alamat identitas objek, bukan dari isinya. Justru karena itulah program di atas hanya memeriksa pola awalnya lewat startsWith alih-alih mencetak teks lengkapnya ke keluaran.

Representasi bawaan ini jarang dikehendaki dalam laporan maupun log, sehingga kelas biasanya menyediakan toString sendiri dengan teks yang bermakna dan stabil. Kelas Buku pada program berikut menimpa metode tersebut dengan menyusun isi fieldnya ke dalam satu baris, dan pemanggilan println(buku) otomatis memakai versi yang baru.

java UjiToString.java
public class UjiToString {

    public static void main(String[] args) {
        Buku buku = new Buku("Peta Jalur Pendakian", 240);
        System.out.println(buku);
    }
}

class Buku {
    String judul;
    int jumlahHalaman;

    Buku(String judul, int jumlahHalaman) {
        this.judul = judul;
        this.jumlahHalaman = jumlahHalaman;
    }

    public String toString() {
        return "Buku{judul=" + judul + ", halaman=" + jumlahHalaman + "}";
    }
}
text Keluaran
Buku{judul=Peta Jalur Pendakian, halaman=240}

Keluaran kini dapat direproduksi berulang kali karena teksnya hanya menyusun field, tanpa bagian yang berubah-ubah. Kebiasaan menyediakan toString yang informatif membantu saat debugging, karena objek yang dicetak langsung memperlihatkan isinya, bukan nama kelas dengan angka yang tidak berhubungan dengan data apa pun.

equals bawaan membandingkan referensi#

Metode equals bawaan didefinisikan sama persis dengan operator ==, yaitu bernilai true hanya ketika kedua sisi menunjuk objek yang sama. Dua objek dengan isi field yang identik tetap dianggap berbeda karena keduanya lahir dari pemanggilan new yang terpisah. Program berikut mengukur perilaku itu langsung.

java UjiEquals.java
public class UjiEquals {

    public static void main(String[] args) {
        Buku bukuA = new Buku("Peta Jalur Pendakian", 240);
        Buku bukuB = new Buku("Peta Jalur Pendakian", 240);
        Buku bukuC = bukuA;
        System.out.println("bukuA.equals(bukuB) : " + bukuA.equals(bukuB));
        System.out.println("bukuA.equals(bukuC) : " + bukuA.equals(bukuC));
    }
}

class Buku {
    String judul;
    int jumlahHalaman;

    Buku(String judul, int jumlahHalaman) {
        this.judul = judul;
        this.jumlahHalaman = jumlahHalaman;
    }
}
text Keluaran
bukuA.equals(bukuB) : false
bukuA.equals(bukuC) : true

Percobaan pertama menghasilkan false karena bukuA dan bukuB menunjuk dua objek berbeda, sedangkan percobaan kedua menghasilkan true karena bukuC hanyalah salinan referensi dari bukuA. Isi field sama sekali tidak terlibat dalam kedua pembandingan tersebut. Bila kesetaraan isi yang dikehendaki, metode equals harus ditulis ulang agar membandingkan field yang relevan, pekerjaan yang diatur oleh kontrak resmi dalam spesifikasi bahasa dan wajib disertai penyesuaian hashCode. Untuk pembawa data, kelas record memberikan versi siap pakai dari perbandingan semacam itu.

Kesalahan umum#

  • Memakai == atau equals bawaan untuk membandingkan isi dua objek. Keduanya membandingkan referensi, sehingga hasilnya false untuk dua objek berbeda walau fieldnya identik. Tulis ulang equals atau gunakan kelas yang sudah menyediakannya.
  • Mengharapkan toString bawaan menampilkan isi objek. Teksnya berisi nama kelas dengan hash code identitas yang berubah setiap eksekusi. Sediakan toString sendiri bila teksnya akan dibaca manusia.
  • Mencetak hasil toString bawaan lalu menggunakannya sebagai penanda di log. Nilainya tidak stabil antarjalankan, sehingga pencocokan log menjadi menyesatkan. Log yang stabil memerlukan representasi yang menyertakan isi, bukan identitas.
  • Melakukan casting dari Object tanpa memastikan tipenya. Pemanggilan (Buku) wadah atas objek yang bukan Buku memunculkan ClassCastException saat program berjalan. Periksa dengan instanceof terlebih dahulu bila tipenya tidak pasti.

Ringkasan#

  • Object adalah leluhur semua kelas Java, diwarisi secara implisit, dan menjadi sumber metode dasar seperti toString, equals, dan getClass.
  • Variabel bertipe Object dapat menampung objek apa pun, dengan pembayaran berupa casting kembali ke tipe asli sebelum anggota khususnya dapat dipakai.
  • toString bawaan menghasilkan nama kelas, @, dan hash code identitas, sehingga kelas biasanya menyediakan versi sendiri yang stabil dan informatif.
  • equals bawaan setara dengan ==, yaitu membandingkan referensi, sedangkan perbandingan isi menuntut penulisan ulang yang mengikuti kontrak beserta hashCode.

Referensi#