[혼공자] 06-4. 메소드

Benjamin·2023년 3월 6일
0

혼공자

목록 보기
18/27

06-4. 메소드

메소드는 객체의 동작에 해당하는 중괄호 {} 블록을 말한다.
중괄호 블록 이름이 메소드 이름이며, 메소드를 호출하면 중괄호 블록에 있는 모든 코드들이 일괄적으로 실행된다.

메소드 선언은 선언부와 실행 블록으로 구성된다.
메소드 선언부를 메소드 시그너처라고하며, 선언부와 실행블록에는 다음 요소를 포함한다.

  • 리턴타입 : 메소드가 리턴하는 결과의 타입
  • 메소드 이름 : 메소드의 기능이 드러나도록 식별자 규칙에 맞게 이름 명명
  • 매개변수 선언 : 메소드를 실행할 때 필요한 데이터를 받기위한 변수
  • 메소드 실행 블록 : 실행할 코드
리턴타입 메소드이름([매개변수선언,...]) {
	실행할 코드 작성 //메소드 실행 블록 
}

메소드 선언

선언부(리턴타입, 메소드 이름, 매개변수 선언)와 실행블록으로 구성된다.

리턴 타입

리턴값의 타입을 말한다.
리턴값이란 메소드를 실행한 후 결과값을 말한다.
메소드는 리턴값이 있을 수도 있고 없을 수도 있으나 리턴값이 있을 경우 리턴 타입이 선언부에 명시되어야한다. 리턴값이 없으면 리턴타입에 void로 선언한다.

void powerOn() {...}
double divide(int x, int y) {...}

리턴값이 있으냐 없느냐에 따라 메소드를 호출하는 방법이 다르다.

powerOn();
double result = divide(10,20);

powerOn()메소드는 리턴값이 없어서 변수에 저장할 내용이 없다.
단순히 메소드만 호출하면 된다.
그러나 리턴값이 있으면 이것을 저장할 변수가 있어야한다. 해당 변수를 리턴타입으로 선언해야한다.

위 코드에서 만약 result를 int타입으로 선언하면 double값을 int 타입에 저장할 수 없으므로 컴파일 에러가 발생한다.

리턴타입이 있다고해서 반드시 리턴값을 변수에 저장할 필요는 없다.
리턴값이 중요하지 않고 메소드 실행이 중요할 경우 다음과 같이 변수를 선언하지않고 메소드를 호출할 수도 있다.
divide(10,20,);

메소드 이름

자바 식별자 규칙에 맞게 작성하면 되는데, 다음 사항에 주의해야한다.

  • 숫자로 시작하면 안되고, $,_를 제외한 특수문자를 사용하면 안된다.
  • 관례적으로 메소드 이름은 소문자로 작성한다.
  • 서로 다른 단어가 혼합된 이름이면 뒤이어 오는 단어의 첫 글자는 대문자로 작성한다.

메소드 이름은 이 메소드가 어떤 기능을 수행하는지 쉽게 알 수 있도록 기능 이름으로 지어주는것이 좋다.
메소드 이름의 길이는 프로그램 실행과는 무관하니 너무 짧게 주지않도록 한다.

매개 변수 선언

매개변수는 메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용된다.
매개변수가 필요한 경우가 있고 필요 없는 경우가 있다.

메소드 호출 시 넘겨준 매개값은 해당 위치의 매개 변수에 각각 저장되고, 이 매개변수들을 이용해서 메소드 블록을 실행한다.
이때 매개값은 반드시 매개 변수의 타입에 부합되는 값이어야한다.

divide() 메소드가 int 타입 매개변수를 갖고있다면 호출 시 매개값으로 int값이나 int 타입으로 변환될 수 있는 값을 넘겨주어야 한다.
double 타입을 넘기면 int 타입으로 변환될 수 없기떄문에 컴파일에러가 발생하지만, byte 타입을 넘기면 int 타입으로 자동 변환 되기때문에 컴파일 에러가 생기지 않는다.

package sec04.exam01;

public class Calculator {
	void powerOn() {
    	System.out.println("전원을 켭니다.");
    }
}

외부 클래스에서 Calculator 클래스의 메소드를 호출하기 위해서는 다음 예제와 같이 Calculator 객체를 생성하고 참조변수인 myCal를 이용해야한다.
myCal 변수에 도트(.)와 함께 '메소드이름(매개값, ...)'형태로 호출하면 메소드 블록이 실행된다.

package sec04.exam01;

public class CalculatorExample{
	public static void main(String[] args) {
    	Calculator myCal = new Calculator();
        myCal.powerOn();
    }
}

매개 변수의 개수를 모를 경우

메소드의 매개 변수는 개수가 이미 정해져 있는 것이 일반적이지만, 어떤 상황에서는 메소드를 선언할 때 매개 변수의 개수를 알 수 없는 경우도 있다.

해결책은 매개 변수를 배열 타입으로 선언하는것이다.
int sum1(int[] values) {}

sum1() 메소드를 호출할 때 배열을 넘겨줌으로써 배열의 항목 값들을 모두 전달할 수 있다.
배열의 항목수는 호출할 때 결정된다.

int values = {1,2,3};
int result = sum1(values);
int result = sum1(new int[] {1,2,3,4,5});

매개변수를 배열 타입으로 선언하면 메소드를 호출하기 전에 배열을 생성해야하는 불편한점이 있다.
그래서 배열을 생성하지 않고 값의 목록만 넘겨주는 방법도 있다.
다음과 같이 sum2() 메소드의 매개 변수를 ...를 사용해서 선언하면 메소드 호출 시 넘겨준 값의 수에 따라 자동으로 배열이 생성되고 매개값으로 사용된다.
int sum2(int ... values) {}
-> 다른 배열과 똑같이 values.length , values[2] 등 사용 가능
-> 매개변수의 타입이 배열임

...로 선언된 매개 변수의 값은 다음과 같이 메소드 호출 시 쉼표로 나열해주면된다.

int result = sum2(1,2,3);
int result = sum2(1,2,3,4,5);

...로 선언된 매개변수는 배열타입이므로 배열을 직접 매개값으로 사용해도 좋다.

int[] values = {1,2,3};
int result = sum2(values);
int result = sum2(new int[] {1,2,3,4,5});

리턴(return)문

리턴값이 있는 메소드

메소드 선언에 리턴 타입이 있는 메소드는 반드시 리턴문을 사용해서 리턴값을 지정해야한다.
만약 return 문이 없다면 컴파일 에러가 발생하고, return 문이 실행되면 메소드는 즉시 종료된다.
return 리턴값;

return문의 리턴값은 리턴 타입이거나 리턴타입으로 변환될 수 있어야한다.

int plus(int x, int y) {
	byte result = (byte) (x+y);
    return result;
}
  • return문 이후의 실행문
    return 문 이후의 실행문은 결코 실행되지 않는다.
    return 이후에 실행문이 오면 "Unreachable code"라는 컴파일 에러가 발생한다.
    따라서 다음은 잘못된 코딩이다.
int plus(int x, int y) {
	int result = x+y;
    return result;
    System.out.println(result); // Unreachable code
}

하지만 다음의 경우에는 컴파일에러가 발생하지 않는다.

boolean isLeftGas() {
	if(gas == 0) {
    	System.out.prinltn("gas가 없습니다."); //1번
        return false;
    }
    System.out.prinltn("gas가 있습니다."); // 2번
    return true;
}

'2번'은 return false 다음에 있지만, if문의 조건식이 false일 경우 정상적으로 '2번'이 실행되기때문에 이는 "Unreachable code"에러를 발생시키지 않는다.
if문의 조건식이 true가 되면 '1번'이 실행되고 return false가 실행되거 메소드는 즉시 종료한다. 이 경우는 당연히 '2번'은 실행되지 않는다.

리턴값이 없는 메소드 : void

리턴값이 없는 메소드는 리턴 타입으로 void를 사용한다.
그런데 void로 선언된 메소드에서도 return문을 사용할 수 있다. 이것은 리턴값을 지정하는 것이 아니라 메소드 실행을 강제 종료시키는 역할을 한다.
return;
다음은 gas 값이 0보다 클 경우 계속해서 while문을 실행하고, 0일 경우 return문을 실행해서 run() 메소드를 즉시 종료한다.
이 예제에서는 return문 대시 break문을 사용할 수 있다.
차이점은 while문 뒤에 실행문이 추가적으로 더 있을 경우, break문을 반드시 사용해야한다.
return문은 즉시 메소드를 종료시키기 때문이다.

void run() {
	while(true) {
    	if(gas>0) {
        	System.out.println("달립니다.(gas 잔량:" + gas + ")");
            gas -= 1;
        } else {
        	System.out.println("달립니다.(gas 잔량:" + gas + ")");
            return; //run() 메소드 실행 종료 
        }
	}
}

메소드 호출

메소드는 클래스 내/외부의 호출에 의해 실행된다.
클래스 내부의 다른 메소드에서 호출할 경우에는 단순 메소드 이름으로 호출하면 되지만, 클래스 외부에서 호출할 경우에는 우선 클래스로부터 객체를 생성한 뒤 참조 변수를 이용해 메소드를 호출해야한다.
객체가 존재해야 메소드도 존재하기때문이다.

객체 내부에서 호출

클래스 내부에서 다른 메소드를 호출할 경우에는 다음과 같은 형태로 작성한다.
메소드가 매개 변수를 갖고있을 때는 매개 변수의 타입과 수에 맞게 매개값을 제공한다.
메소드(매개값,...);

public class ClassName {
	void method(String p1, int p2) {
     //...
    }
    
    void method2() {
    	method1("홍길동", 100);
    }
}

메소드가 리턴값이 없거나 있어도 받고싶지 않을 경우 위와 같이 모두 호출 가능하다.
리턴값이 있는 메소드를 호출하고 리턴값을 받고 싶다면 변수를 선언하고 리턴값을 대입한다.
타입 변수 = 메소드(매개값, ...);
이떄 변수 타입은 메소드 리턴 타입과 동일하거나, 자동 타입 변환이 될 수 있어야 한다.

객체 외부에서 호출

외부 클래스에서 메소드를 호출하려면 클래스로부터 객체를 생성해야한다.
메소드는 객체에 소속된 멤버이므로 객체가 존재하지 않으면 메소드도 존재하지 않기 때문이다.
클래스 참조변수 = new 클래스(매개값, ...);
객체가 생성되었다면 참조 변수와 함께 도트(.)연산자를 사용해 메소드를 호출할 수 있다.
도트(.)연산자는 객체 접근 연산자로 객체가 가지고있는 필드나 메소드에 접근할 때 사용된다.

참조변수.메소드(매개값,...); //리턴값이 없거나, 있어도 리턴값을 받지 않을 경우
타입 변수 = 참조변수.메소드(매개값,...); //리턴값이 있고, 리턴값을 받고싶을 경우

메소드 오버로딩

클래스 내에 같은 이름의 메소드를 여러 개 선언하는것을 메소드 오버로딩이라고한다.
오버로딩의 사전적 의미는 많이 싣는 것을 뜻한다.
하나의 메소드 이름으로 여러 기능을 담는다하여 붙여진 이름이다.
메소드 오버로딩의 조건은 매개변수 타입, 개수, 순서 중 하나가 달라야한다는 것이다.

메소드 오버로딩이 필요한 이유는 매개값을 다양하게 받아 처리할 수 있도록 하기 위함이다.

예를 들어 다음과 같은 plus()메소드가 있다고 가정하자.

int plus(int x, int y) {
	int result = x+y;
    return result;
}

이를 호출하기 위해서는 2개의 int타입 매개값이 필요하다.
하지만 double타입의 값을 덧셈하기 위해서는 plus()메소드를 호출할 수 없다.
해결방법은 double 타입으로 선언된 plus()메소드를 하나 더 선언하는 것이다.

double plus(double x, double y) {
	double result = x+y;
    return result;
}

오버로딩된 메소드를 호출할 경우 JVM은 매개값의 타입을 보고 메소드를 선택한다.

그렇다면 다음 코드는 어떻게 될까? 컴파일 에러가 날까? 만일 실행된다면 어떤 메소드가 실행될까?

int x = 10;
double y = 20.3;
plus(x,y);

plus(double x, double y)메소드가 실행된다.
JVM은 일차적으로 매개변수 타입을 보지만, 매개 변수의 타입이 일치하지 않을 경우 자동 타입 변환이 가능한지 검사한다.
첫번째 매개변수가 int에서 double로 자동 타입 변환 가능하므로 최종적으로 plus(double x, double y)메소드가 선택된다.

메소드를 오버로딩할 때 주의할 점은 매개변수 타입과 개수, 순서가 똑같을 경우 매개변수 이름이 다르다고해서 이것을 메소드 오버로딩이라고 하지 않는다는 것이다.
또한 리턴타입만 다르고 매개 변수가 동일하다면 이것도 오버로딩이 아니다.
왜냐하면 리턴타입은 JVM이 메소드를 선택할 때 아무런 도움을 주지 못하기 때문이다.

다음과 같이 선언하면 오버로딩이 아니기때문에 컴파일 에러가 발생한다.

int divide(int x,int y) {...}
double divide(int boonja, int boonmo){...}

메소드 오버로딩의 가장 대표적인 예는 System.out.println()메소드이다.

println()메소드는 호출할 때 주어진 매개값의 타입에 따라 다음과 같이 오버로딩된 println()메소드 중 하나를 호출한다.

void println() {...}
void println(boolean x) {...}
void println(char x) {...}
void println(char[] x) {...}
void println(double x) {...}
void println(float x) {...}
void println(int x) {...}
void println(long x) {...}
void println(Object x) {...}
void println(String x) {...}

출처
혼자 공부하는 자바

0개의 댓글