home > back-end > java > [java] 메소드

[java] 메소드
back-end java method

intro : java 메소드에 대한 개념을 알아보자.

메서드 시작

두 숫자를 입력 받아서 더하고 출력하는 단순한 기능을 개발해보자. 먼저 1 + 2 를 수행하고, 그 다음으로 10 + 20 을 수행할 것인데, 아래 코드를 보면 두개의 변수를 받아서 합을 구하는 연산 과정이 중복이 된다. 이 중복되는 부분 어떻게 개선을 하면 좋을까 ?

public class Method1 {
    public static void main(String[] args) {
        // 계산1
        int a = 1;
        int b = 2;
        System.out.println(a + "+" + b + " 연산 수행");
        int sum1 = a + b;
        System.out.println("결과1 출력:" + sum1);
        
        // 계산2
        int x = 10;
        int y = 20;
        System.out.println(x + "+" + y + " 연산 수행");
        int sum2 = x + y;
        System.out.println("결과2 출력:" + sum2);
    }
} 

메서드 사용

자바에서는 함수를 메서드(Method)라 한다. 메서드도 함수의 한 종류라고 생각하면 된다. 지금은 둘을 구분하지 않고, 이정도만 알아두자. 앞서 작성한 코드의 중복된 부분을 메서드를 이용하여 개선해보자.

public class Method1Ref {
    public static void main(String[] args) {
        int sum1 = add(5, 10);
        System.out.println("결과1 출력:" + sum1);
        int sum2 = add(15, 20);
        System.out.println("결과2 출력:" + sum2);
    }
    
    // add 메서드
    public static int add(int a, int b) {
        System.out.println(a + "+" + b + " 연산 수행");
        int sum = a + b;
        return sum;
    }
}  

아래와 같은 코드에 add 함수를 메서드라고 한다. 메서드의 역할은 어떤 연산을 처리한 다음에 결과를 반환한다. (무조건적인 반환을 해주어야 하는건 아니다.)

public static int add(int a, int b) {
        System.out.println(a + "+" + b + " 연산 수행");
        int sum = a + b;
        return sum;
}

메서드의 구조

메서드의 구조는 크게 2가지로 나눌 수 있는데, 메서드 선언부와 본문으로 나눌 수 있다.

메서드 선언

메서드 이름 반환타입 매개변수 목록을 포함한다. 이름 그대로 이런 메서드가 있다고 선언하는 것이다. 메서드 선언 정보를 통해 다른 곳에서 해당 메서드를 호출할 수 있다.

// add 메서드 선언부
public static int add(int a, int b)

메서드 본문

메서드가 수행해야 하는 코드 블록이다. 메서드를 호출하면 메서드 본문이 순서대로 실행된다. 메서드 본문은 블랙박스이다. 메서드를 호출하는 곳에서는 메서드 선언은 알지만 메서드 본문은 모른다. 메서드의 실행 결과를 반환하려면 return 문을 사용해야 한다. return 문 다음에 반환할 결과를 적어주면 된다.

{
        // add 메서드 본문
        System.out.println(a + "+" + b + " 연산 수행");
        int sum = a + b;
        return sum;
} 

메서드 호출과 용어정리

메서드를 호출할 때는 다음과 같이 메서드에 넘기는 매개변수(파라미터)의 타입이 맞아야 한다. 물론 넘기는 값과 매개변수(파라미터)의 순서갯수도 맞아야 한다.

인수 (Argument)

여기서 "hello" , 20 처럼 메서드에 넘기는 값을 영어로 Argument(아규먼트), 한글로 인수 또는 인자라 한다.실무에서는 아규먼트, 인수, 인자라는 용어를 모두 사용한다.

매개변수(Parameter)

메서드를 정의할 때 선언한 변수인 String str , int age 를 매개변수, 파라미터라 한다. 메서드를 호출할 때 인수를 넘기면, 그 인수가 매개변수에 대입된다. 실무에서는 매개변수, 파라미터 용어를 모두 사용한다.

위 둘의 단어는 반드시 분리되어 사용되어야 한다 혼용되어선 안된다.

메서드 정의

메서드는 다음과 같이 정의 할 수 있다.

public static int add(int a, int b) {
    // 메서드 본문, 실행 코드
}

제어자 반환타입 메서드이름(매개변수 목록) {
    메서드 본문
}     

제어자(Modifier)

public , static 과 같은 부분이다. 제어자는 뒤에서 설명한다. 지금은 항상 public static 키워드를 입력하자.

반환 타입(Return Type)

메서드가 실행 된 후 반환하는 데이터의 타입을 지정한다. 메서드가 값을 반환하지 않는 경우, 없다는 뜻의 void 를 사용해야 한다. 예) void print(String str)

메서드 이름(Method Name)

메서드의 이름이다. 이 이름은 메서드를 호출하는 데 사용된다.

매개변수(Parameter)

입력 값으로, 메서드 내부에서 사용할 수 있는 변수이다. 매개변수는 옵션이다. 입력값이 필요 없는 메서드는 매개변수를 지정하지 않아도 된다. 예) add()

메서드 본문(Method Body)

실제 메서드의 코드가 위치한다. 중괄호 {} 사이에 코드를 작성한다.

반환 타입

반환 타입이 있으면 반드시 값을 반환해야 한다. 반환 타입이 있는 메서드는 반드시 return 을 사용해서 값을 반환해야 한다.

아래 코드에서 if 조건이 만족할 때는 true 가 반환되지만, 조건을 만족하지 않으면 어떻게 될까? 조건을 만족하지 않은 경우에는 return 문이 실행되지 않는다. 따라서 이 코드를 실행하면 return 문을 누락했다는 다음과 같은 컴파일 오류가 발생한다.

public class MethodReturn1 {
    public static void main(String[] args) {
        boolean result = odd(2);
        System.out.println(result);
    }
    public static boolean odd(int i) {
        if (i % 2 == 1) {
            return true;
        }
        // 반드시 boolean 타입으로 return이 되어야 하는 메서드 인데 if문 조건을 만족하지 못한다면
        // return 되지 않기에 컴파일 오류가 발생한다.
    }
}

위 코드는 다음과 같이 수정되어야 한다.

public class MethodReturn1 {
    public static void main(String[] args) {
        boolean result = odd(2);
        System.out.println(result);
    }
    public static boolean odd(int i) {
        if (i % 2 == 1) {
            return true;
        } else {
            // 조건문을 만족하지 않는다면 false 반환
            return false;
        }
    }
}

return 문을 만나면 그 즉시 메서드를 빠져나간다.

return 문을 만나면 그 즉시 해당 메서드를 빠져나가는데 다음과 같이 활용할 수 있다. (메서드를 특정 시점에 강제로 종료해야할때 자주 사용한다.)

public class MethodReturn2 {
    public static void main(String[] args) {
        checkAge(10);
        checkAge(20);
    }
    public static void checkAge(int age) {
        if (age < 18) {
            System.out.println(age + "살, 미성년자는 출입이 불가능합니다.");
            // return 문을 만나면 메서드가 종료되고, 아래 출력문이 실행되지 않는다.
            return;
        }
        System.out.println(age + "살, 입장하세요.");
    }
}     

메서드 호출과 값 전달1

자바에서 중요한 원칙이 있는데, 자바는 값을 대입할때 변수의 값을 복사해서 대입한다. 아래 코드를 통해서 내가 이해한 중요한 원칙을 제대로 이해했는지 확인해보자. 아래 주석으로 출력 결과를 작성했다. 혹시 틀리게 생각한 부분이 있다면 변수의 값을 복사해서 대입한다는 개념이 아직 부족하다는 뜻이다.

public class MethodValue1 {
    public static void main(String[] args) {
        int num1 = 5;
        System.out.println("1. changeNumber 호출 전, num1: " + num1);
        changeNumber(num1);
        System.out.println("4. changeNumber 호출 후, num1: " + num1);
    }

    public static void changeNumber(int num2) {
        System.out.println("2. changeNumber 변경 전, num2: " + num2);
        num2 = num2 * 2;
        System.out.println("3. changeNumber 변경 후, num2: " + num2);
    }
}

// 1. changeNumber 호출 전, num1: 5
// 2. changeNumber 변경 전, num2: 5
// 3. changeNumber 변경 후, num2: 10
// 4. changeNumber 호출 후, num1: 5

메서드 호출과 값 전달2

다시한번 중요한 원칙인 자바는 값을 복사해서 대입한다라는 개념을 이해했는지 재확인해보자.

public class MethodValue2 {
    public static void main(String[] args) {
        int number = 5;
        System.out.println("1. changeNumber 호출 전, number: " + number); // 출력: 5
        changeNumber(number);
        System.out.println("4. changeNumber 호출 후, number: " + number); // 출력: 5
    }

    public static void changeNumber(int number) {
        System.out.println("2. changeNumber 변경 전, number: " + number); // 출력: 5
        number = number * 2;
        System.out.println("3. changeNumber 변경 후, number: " + number); // 출력: 10
    }
}

// 1. changeNumber 호출 전, number: 5
// 2. changeNumber 변경 전, number: 5
// 3. changeNumber 변경 후, number: 10
// 4. changeNumber 호출 후, number: 5

메서드 호출과 값 반환받기

메서드를 사용해서 값을 변경하려면 어떻게 해야할까? 메서드의 호출 결과를 반환 받아서 사용하면 된다.

public class MethodValue3 {
    public static void main(String[] args) {
        int num1 = 5;
        System.out.println("changeNumber 호출 전, num1: " + num1); // 출력: 5
        num1 = changeNumber(num1);
        System.out.println("changeNumber 호출 후, num1: " + num1); // 출력: 10
    }
    public static int changeNumber(int num2) {
        num2 = num2 * 2;
        return num2;
    }
}

메서드와 형변환

메서드를 호출할 때도 형변환이 적용된다. 메서드 호출과 명시적 형변환, 자동 형변환에 대해 알아보자.

명시적 형변환

메서드를 호출하는데 인자와 매개변수의 타입이 맞지 않다면 어떻게 해야할까? 아래 주석을 풀고 실행하면 다음과 같은 오류가 발생한다. (java: incompatible types: possible lossy conversion from double to int)

public class MethodCasting1 {
    public static void main(String[] args) {
        double number = 1.5;
        // double을 int형에 대입하므로 컴파일 오류
        // 반드시 실행하고 싶다면 (int) number 을 인수로 전달해야 한다.
        //printNumber(number);
        printNumber((int) number); // 명시적 형변환을 사용해 double을 int로 변환
    }

    public static void printNumber(int n) {
        System.out.println("숫자: " + n);
    }
}

자동 형변환

int < long < double 메서드를 호출할 때 매개변수에 값을 전달하는 것도 결국 변수에 값을 대입하는 것이다. 따라서 앞서 배운 자동 형변환이 그대로 적용된다. 메서드를 호출할 때는 전달하는 인수의 타입과 매개변수의 타입이 맞아야 한다. 단 타입이 달라도 자동 형변환이 가능한 경우에는 호출할 수 있다.

byte → short → int → long → float → double
char → int → long → float → double

public class MethodCasting2 {
    public static void main(String[] args) {
        int number = 100;
        printNumber(number); // int에서 double로 자동 형변환
    }

    public static void printNumber(double n) {
        System.out.println("숫자: " + n);
    }
}

메서드 오버로딩

자바는 메서드의 이름 뿐만 아니라 매개변수 정보를 함께 사용해서 메서드를 구분한다. 따라서 다음과 같이 이름이 같고, 매개변수가 다른 메서드를 정의할 수 있다. 이렇게 이름이 같고 매개변수가 다른 메서드를 여러개 정의하는 것을 메서드 오버로딩(Overloading)이라 한다. 오버로딩은 번역하면 과적인데, 과하게 물건을 담았다는 뜻이다. 따라서 같은 이름의 메서드를 여러개 정의했다고 이해하면 된다.

오버로딩 규칙

메서드의 이름이 같아도 매개변수의 타입 및 순서가 다르면 오버로딩을 할 수 있다. 참고로 반환 타입은 인정하지 않는다.

메서드 시그니처(method signature)

메서드 시그니처 = 메서드 이름 + 매개변수 타입(순서)
메서드 시그니처는 자바에서 메서드를 구분할 수 있는 고유한 식별자나 서명을 뜻한다. 메서드 시그니처는 메서드의 이름과 매개변수 타입(순서 포함)으로 구성되어 있다. 쉽게 이야기해서 메서드를 구분할 수 있는 기준이다. 자바 입장에서는 각각의 메서드를 고유하게 구분할 수 있어야한다. 그래야 어떤 메서드를 호출 할 지 결정할 수 있다. 따라서 메서드 오버로딩에서 설명한 것 처럼 메서드 이름이 같아도 메서드 시그니처가 다르면 다른 메서드로 간주한다. 반환 타입은 시그니처에 포함되지 않는다.

다음 코드는 오버로딩을 활용한 코드이다.

public class Overloading1 {
    public static void main(String[] args) {
        System.out.println("1: " + add(1, 2));
        System.out.println("2: " + add(1, 2, 3));
    }

    // 첫 번째 add 메서드: 두 정수를 받아서 합을 반환한다.
    public static int add(int a, int b) {
        System.out.println("1번 호출");
        return a + b;
    }

    // 두 번째 add 메서드: 세 정수를 받아서 합을 반환한다.
    // 첫 번째 메서드와 이름은 같지만, 매개변수 목록이 다르다.
    public static int add(int a, int b, int c) {
        System.out.println("2번 호출");
        return a + b + c;
    }
}

아래코도는 오버로딩 케이스중에 헷갈릴만한 케이스인데, add 메서드는 파라미터가 실수형 타입 double을 인자값으로 받고있다. 이때 add(1, 2)를 호출하게 된다면 어떻게 될까? 단순히 실수형 타입에 정수형 타입을 넣어야 하니 오류가발생할것 같지만 실제로는 자동형변환이 일어나서 add(1, 2) 가 정상적으로 실행된다.

public class Main {
    public static void main(String[] args) {
        System.out.println("1: " + add(1, 2));
        System.out.println("2: " + add(1.2, 1.5));
    }

    public static double add(double a, double b) {
        System.out.println("add 메서드 호출");
        return a + b;
    }
}

// add 메서드 호출
// 1: 3.0
// add 메서드 호출
// 2: 2.7