본문 바로가기

Java/기본 이론

[JAVA] Chapter 17. 함수와 메소드

1. 함수(function)와 메소드(Method)의 정의

함수는 특정 기능을 제공하기 위한 프로그램의 모듈을 말한다.

함수는 전달된 값에 대해 동작을 수행하고 값을 도출한다.

함수는 동적인 행동을 부여하는 것으로 메소드와 기능적인 차이에 구별을 두기는 힘들다.

하지만 메소드는 클래스의 포함 대상이며, 클래스는 필드와 메소드로 나뉜다.

즉, 메소드를 호출하려면 해당 클래스를 먼저 참조하고 정의한 상태여야 한다. 

 

쉽게 말해, 함수는 클래스에 포함되지 않아도 실행이 가능하고 기능을 하는 명령문이고, 메소드는 클래스에 포함된 명령문의 집합을 말한다.

때문에 메소드는 객체를 대상으로 하며, 메소드 자신 또한 객체로 취급되어 객체와 같은 특성을 가진다. 

 

메소드는 만들어져 있는 것을 사용하기도 하고, 직접 만들기도 한다.

우리가 사용하는 Scanner나 System.out.print 가 대표적인 메소드에 해당한다.

Java의 컴파일러 안에 저장된 System 안에, out 안에, print라는 메소드를 호출하는 것이다.  

 

메소드의 용이한 점은 반복되는 연산을 정의하는 것인데, 이렇게 연산을 정의하면 동일 클래스 내에서 해당 연산을 리턴하여 복잡한 연산식을 재차 적지 않고도 사용할 수 있다, 

 

메소드 타입 메소드명(파라미터리스트){

실행문;

}

int add(int a, int b){
int c = 0; 

c= a+b;

return c; // 반환값}

 

2. 메소드 정의 - 데이터 타입

메소드는 반환(return)하는 값에 따라 함수 타입을 지정한다.

함수 타입은 변수 명과 동일하다. 

메소드가 int를 반환한다면 메소드타입은 int 로 지정해야한다.

변수의 형식과 메소드의 형식이 동일해야 하며, 이는 ‘반환’하는 경우, 즉 Return하는 경우에만 적용된다. 

만약 메소드가 반환하는 값이 없다면 void로 할당한다. 

 

3. 파라미터(매개변수)

파라미터(매개변수)는 함수 호출 시, 넣어주는 값을 저장하기 위한 변수를 말한다. 

파라미터(매개변수)는 변수명 규칙과 동일하게 적용된다.

괄호안에 위치하는 파라미터는 항상 있는 것이 아닌데, 함수가 연산을 실행하는데 파라미터를 중심으로 연산하지 않거나 연산 자체가 없는 함수라면 따로 매개변수가 필요하지 않기 때문이다. 

때문에 파라미터는 필요한 경우에만 필요한 개수만큼 할당한다.

파라미터를 지정할 때는 변수 선언과 동일하게 타입과 이름을 명시해야 한다. 

 

public String say()
{
	return "Hi";
} // 해당 함수는 연산이 필요없어 매개변수가 필요 없다.

public int sum(int a, int b)
{	
	return a+b;
} // 매개변수와 리턴형이 모두 int 타입이다.

public void sum(int a, int b) 
{
	System.out.println(a+"과 "+b+"의 합은 "+(a+b)+"입니다.");
} //리턴값이 없다.

 

아래와 같이 배열을 반환할 수 있다. 아래의 내용은 배열을 생성하는 메소드이다. 

 

static int sum(int[] a) 
{
	int b = 0; 
	for (int i = 0; i< a.length; i++) 
	{
		b = b + a[i];
	}
	return b;
}

 

4. 메소드 호출 

메소드는 main 메소드 밖에, Class 안에 위치한다.

Java는 클래스를 벗어나면 해당 정보를 호출할 수 없기 때문에 메소드 호출은 Class를 벗어날 수 없으며, Class가 다른 곳에 위치한 함수를 호출할 수 없다.

때문에 메소드 호출은 main()이나 다른 함수 안에서 할 수 있다. 

 

int add(int a, int b){
	int c = 0; 
	c= a+b;
	return c; // 반환값
}

//이를 메인에서 호출한다면 아래와 같은 형태이다. 

add(5, 6) 

 

위의 예에서 add() 함수가 호출될 때, argument(전달인자) 값이 복사되어 함수에 전달된다.

함수는 해당 값을 연산하여 결과 값을 호출한 영역으로 다시 돌려준다. 

5. 메소드의 종료 

기본적으로 return 을 만나면 메소드가 종료된다.

이는 값을 반환하는 특성때문에 이후 연산이 의미가 없기 때문이다.

개발자는 이를 이용하여 일부 함수를 강제로 종료하기도 한다.

 

모든 메소드는 return 을 반드시 1개만 한다. 때문에 다수의 값을 반환하려면 배열로 지정해서 보내야 한다. 

쉽게 말해 데이터 자체가 항산 1개만 반환되기 때문에, 2개 이상의 데이터를 반환할 경우, heap 영역에서 참조할 수 있는 배열을 만들어 반환한다.

배열 데이터는 heap 영역에 저장되어 method를 벗어나도 호출이 가능하기 때문이다.

 

6. 생성자 (constructor)

6.1. 생성자가 필요한 이유: 인스턴스 변수의 초기화

클래스에 객체를 생성하면 객체는 메모리에 즉시 생성된다. 

이렇게 생성된 객체는 초기값을 가진 상태로 메모리에 할당된다. 

초기값은 자동으로 할당되며 대부분 0 혹은 null 등으로 할당된다.

 

객체가 가지는 초기값은 기능 구현에 필요할 수 있어 개발자가 임의할당하거나 초기화하 하여야 한다. 

다만, 만약 개발자가 변화한 변수에 초기값을 설정하고 싶을지라도 객체 데이터의 접근 권한에 따라 직접 변경이 불가능할 수 있다.

 

private 에 포함된 변수는 접근 권한에 따라 접근이 불가능할 수 있다.

이에 해당 객체에서 변수의 초기값을 수정하기 위한 메소드가 필요하다. 

이러한 초기화만을 위한 메소드는 객체가 생성된 후부터 사용되기 전까지 인스턴스 변수의 초기화를 위해 호출되어야 한다. 

 

6.2. 생성자 

자바에서는 객체의 생성과 동시에 인스턴스 변수를 원하는 값으로 초기화할 수 있는 생성자 메소드를 제공한다. 

생성자의 이름은 해당 클래스와 같아야 한다. 

 

생성자는 반환값이 없지만, 반환 타입을 void로 선언하지 않는다. 

생성자는 초기화를 위해 테이터를 인수로 전달받을 수 있다. 

객체를 초기화하는 방법이 여러개 존재한다면 하나의 클래스가 여러 개의 생성자를 가지기도 한다. 이는 생성자가 하나의 메소드이기 때문이다.

 

6.3. 생성자의 선언 

자바에서 클래스 생성자를 선언한다면 다음 과 같다. 

 

클래스이름() { ... }                  // 매개변수가 없는 생성자 선언

클래스이름(인수1, 인수2, ...) { ... } // 매개변수가 있는 생성자 선언

 

Bbang(String name, String flavor, int amoun, int price) 
{
    this.name = name; 
    this.flavor = flavor; 
    this.amount = 0; 
    this.price = 0;
}

위와 같이 변수의 데이터를 초기화하는 생성자를 선언할 수 있다.

 

6.4. 생성자의 호출

class Bbang 
{
    private String name;
    private String flavor;
    private int amount;
    private int price;

    Bbang (String name, String flavor, int amount) 
    {
        this.name = name;
        this.flavor = flavor;
        this.amount = amount;
        this.price = price;
    }
    
    public String BbangStore() 
    {
        return this.flavor + "맛 " + this.name + " " + this.amount + "개 " +;
    }
}

public class BuyBbang 
{
    public static void main(String[] args) 
    {
        Bbang bungerbbang = new Bbang ("붕어빵", 슈크림, 5); // 생성자의 호출
        System.out.println(bungerbbang.BbangStore()); // 생성자에 의해 초기화되었는지를 확인함.
    }
}

 

6.5.기본 생성자(default constructor)

자바는 기본으로 제공하는 생성자를 가지고 있다.

생성자는 컴파일러에 포함된 기능이다.

기본 생성자는 매견변수를 하나도 가지지 않고, 아무런 명령어도 포함하고 있지 않다.

 

자바 컴파일러는 컴파일시 "클래스 이름(){}"의 형식으로 기본 생성자를 추가한다.

이러한 기본 생성자를 이용하면 class에서 변화한 변수의 값을 초기화할 수 있다. 

 

class Bbang {
    private String name = "붕어빵";
    private String flavor = "단팥";
    private int amount = 3;
    private int price = 1000;
	
    public String BbangStore() 
    {
        return this.flavor + "맛 " + this.name + " " + this.amount + "개 " +;
    }

	public class BuyBbang2 
    {
    public static void main(String[] args) 
    {
        Bbang bungerbbang = new Bbang();             // 기본 생성자의 호출
        System.out.println(bungerbbang.BbangStore()); // 단팥맛 붕어빵 3개
    }
}

 

위와 같이 이미 생성한 클래스를 기본 생성자를 통해 초기화할 수 있다.

다만 매개변수를 가지는 생성자가 정의된다면 기본 생성자는 자동으로 생성되지 않는다. 

매개변수를 가지는 생성자가 이미 정의된 상태라면 해당 매개변수를 입력하여 해당하는 생성자를  호출해야 한다.

 

만약 기본 생성자를 사용하다 오류가 발생한다면 위와 같은 상황일 가능성이 높다. 

 

7. this와 this()

7.1. this 참조 변수

this 참조 변수는 인스턴스가 바로 자기 자신을 참조하는 데 사용하는 변수이다. 

쉽게 말해 자신이 속한 영역안에서 this를 사용하면 자기 자신을 가르키며, 클래스를 대상으로 사용하면 해당 클래스 자신안에서 데이터를 지정할 수 있다.

 

class Bbang 
{
    private String name;
    private String flavor;
    private int amount;
    private int price;

    Bbang (String name, String flavor, int amount) 
    {
        this.name = name;
        this.flavor = flavor;
        this.amount = amount;
        this.price = price;
    }
}

 

자바에서는 this 참조 변수를 사용하여 인스턴스 변수에 쉽게 접근이 가능하다.

모든 인스턴스 메소드에는 this 참조 변수가 숨겨진 지역 변수로 존재하고 있다.

다만, this 참조 변수는 인스턴스 메소드에서만 사용할 수 있으며, 클래스 메소드에서는 사용할 수 없다.

 

지역 변수 및 인스턴스가 기억나지 않는다면 chapter 16을 참고하자. 

 

7.2. this() 메소드

this() 메소드는 생성자 내부에서만 사용할 수 있고, 같은 클래스의 다른 생성자를 호출할 때 사용한다.

this() 메소드에 인수를 전달하면, 생성자 중에서 메소드 파라미터가 일치하는 다른 생성자를 찾아 호출해 준다.

 

class Bbang 
{
    private String name;
    private String flavor;
    private int amount;
    private int price;

    Bbang (String name, String flavor, int amount) 
    {
        this.name = name;
        this.flavor = flavor;
        this.amount = amount;
        this.price = price;
    }
    Bbang()
    {
    	this(붕어빵, 단팥, 3); // 다른 생성자를 호출함.
    }
    public String BbangStore() 
    {
        return this.flavor + "맛 " + this.name + " " + this.amount + "개 " +;
    }
}

public class Method05 
{
    public static void main(String[] args) 
    {
        Bbang danpat= new Bbang(); 
        System.out.println(danpat.BbangStore());
    }
}

// 결과: 단팥맛 붕어빵 3개

 

예제에서 매개변수를 가지는 첫 번째 생성자는 this 참조 변수를 사용하여 인스턴스 변수에 접근하고 있다.

또한, 매개변수를 가지지 않는 두 번째 생성자는 내부에서 this() 메소드를 이용하여 첫 번째 생성자를 호출한다.

이렇게 내부적으로 다른 생성자를 호출하여 인스턴스 변수를 초기화할 수 있다.

 

9. 메소드 오버로딩

메소드 오버로딩(overloading)이란 같은 이름의 메소드를 중복하여 정의하는 것이다.

자바에서는 원래 한 클래스 내에 같은 이름의 메소드를 둘 이상 생성할 수 없다.

하지만 매개변수의 개수나 타입을 다르게 하면, 하나의 이름으로 메소드를 작성할 수 있다.

쉽게 말해 메소드 오버로딩이란 파라미터르를 다르게한 동일한 이름의 메소드를 다수 생성하는 것이다.

 

메소드 오버로딩의 장점은 메소드에 사용되는 이름을 정약할 수 있다는 것이다.

예를 들어 전체 합을 구하는 메소드를 정의한다고 하자. 

동작은 기본적으로 파라미터 리스트(매개변수 리스트; == 시그니처)에 따라 입력된 값들을 더하는 것이다. 

메소드 오버로딩을 사용하면 이 메소드를 Sum으로 정하고 파라미터를 다르게하여 정수의 합, 소수의 합, 배열 구성요소의 합을 만들수 있다는 것이다.

 

메소드 오버로딩은 메소드 이름이 같고, 파라미터 리스트(시그니처)가 다르면 선언할 수 있다. 

이름이 다르면 별개의 메소드이며, 이름이 같고 메소드가 같으면 같은 메소드를 중복하여 생성한 것이다. 

또한 반환값이 다른 경우도 별개의 메소드로 이해한다. (반환 값이 다르다는 것은 동일한 동작으로 볼 수 없기 때문이다.)

 

10. 재귀 메소드

메소드 내에서 자신을 호출하는 메소드를 말한다.

반복 동작을 구현하기 편하나 동시에 많은 메소드가 호출되어 스택 메모리의 사용량이 크게 늘고 부하를 줄 수 있다.

반복문과 유사하지만, 변화하는 값을 할당하지 않으면 무한반복한다.

때문에 명령문에 변화하는 값을 설정해야 한다. 

 

static int method(int x){

    int a = 0; 
	if( x == 1)
 	{
  		a = 1;
	}
	else
    {
		a = x*(method(x-1));
	}
	return a;
}

 

재귀 호출은 반복문으로 대처가 가능하지만, 이 재귀 호출을 이용하면 '알고리즘'의 반복문을 만들 수 있다.

알고리즘을 구성할때마다 반복문을 사용한다면 리소스 낭비이고 수정이 어려울 수 있다.

때문에 재귀호출을 사용하면 복잡한 알고리즘을 몇개의 메소드로 구축할 수 있다는 장점을 가진다.