코린이의 소소한 공부노트

메서드의 생성(선언)과 사용(호출) 본문

Java

메서드의 생성(선언)과 사용(호출)

무지맘 2022. 3. 4. 22:09

메서드는 객체지향 언어에서 쓰는 말로, 작업 단위로 문장들을 묶어서 이름 붙인 것이다. 함수는 클래스에서 독립적으로 있을 수 있는 반면 메서드는 클래스 안에 존재해야 한다. 이런 차이가 있긴 하나, 결과적으로 함수와 메서드는 같은 말이다.

 

메서드를 왜 사용하는지 배열의 모든 요소를 2번 출력하는 상황을 보자.

 - 왼쪽 코드: 출력할 때마다 for문 사용

 - 오른쪽 코드: 배열 요소를 출력하는 메서드를 만들어 놓고 메인 메서드에서 메서드 호출(사용) <- printArr(arr);

  - 코드의 길이는 (그림상에서는) 왼쪽 < 오른쪽

  - 왼쪽의 경우 for문을 다 훑어봐야 무슨 작업인지 알 수 있음

  - 오른쪽의 경우 메서드 이름만 봐도 어떤 작업을 하는 메서드인지 알 수 있음

  - 배열의 요소를 앞에서부터 50개까지만 출력하는 형식으로 바꾸려면 왼쪽은 2번, 오른쪽은 1번 수정해야 함

여기까지만 봐도 메서드를 사용하는 것이 훨씬 좋다는 게 느껴질 것이다. 지금부터 자세히 설명하겠다.

 

[메서드의 장점]

 1. 코드의 중복을 줄일 수 있다.

  - 같은 작업을 수행하는 코드를 1번만 쓰면 됨

  - if 중복 제거가 안됨 -> 수정이 필요할 때 모든 중복 코드를 다 찾아서 일일이 수정 -> 관리가 어려움

 2. 코드 관리가 쉽다.

  - 메서드를 한 곳에 써놓고 사용하기 때문에, 메서드 수정이 필요한 경우 여러 군데에서 메서드 호출을 해도 1군데만 수정하면 됨(=코드의 유지보수성이 좋음)

3. 코드를 재사용할 수 있다.

  - 필요할 때 몇 번이든 호출해서 쓸 수 있음

  - 재사용성이 올라갈수록 코드가 간결해지고 이해하기 쉬워진다.

 

[메서드의 작성법]

메서드는 클래스 영역에만 정의가 가능하고, 선언부와 구현부로 이루어져 있다. 선언부에는 입력과 출력이 설명되어있고, 구현부는 메서드의 기능을 실행한다.

class Mathfunc{
    int add(int x, int y) {
        int result = x + y; 
        return result;
    }
}

1. 선언부: 반환타입 메서드이름 (타입 변수명, 타입 변수명, ...)

int add(int x, int y)

 1) 반환타입: 메서드 작업 후 반환할 타입(output)

  - 반환 값은 1개가 기본 -> 타입도 1개만 작성

  - 만약 반환할 것이 없다면 void로 쓰기

  - 만약 반환할 것이 2개 이상이라면, 배열 등의 객체에 결과를 담아서 반환타입으로 쓰기

  - 참조형 반환타입에 대한 설명은 링크 제일 아래 >> 2022.03.06 - [Java] - 메서드의 매개변수

 2) 메서드이름: 메서드 호출 시 사용할 이름

  - 1개의 메서드는 1개의 기능만 수행하게 짜는 것이 일반적

  - 그 기능이 잘 드러나게 메서드 이름 작성

 3) 매개변수(parameter): 메서드 내부에서 쓸 지역 변수(input)

  - 호출하는 메서드와 호출받는 메서드의 중간 역할을 한다고 해서 매개변수라고 부름

  - 어떤 값을 받아서 작업해야 한다면 매개변수로 타입과 변수명을 선언해주기

  - 매개변수의 개수는 딱히 제한 없음 -> 0개여도 상관없음. 여러 개는 콤마, 로 구분 짓기

  - 매개변수는 자동 형변환이 가능함

  - 기본형, 참조형 관계없이 올 수 있음

2. 구현부: { // 메서드 호출 시 수행될 코드 }

{
    int result = x + y; 
    return result;
}

 1) 프로그램에서 반복적으로 수행되는 여러 문장을 메서드로 작성

 2) 모든 문장을 수행하거나 return문을 만나면 호출한 메서드(main)로 돌아감

  - 반환타입이 void면 return문 생략 가능. 필요시 사용

  - 반환타입이 void가 아니라면 반드시 return문 사용

    - return문이 조건식이 참일 때만 실행된다면 에러 발생

      -> 조건식이 거짓일 때도 return문이 있도록 작성해야 함

예를 들어, 두 정수를 받아 더 큰 값을 반환하는 함수를 만들었다고 해보자.

int findBigger(int x, int y) {
    if (x>y)
        return x; // error: This method must return a result of type int
}

사람이 봤을 때, 반쪽짜리 구현이라 당연히 이상하게 느낄 것이다. 컴파일러는 이것을 return문이 없다는 에러로 표현한다. 조건식이 거짓일 때 return문이 없기 때문이다.

int findBigger(int x, int y) {
    if (x>y)
        return x;
    else
        return y;
}

이제 좀 편안해졌다.

 

[메서드가 여러 개의 기능을 수행하게 작성하면 안 되는 이유]

1. 메서드 이름을 직관적이게 만들기 어려움

2. 재사용성이 떨어짐

  - A 메서드는 a, b를 수행하고 B 메서드는 a만 수행한다고 할 때, a가 주 기능이라고 한다면 B 메서드를 더 많이 사용

    -> A 메서드의 재사용성이 떨어짐

3. 유지보수성이 떨어짐

  - A 메서드에 기능이 3가지가 있는데, 그중 하나를 수정하게 되면 다른 기능도 수정하게 될 가능성이 커짐

 

[메서드의 호출(call)]

작성만 하면 메서드가 실행되지 않는다. 호출을 해줘야 메서드 구현부가 실행된다.

1. 메서드이름();

  - 입력 값이 필요 없을 때의 호출법 

2. 메서드이름(입력값1, 입력값2, ...);

  - 입력 값이 필요할 때의 호출법

  - 매개변수에는 변수 or 값을 입력

  - 입력으로 넘긴 값이 메서드의 매개변수에 대입됨

  - 입력값으로 넘기는 매개변수의 개수 = 메서드 선언 시 설정해놓은 매개변수의 개수

작업을 마치면 호출한 곳으로 돌아오고, 이때 반환 값이 있을 경우 그 값을 같은 타입의 변수에 저장할 수 있다. 반환 값이 있는데 변수에 저장을 하지 않아도 에러는 발생하지 않는다.

 

[메서드 선언 후 호출 시 일어나는 일]

입력 값의 여부, 반환 값의 여부에 따라 메서드 호출 시 어떤 일이 일어나는지 하나하나 살펴보자.

1. Mathfunc 클래스에 4가지 메서드를 선언했다.

class Mathfunc {
    // 입력O, 반환O
    double sub(double x, double y){
        double result = x-y;
        return result;
    }

    // 입력X, 반환O
    double givemePi() { return 3.14; }

    // 입력O, 반환X
    void printPlus1(int x) {
        System.out.println(x+1);
    }

    // 입력X, 반환X
    void printMath() {
        System.out.println("What a wonderful Math!");
    }
}

2. 각각의 메서드를 활용하기 위해 메인 메서드에서 객체를 생성한다.

public static void main(String args[]) {
    Mathfunc mf = new Mathfunc();
}

3. 이제 메인 메서드에서 각 메서드를 호출하고, 결과를 확인해보겠다.

1) 반환O, 입력O

double result = mf.sub(6, 8); // int로 넘겼지만 double로 자동 형변환
System.out.println(result); // -2.0

class Mathfunc {
    double sub(double x, double y){
        double result = x-y;
        return result;
    }
}

  메인이 sub을 호출하며 6과 8을 넘김

  -> x=6.0, y=8.0 대입 후 6.0-8.0을 계산하여 -2.0를 result에 저장

  -> 메인으로 -2.0이 반환되어 result에 대입되고, println문에서 -2.0 출력

  # 변수 result의 중복

   - sub 메서드에도, main 메서드에도 int 타입의 변수 result가 존재

   - 지역 변수의 유효 범위는 메서드 내부

   - 따라서 이름이 같아도 다른 메서드 영역에 있기 때문에 다른 것으로 취급

2) 반환O, 입력X

double myPi = mf.givemePi();
System.out.println(myPi); // 3.14

class Mathfunc {
    double givemePi() { return 3.14; }
}

  메인이 givemePi를 호출

  -> 3.14를 반환함

  -> 3.14가 myPi에 대입되고, println문에서 3.14 출력

3) 반환X, 입력O

mf.printPlus1(5); // 6

class Mathfunc {
    void printPlus1(int x) {
        System.out.println(x+1);
    }
}

  메인이 printPlus1을 호출하며 5를 넘김

  -> x=5가 대입되고, println문에서 5+1을 계산 후 6 출력

4) 반환X, 입력X

mf.printMath(); // "What a wonderful Math!"

class Mathfunc {
    void printMath() {
        System.out.println("What a wonderful Math!");
    }
}

  메인에서 printMath 호출

  -> println문에서 문구 출력

>> 메서드의 실행 흐름: 메서드 선언 -> main에서 객체 생성 -> 메서드 호출 -> 메서드 구현부 실행 -> 반환타입에 따라 값 반환 or 구현부 실행 끝 -> main으로 돌아옴

 

[쿠키글] 리팩터링(refactoring)

코딩을 하다 보면 처리는 간단한데 일일이 쓰기 귀찮은 작업이 생긴다. 그럴 때 쓰라고 만들어둔 것이 메서드이다.

예를 들어, 0부터 9 사이의 숫자 중 랜덤으로 2개를 골라 그 정수를 각각 십의 자리와 일의 자리로 하는 정수 값이 필요하다고 해보자. 그럼 아래처럼 메서드 하나를 간단하게 만들 수 있다.

int into2digits(int x, int y) {
    int result = 10*x + y; 
    return result;
}

십의 자리와 일의 자리 숫자를 받으면 그 2자리 수의 값을 반환하는 메서드이다. 지금도 충분히 간단해 보이지만, 더 간단하게 만들 수도 있다.

int into2digits(int x, int y) {
    return 10*x + y;
}

이렇게까지 할 일인가 싶지만, 절대 무시할 수 없는 작업이다!

리팩터링 전에는 입력받기 - 변수 선언 - 계산 - 변수에 저장 - 해당 변수에 저장된 값 불러오기 - 반환의 6가지 과정을 거쳐야 한다면, 리팩터링 후에는 입력받기 - 계산 - 반환의 3가지 과정만 거치면 되기 때문에 여러 번 메서드 호출이 일어나게 되면 훨씬 빠른 계산이 가능하다.

너무 간단한 작업이긴 하지만, 메서드 예시에 나왔던 add, findBigger, sub메서드도 리팩터링을 한다면 다음과 같이 된다.

int add(int x, int y) {
    return x + y;
}

int findBigger(int x, int y) {
    return x>y ? x : y;
}

double sub(double x, double y){
    return x - y;
}

 

'Java' 카테고리의 다른 글

메서드의 매개변수  (0) 2022.03.06
메서드의 호출 스택  (0) 2022.03.05
선언 위치에 따른 변수의 종류  (0) 2022.03.03
클래스의 정의  (0) 2022.03.02
객체 배열  (0) 2022.03.01