Java 메서드 이해하기: 매개변수, 반환값, 값 전달, 오버로딩

Alchemist·2026년 10월 7일

KT AIVLE

목록 보기
67/130

프로그램이 커질수록 같은 코드가 반복되고 하나의 main() 메서드가 길어지기 쉽다. 이 문제를 해결하는 핵심 도구가 메서드(method) 다.

메서드는 특정 작업을 이름 붙인 코드 블록으로 분리하고 필요할 때 호출할 수 있게 해준다.

메서드가 필요한 이유

다음 계산을 여러 곳에서 반복한다고 생각해보자.

int total = price * quantity;
int deliveryFee = total >= 50000 ? 0 : 3000;
int payment = total + deliveryFee;

같은 로직을 여러 곳에 복사하면 변경할 때 모든 코드를 찾아 수정해야 한다.

메서드로 분리할 수 있다.

static int calculatePayment(int price, int quantity) {
    int total = price * quantity;
    int deliveryFee = total >= 50000 ? 0 : 3000;
    return total + deliveryFee;
}

사용하는 쪽에서는 의도가 훨씬 선명하다.

int payment = calculatePayment(15000, 4);

메서드 기본 구조

static int add(int a, int b) {
    int result = a + b;
    return result;
}

구성 요소를 나눠보면 다음과 같다.

static int add(int a, int b)
       ↑   ↑      ↑
     반환형 이름   매개변수

그리고 { } 안이 메서드 본문이다.

매개변수와 인수

static int add(int a, int b) {
    return a + b;
}

여기서 a, b는 매개변수(parameter) 다.

호출할 때 넘기는 실제 값은 인수(argument) 다.

int result = add(10, 20);

여기서 10, 20이 인수다.

반환값

반환 타입이 int라면 메서드는 int 값을 반환해야 한다.

static int multiply(int a, int b) {
    return a * b;
}

호출 결과를 변수에 저장할 수 있다.

int result = multiply(4, 5);

void 메서드

반환할 값이 없다면 void를 사용한다.

static void printWelcomeMessage() {
    System.out.println("환영합니다.");
}

호출:

printWelcomeMessage();

return은 메서드를 즉시 종료한다

static void printUser(int age) {
    if (age < 0) {
        return;
    }

    System.out.println("age=" + age);
}

age < 0이면 return에서 메서드 실행이 끝난다.

이 패턴은 잘못된 조건을 초기에 빠르게 종료하는 early return으로 자주 활용된다.

Java는 값을 복사해서 전달한다

메서드를 이해할 때 매우 중요한 원칙이다.

static void changeNumber(int number) {
    number = 100;
}

public static void main(String[] args) {
    int value = 10;

    changeNumber(value);
    System.out.println(value);
}

출력:

10

왜 100이 아닐까?

호출 시 value 변수 자체를 넘기는 것이 아니라 현재 값 10이 복사되어 number에 전달되기 때문이다.

value = 10
   ↓ 값 복사
number = 10

메서드 안에서 number를 바꿔도 원본 지역 변수 value는 바뀌지 않는다.

변경 결과가 필요하다면 반환값을 받아야 한다.

static int changeNumber(int number) {
    return 100;
}

value = changeNumber(value);

Java는 객체를 전달할 때도 참조값 자체가 복사되어 전달된다. 따라서 Java는 항상 pass-by-value 방식이라고 이해하는 것이 정확하다.

메서드와 형변환

호출 인수의 타입이 매개변수보다 작은 범위라면 자동 형변환이 가능한 경우가 있다.

static void printNumber(long value) {
    System.out.println(value);
}

int count = 10;
printNumber(count);

int 값이 long으로 자동 변환된다.

반대로 long 값을 int 매개변수에 자동으로 넘길 수는 없다.

메서드 오버로딩

메서드 이름이 같아도 매개변수 구성이 다르면 여러 메서드를 정의할 수 있다.

static int add(int a, int b) {
    return a + b;
}

static double add(double a, double b) {
    return a + b;
}

호출 시 전달한 인수 타입에 맞는 메서드가 선택된다.

System.out.println(add(1, 2));
System.out.println(add(1.5, 2.5));

오버로딩에서 중요한 것은 메서드 이름 + 매개변수 타입/순서다.

반환 타입만 다르게 만드는 것은 오버로딩이 아니다.

// 컴파일 불가
static int find() {
    return 1;
}

static double find() {
    return 1.0;
}

호출하는 쪽에서 어떤 메서드를 선택해야 하는지 구분할 수 없기 때문이다.

메서드 추출로 코드 개선하기

긴 코드 안에 입금과 출금 로직이 섞여 있다고 가정해보자.

if (menu == 1) {
    // 입금 처리 코드 20줄
} else if (menu == 2) {
    // 출금 처리 코드 25줄
}

메서드로 나누면 상위 코드가 프로그램의 흐름을 설명하게 된다.

if (menu == 1) {
    balance = deposit(balance, amount);
} else if (menu == 2) {
    balance = withdraw(balance, amount);
}

메서드를 잘 나누는 것은 단순히 코드 줄 수를 줄이는 작업이 아니다.

코드에 이름을 붙여 의도를 드러내는 작업이다.

좋은 메서드 이름

좋지 않은 예:

static int process(int a, int b) {
    // ...
}

더 나은 예:

static int calculateOrderTotal(int unitPrice, int quantity) {
    // ...
}

호출부만 보더라도 무엇을 하는지 이해할 수 있어야 한다.

메서드 선언을 구성 요소별로 읽기

static int calculateTotal(int price, int quantity) {
    return price * quantity;
}

이 코드를 부분별로 나누면 다음과 같다.

static                메서드 관련 제어자
int                   반환 타입
calculateTotal        메서드 이름
int price, int quantity  매개변수
{ ... }               메서드 본문

입문 단계의 main()에서 바로 호출하기 위해 static 메서드를 주로 사용하지만, 객체지향을 배우면 인스턴스 메서드와 static 메서드의 차이를 본격적으로 다루게 된다.

반환 타입이 있다면 모든 정상 경로에서 값을 반환해야 한다

static String getStatus(int stock) {
    if (stock > 0) {
        return "AVAILABLE";
    }

    // return이 없다면 컴파일 오류
}

반환 타입이 String인데 어떤 실행 경로에서는 값 없이 끝날 수 있기 때문이다.

static String getStatus(int stock) {
    if (stock > 0) {
        return "AVAILABLE";
    }

    return "SOLD_OUT";
}

컴파일러가 실행 전에 이런 실수를 잡아준다는 점은 정적 타입 언어의 장점 중 하나다.

early return으로 중첩 줄이기

return은 값을 돌려주는 기능뿐 아니라 현재 메서드를 즉시 종료한다.

static void processOrder(int quantity) {
    if (quantity <= 0) {
        System.out.println("잘못된 수량");
        return;
    }

    System.out.println("주문 처리 시작");
}

오류 조건을 먼저 처리하고 빠져나오면 정상 흐름의 중첩이 줄어드는 경우가 많다.

Java는 참조형도 pass-by-value다

static void changeFirst(int[] values) {
    values[0] = 999;
}

호출하면 원본 배열의 요소가 바뀐다.

int[] numbers = {1, 2, 3};
changeFirst(numbers);
System.out.println(numbers[0]); // 999

이것만 보면 참조를 "그대로 전달"한다고 느낄 수 있지만 Java의 규칙은 여전히 값 전달(pass-by-value) 이다.

복사되는 값이 배열 객체 자체가 아니라 배열을 가리키는 참조값이기 때문이다. 두 참조가 같은 배열을 가리키므로 메서드 내부에서 요소를 변경하면 동일한 객체가 바뀐다.

반면 매개변수 자체에 새 배열을 대입해도 호출자의 변수가 가리키는 대상은 바뀌지 않는다.

static void replace(int[] values) {
    values = new int[]{100, 200};
}
int[] numbers = {1, 2, 3};
replace(numbers);

System.out.println(numbers[0]); // 1

이 차이가 Java의 값 전달 규칙을 이해하는 핵심이다.

오버로딩은 반환 타입만으로 구분할 수 없다

다음 두 메서드는 만들 수 없다.

static int find(int id) {
    return id;
}

// 컴파일 오류
static String find(int id) {
    return String.valueOf(id);
}

호출부가 다음처럼 생겼을 때 어떤 메서드를 고를지 결정할 수 없기 때문이다.

find(10);

오버로딩을 구분하는 핵심은 메서드 이름 + 매개변수 타입/개수/순서다.

자동 형변환도 오버로딩 선택에 영향을 준다

static void print(int value) {
    System.out.println("int: " + value);
}

static void print(double value) {
    System.out.println("double: " + value);
}
print(10);   // int 버전
print(10.5); // double 버전

정확히 맞는 타입이 있으면 그 메서드가 우선한다.

만약 int 버전이 없다면 10은 double로 자동 변환될 수 있으므로 double 버전이 호출될 수 있다.

오버로딩이 많아질수록 호출 규칙을 머릿속으로 계산하기 어려워진다. 편의 기능이라고 해서 무조건 많이 만드는 것보다 읽는 사람이 어떤 메서드가 선택되는지 쉽게 알 수 있게 설계하는 것이 중요하다.

메서드로 추출하면 main이 요구사항처럼 읽힌다

int subtotal = calculateSubtotal(price, quantity);
int discount = calculateDiscount(subtotal, memberGrade);
int payment = calculatePayment(subtotal, discount);
printReceipt(payment);

각 메서드 내부 구현을 당장 읽지 않아도 프로그램이 무엇을 하는지 파악할 수 있다.

좋은 메서드 분리는 단순히 코드 줄 수를 줄이는 것이 아니라 복잡한 세부 구현을 이름 뒤에 숨기고 상위 흐름을 선명하게 만드는 추상화다.

핵심 정리

  • 메서드는 반복되는 로직을 이름 있는 코드 블록으로 분리한다.
  • 메서드는 반환 타입, 이름, 매개변수, 본문으로 구성된다.
  • void는 반환값이 없는 메서드에 사용한다.
  • return은 값을 반환하면서 메서드를 종료하거나 void 메서드를 조기 종료할 수 있다.
  • Java는 메서드 호출 시 항상 값을 복사해서 전달한다.
  • 기본형 값은 메서드 내부에서 변경해도 호출한 쪽의 원본 변수가 바뀌지 않는다.
  • 메서드 오버로딩은 이름은 같고 매개변수 타입이나 개수가 다른 메서드를 정의하는 기능이다.
  • 반환 타입만 다른 메서드는 오버로딩할 수 없다.
profile
html_programming_language

0개의 댓글