Menambahkan member baru pada interface yang sudah lama beredar memaksa setiap class implementasi menulis kode untuk member itu, termasuk class yang tidak membutuhkannya. Pada aplikasi loyalitas pelanggan, misalnya, kontrak IPelanggan ingin diperkaya method sapaan tanpa merusak ratusan class yang sudah mengimplementasikannya. Sejak C# 8, interface dapat membawa default implementation: member interface yang sudah berisi badan method. Class implementasi bebas memakai versi bawaan tersebut atau menggantinya dengan versi sendiri.

Deklarasi Default Member #

Default member ditulis seperti method biasa di dalam interface, lengkap dengan badan dan diakhiri titik koma di deklarasi property terpisah. Property yang dirujuk di dalam badan tetap harus dideklarasikan sebagai anggota kontrak, karena default implementation hanya berlaku untuk member yang punya badan. Fitur ini tersedia sejak C# 8.0, dan interface dengan default member kini mendekati kemampuan abstract class, yaitu menyimpan kode yang bisa dipakai langsung oleh implementasi.

Contoh: Sapaan Pelanggan #

csharp
public interface IPelanggan
{
    string Nama { get; }

    string Sapaan() => $"Halo, {Nama}!";
}

public class PelangganReguler : IPelanggan
{
    public string Nama { get; }

    public PelangganReguler(string nama)
    {
        Nama = nama;
    }
}

public class PelangganVip : IPelanggan
{
    public string Nama { get; }

    public PelangganVip(string nama)
    {
        Nama = nama;
    }

    public string Sapaan()
    {
        return $"Selamat datang, {Nama}!";
    }
}

IPelanggan reguler = new PelangganReguler("Sinta");
Console.WriteLine(reguler.Sapaan());

IPelanggan vip = new PelangganVip("Bunga");
Console.WriteLine(vip.Sapaan());

Output yang Dihasilkan:

text
Halo, Sinta!
Selamat datang, Bunga!

Mari kita bedah baris demi baris:

  1. string Sapaan() => $"Halo, {Nama}!": Interface menyediakan badan bawaan yang memakai property Nama milik kontraknya sendiri; class PelangganReguler tidak menulis apa pun untuk method ini.
  2. class PelangganReguler : IPelanggan: Class ini hanya mengisi property Nama; kompilasi tetap lulus karena method Sapaan() sudah terpenuhi oleh versi bawaan.
  3. public string Sapaan() pada PelangganVip: Class VIP memilih mengganti versi bawaan, dan implementasi class tersebut otomatis menang atas versi interface.
  4. reguler.Sapaan(): Pemanggilan melalui reference interface menjalankan versi bawaan karena PelangganReguler tidak menyediakan pengganti, menghasilkan baris pertama output.
  5. vip.Sapaan(): Pemanggilan dengan bentuk sama menjalankan versi PelangganVip, membuktikan default member tetap mengikuti pola polymorphism.

Batasan Pemanggilan #

Default member hanya dapat dipanggil melalui variable yang bertipe interface-nya. Reference bertipe PelangganReguler tidak melihat method Sapaan(), karena member bawaan bukan bagian permukaan class; untuk memanggilnya, kita menampung object dalam variable IPelanggan terlebih dahulu. Batasan ini sejalan dengan explicit implementation pada bab sebelumnya: member milik interface hidup di dunia interface.

Dengan default implementation, interface kini mendukung evolusi bertahap. Bab berikutnya membahas covariance dan contravariance, aturan konversi antar tipe generic yang berhubungan melalui pewarisan.