Fungsi & Method di Java

12 min readFundamental
JavaMethodFungsiBackend

Di bahasa pemrograman, semua logika bisnis kita ditulis di dalam blok kode. Agar blok kode itu bisa dipakai berulang kali — bukan ditulis ulang setiap saat — Java memberi kita method (kadang disebut fungsi). Menguasai method berarti menguasai cara memecah masalah besar menjadi potongan-potongan kecil yang rapi, bisa diuji, dan mudah dibaca. Artikel ini membahasnya dari dasar sampai yang paling sering dipakai sehari-hari di backend.

Apa Itu Method?

Method adalah blok kode yang diberi nama, menerima input (parameter), dan bisa dipanggil berkali-kali. Setiap kali dipanggil, method menjalankan instruksinya lalu (opsional) mengembalikan hasil.

public class Greeting {
    public String sapa(String nama) {
        return "Halo, " + nama + "!";
    }
}

Bayangkan method seperti resep masakan: ditulis sekali di buku, lalu bisa diikuti setiap kali Anda mau memasak hidangan yang sama. Tanpa method, kode Anda akan penuh duplikasi — satu perubahan kecil berarti mengubah puluhan tempat.

Konsep kunci: method = kode yang diberi nama, bisa menerima input, dan bisa dipanggil ulang. Method adalah unit terkecil dari logika yang bisa diuji dan dirawat.

Method Static vs Method Instance

Method Java dibagi dua berdasarkan cara memanggilnya:

JenisKata kunciDipanggil lewatKapan dipakai
Instance methodTanpa staticObjek (obj.method())Butuh membaca/mengubah state objek
Static methodstaticNama class (Math.max(...))Utilitas, tidak butuh state objek
public class Kalkulator {
    // static: cukup dipanggil lewat nama class
    public static int tambah(int a, int b) {
        return a + b;
    }

    // instance: butuh objek, bisa menyimpan state
    private int total = 0;

    public void akumulasi(int nilai) {
        this.total += nilai;
    }

    public int getTotal() {
        return total;
    }
}

int hasil = Kalkulator.tambah(2, 3);        // static → lewat nama class
Kalkulator k = new Kalkulator();
k.akumulasi(10);                             // instance → lewat objek

Aturan praktis backend: method yang tidak membaca atau mengubah data objek — misal formatTanggal() — sebaiknya dibuat static; method yang bekerja dengan state objek adalah instance method. Detailnya di dokumentasi tentang variabel class.

Anatomi Deklarasi Method

Deklarasi method di Java terdiri dari beberapa bagian yang masing-masing punya peran:

public    static   int    hitungDiskon   (double harga, int persen)
│         │        │      │              └──── parameter (input)
│         │        │      └──── nama method (kata kerja)
│         │        └──── return type (tipe hasil yang dikembalikan)
│         └──── modifier opsional lain
└──── access modifier (hak akses)
BagianContohPenjelasan
Access modifierpublic, private, protectedSiapa saja yang boleh memanggil method ini
Modifier lainstatic, finalPerilaku tambahan (misal bisa dipanggil tanpa objek)
Return typeint, String, voidTipe nilai yang dikembalikan, atau void kalau tidak ada
Nama methodhitungDiskonKata kerja yang menggambarkan aksi method
Parameter(double harga, int persen)Input yang diterima method (boleh kosong)
Body{ return ...; }Blok kode yang dijalankan saat method dipanggil
public class Checkout {
    public double hitungDiskon(double harga, int persen) {
        double potongan = harga * persen / 100.0;
        return harga - potongan;
    }
}

Return Value: void vs Tipe Data

public class OrderService {
    // Mengembalikan nilai → return type berupa tipe data
    public double hitungTotal(List<Double> itemHarga) {
        double total = 0;
        for (double h : itemHarga) {
            total += h;
        }
        return total;
    }

    // Tidak mengembalikan apa pun → return type void
    public void kirimKonfirmasi(String email) {
        System.out.println("Konfirmasi dikirim ke " + email);
    }
}

Konsep kunci: void berarti method hanya "melakukan sesuatu" tanpa memberi hasil; tipe data berarti method "memberi nilai" kepada pemanggilnya. Pilih sesuai kebutuhan: method yang menghitung harus return, method yang melakukan aksi sampingan cukup void.

Early Return & Guard Clause

return tidak harus berada di akhir method. Kita boleh menghentikan eksekusi lebih awal — pola ini disebut guard clause, sangat umum di backend untuk menolak input yang tidak valid sebelum logika utama dijalankan.

public double hitungTotalBayar(double harga, int persenDiskon) {
    if (harga < 0) {
        return 0;                  // early return: input tidak valid, langsung berhenti
    }
    if (persenDiskon > 100) {
        throw new IllegalArgumentException("Diskon tidak boleh lebih dari 100%");
    }
    return harga - (harga * persenDiskon / 100.0);
}

Pola ini membuat kode lebih datar (tidak bertingkat-tingkat if bersarang), validasi yang gagal berhenti lebih awal, dan logika utama hanya berjalan untuk data yang benar.

Parameter & Argument: Java Selalu Pass-by-Value

Istilah yang perlu dibedakan dulu: parameter (variabel yang ditulis di deklarasi method, seperti double harga) dan argument (nilai nyata yang diberikan saat method dipanggil, seperti 99_000).

Pertanyaan klasik yang sering menjebak junior: apakah Java pass-by-value atau pass-by-reference? Jawabannya tegas: Java selalu pass-by-value. Argument di-copy ke parameter, sehingga perubahan di dalam method tidak memengaruhi variabel asli pemanggil.

public static void ubah(int angka) {
    angka = 100;   // hanya mengubah salinan di parameter
}

public static void main(String[] args) {
    int asli = 5;
    ubah(asli);
    System.out.println(asli);   // tetap 5, bukan 100
}

Yang membingungkan adalah untuk objek: yang di-copy adalah nilai reference-nya (alamat objek di memori), bukan objeknya. Jadi method bisa mengubah isi objek, tetapi tidak bisa menukar objek di tempat pemanggil. Penjelasan lengkapnya di dokumentasi passing arguments.

public class User {
    public String nama;
}

public static void gantiNama(User u) {
    u.nama = "Budi";      // efek terlihat di pemanggil → objek yang sama
}

public static void tukar(User a, User b) {
    User tmp = a;         // hanya menukar salinan reference
    a = b;
    b = tmp;              // pemanggil tidak terpengaruh
}

public static void main(String[] args) {
    User u = new User();
    u.nama = "Andi";
    gantiNama(u);
    System.out.println(u.nama);  // "Budi" — isi objek berubah
}

Konsep kunci: "pass-by-value" untuk objek berarti alamat objek disalin. Method bisa mengubah isi objek yang dipegangnya, tetapi tidak bisa mengganti objek milik pemanggil dengan objek baru.

Method Overloading

Overloading adalah teknik menulis beberapa method dengan nama sama tetapi parameter berbeda (jumlah, tipe, atau urutannya). Java memilih method yang tepat berdasarkan argument yang diberikan saat pemanggilan.

public class Formatter {
    public String format(int angka) {
        return "Rp " + angka;
    }

    public String format(double angka) {
        return "Rp " + String.format("%.2f", angka);
    }

    public String format(int angka, String kodeMataUang) {
        return kodeMataUang + " " + angka;
    }
}

Contoh paling terkenal di JDK adalah Integer.parseInt(...) yang memiliki versi overload: parseInt(String), parseInt(String, int radix), dan lainnya. Overloading membuat API terasa nyaman: pemanggil memakai nama yang sama, Java yang mencarikan versi yang cocok.

Syarat overload: parameter harus berbeda; mengubah return type saja tidak dianggap overload dan akan error kompilasi.

Varargs: Argument Berjumlah Tak Tentu

Kadang kita tidak tahu berapa banyak argument yang akan diberikan. Dengan varargs (tanda ...), sebuah parameter bisa menerima jumlah nilai berapa pun, dan di dalam method diperlakukan sebagai array:

public class OrderService {
    public static double totalSemua(double... harga) {
        double total = 0;
        for (double h : harga) {
            total += h;
        }
        return total;
    }
}

double t = OrderService.totalSemua(10_000, 20_000);       // 2 argumen
double t2 = OrderService.totalSemua(1_000, 2_000, 3_000); // 3 argumen
double t3 = OrderService.totalSemua();                    // 0 argumen, total = 0

Perhatikan aturannya: varargs harus menjadi parameter terakhir dan hanya boleh ada satu; di belakang layar Java mengubahnya menjadi array (double[] harga).

Rekursi: Method yang Memanggil Diri Sendiri

Rekursi terjadi ketika sebuah method memanggil dirinya sendiri untuk menyelesaikan versi lebih kecil dari masalah yang sama. Contoh klasiknya adalah faktorial (5! = 5 × 4 × 3 × 2 × 1):

public static int faktorial(int n) {
    if (n <= 1) {
        return 1;                       // base case: titik berhenti
    }
    return n * faktorial(n - 1);        // recursive case: panggil diri sendiri
}

Setiap method rekursif wajib punya base case (kondisi berhenti). Tanpa itu, method akan terus memanggil dirinya sendiri sampai memori habis dan memunculkan StackOverflowError — setiap pemanggilan menyimpan konteks di call stack yang terbatas.

public static int tanpaBaseCase(int n) {
    return n + tanpaBaseCase(n - 1);   // tidak pernah berhenti
}

Konsep kunci: rekursi menambah satu frame ke call stack per pemanggilan; kedalaman terlalu besar → StackOverflowError. Untuk data besar, loop for/while (iterasi) lebih hemat memori dan lebih cepat.

Kapan memakai apa

SituasiPilihan
Masalah terstruktur bertingkat (tree, folder)Rekursi lebih ekspresif
Operasi pada list/rangkaian data besarIterasi (for/while)
Depth sangat dalam (ribuan level)Hindari rekursi, pakai iterasi

Method String yang Sering Dipakai di Backend

String adalah tipe yang paling sering diolah di backend — email, nama, alamat, JSON snippet. Ini method yang wajib hafal:

MethodContohHasil
length()"user".length()4
substring(a, b)"hendro@mail.com".substring(0, 6)"hendro"
split(regex)"a,b,c".split(",")["a","b","c"]
contains(s)"user@mail.com".contains("@")true
toLowerCase()"ADMIN".toLowerCase()"admin"
trim()" budi ".trim()"budi"
format(fmt, args...)String.format("Halo %s", "Budi")"Halo Budi"
String raw = "  HEndro@Mail.Com  ";
String email = raw.trim().toLowerCase();       // "hendro@mail.com"
boolean valid = email.contains("@") && email.contains(".");
String[] parts = email.split("@");             // ["hendro", "mail.com"]
String label = String.format("Member: %s", parts[0]);  // "Member: hendro"

Perhatikan cara berantai: raw.trim().toLowerCase() — method string bisa dipanggil berurutan karena setiap method mengembalikan String baru. Ini disebut method chaining.

Lambda & Functional Interface

Sejak Java 8, kita bisa menulis logika singkat sebagai lambda expression: fungsi tanpa nama dengan sintaks (params) -> body.

// Tanpa lambda (pakai anonymous class)
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

// Dengan lambda — jauh lebih singkat
Collections.sort(list, (a, b) -> a.length() - b.length());

Lambda hanya bisa dipakai pada functional interface — interface yang punya tepat satu method abstrak. Seperti Comparator di atas, atau interface bawaan di package java.util.function:

InterfaceInput → OutputContoh pemakaian
Function<T, R>T → Rs -> s.trim() (ubah satu nilai jadi nilai lain)
Predicate<T>T → booleanu -> u.age >= 17 (uji kondisi)
Supplier<T>() → T() -> UUID.randomUUID().toString() (produksi nilai)
Consumer<T>T → voidlog -> System.out.println(log) (aksi, tanpa hasil)

Lambda ini berpasangan sangat erat dengan Stream API. Dalam satu kalimat: stream memproses koleksi secara deklaratif, dan lambda adalah "mesin kecil" yang kita selipkan ke dalamnya.

List<User> dewasa = users.stream()
        .filter(u -> u.age >= 17)            // Predicate
        .map(u -> u.email.toUpperCase())     // Function
        .toList();                           // kumpulkan hasil

Method Reference

Method reference adalah penyederhanaan lambda ketika lambda hanya memanggil method yang sudah ada, dengan sintaks ClassName::methodName.

// Lambda penuh
users.forEach(u -> System.out.println(u.email));

// Method reference — lebih bersih
users.forEach(System.out::println);
// Contoh di backend: urutkan user berdasarkan nama
users.sort(Comparator.comparing(User::getNama));

User::getNama sama artinya dengan u -> u.getNama(). Pakai method reference saat lambda hanya "meneruskan" pemanggilan; kalau body lambda berisi logika, tulis lambda biasa agar tetap jelas.

Contoh Mini Integrasi: Service Validasi & Diskon

Semua konsep di atas saling menguatkan. Berikut contoh kecil yang menyusun method-method menjadi satu service sederhana — pola yang sangat mirip dengan kode backend sungguhan: method satu tanggung jawab, guard clause untuk validasi, method static untuk logika murni, dan lambda untuk pengolahan data.

import java.util.List;

public class OrderService {

    // 1. Guard clause — validasi email
    public static boolean isValidEmail(String email) {
        if (email == null || email.isBlank()) {
            return false;
        }
        return email.contains("@") && email.contains(".");
    }

    // 2. Static method murni — hitung diskon
    public static double hitungDiskon(double harga, int persen) {
        if (harga < 0 || persen < 0 || persen > 100) {
            throw new IllegalArgumentException("Input diskon tidak valid");
        }
        return harga - (harga * persen / 100.0);
    }

    // 3. Instance method — memakai state, memanggil method lain
    private double totalPendapatan;
    public void prosesPembayaran(String email, double harga, int persen) {
        if (!isValidEmail(email)) {
            throw new IllegalArgumentException("Email tidak valid");
        }
        double dibayar = hitungDiskon(harga, persen);
        totalPendapatan += dibayar;
        System.out.printf("Tagihan dikirim ke %s: Rp %.2f%n", email, dibayar);
    }

    // 4. Memanfaatkan lambda + method reference
    public static List<String> emailValid(List<String> emails) {
        return emails.stream()
                .map(String::trim)
                .filter(OrderService::isValidEmail)
                .map(String::toLowerCase)
                .toList();
    }
}

Lihat bagaimana method-method ini saling memanggil: prosesPembayaran memakai isValidEmail untuk validasi dan hitungDiskon untuk perhitungan, sementara emailValid merangkai lambda untuk memfilter koleksi. Setiap method kecil berdiri sendiri dan mudah diuji — inilah kenapa menguasai method adalah kunci menulis backend yang rapi.

Ringkasan

  • Method adalah blok kode bernama yang bisa dipanggil ulang; unit logika terkecil yang bisa diuji.
  • Static method dipanggil lewat nama class dan cocok untuk utilitas; instance method bekerja dengan state objek.
  • Anatomi method: access modifier, return type, nama, parameter, dan body — masing-masing punya peran jelas.
  • void = tanpa hasil; tipe data = mengembalikan nilai. Gunakan guard clause untuk menghentikan eksekusi lebih awal saat input tidak valid.
  • Java selalu pass-by-value; untuk objek, yang disalin adalah reference-nya, jadi isi objek bisa berubah tapi objeknya tidak bisa diganti.
  • Overloading = nama method sama, parameter berbeda; varargs (...) menampung argumen berjumlah tak tentu.
  • Rekursi elegan untuk masalah bertingkat, tetapi hati-hati StackOverflowError; iterasi lebih hemat memori untuk data besar.
  • String method seperti substring, split, trim, dan format adalah alat sehari-hari backend.
  • Lambda ((p) -> body) dan method reference (Class::method) membuat kode koleksi dan stream jauh lebih ringkas.
  • Service yang baik = kumpulan method kecil, satu tanggung jawab, saling memanggil dengan rapi.

Lanjut membaca

← Back to technical articles