해당 질문은, 함수의 매개값 전달 방식과 관련이 있다. Call by reference는 값의 주소 참조이고 비교되는 개념인 Call by value는 값의 자체 복사다. 코드로 예를 들면...
public class main {
public static void main(String[] args) {
int var = 1; // 원시 타입 변수 int
int[] arr = { 1 }; // 참조 타입 변수(배열)
// 값 자체 복사 (call by value)
copyValue(var);
System.out.println(var); // 1(값 변화 x)
// 배열 자체를 보냄 (call by reference)
referValue(arr);
System.out.println(arr[0]); // 101(값 변화)
}
static void copyValue(int var) {
var += 100;
}
static void referValue(int[] arr) {
arr[0] += 100;
}
}
이런 식으로, 원시 타입인 int
를 타입으로 가지는 변수를 함수의 매개값으로 전달하면 값 자체를 복사해서 매개변수로 전달하기 때문에 원본은 변화가 없다. 반대로 참조 타입인 배열을 타입으로 가지는 변수를 함수의 매개값으로 전달하면 값의 주소를 매개변수로 전달하기 때문에 원본 역시 영향을 받는다.
좀 더 상세히 들어가면, 스택 영역에는 변수의 값이 할당되고, 힙 영역에는 (참조) 변수의 주소가 할당된다. 이 주소는 스택 영역에 있는 참조 변수(들)이 공통으로 바라보고 있기 때문에 값의 주소 참조(Call by reference)가 일어나게 되는 것이다.
근데 사실 위의 예시는 내가 자바를 공부하기 때문에 자바로 코드를 짜고 예를 든 것 뿐이지. 실제 자바에서는 포인터를 철저하게 숨겼기 때문에 Call by reference가 존재하지 않는다. 즉, 단순하게 값을 복사하는지, 주소를 복사하는지에 대한 차이만 있을 뿐이다. 이를 통해서 값이 복사되면 바로 연산하고 주소를 복사하면 주소를 타고 들어가서 값을 가져와서 연산한다.
통상 초기값, 즉 원본의 불변성을 중요하게 생각하지 않을 때에는 Call by reference 개념을 기반으로 한 주소값 복사를 수행하는 것이 보편적이다.
다형성은 작게 보면은 단위 기능을 기반으로 여러 결과를 내놓는 것이고, 크게 보면 클래스 및 그 이상의 범위에서 여러 결과를 내놓는 것이라고 요약할 수 있다. 오버라이딩 및 오버로딩은 단위 기능에 해당할 수 있는 메소드를 바탕으로 다형성을 구현할 수 있는 기능이다.
public class UpStream {
public String method(String str) {
return "upstream " + str;
}
}
위와 같은 부모 클래스가 존재하고, 아래는 부모 클래스를 상속하는 자식 클래스다. 이때, method
메소드는 오버라이딩 돼서 DownStream
클래스에서 새로운 결과를 이끌어낼 수 있다. 이것이 오버라이딩이다.
public class DownStream extends UpStream {
@Override
public String method(String str) {
return "downstream " + str;
}
}
오버라이딩의 조건은 다음과 같다.
- 부모 메소드의 선언부(리턴 타입, 메소드 이름, 매개변수)와 동일해야 한다.
- 접근 제한을 더 강하게 오버라이딩 할 수 없다.
- 새로운 예외를 throws 할 수 없다.
메소드가 오버라이딩되면, 오버라이딩 당한 부모의 원본 메소드는 숨겨지는 것이 기본이다. 하지만, super 키워드를 사용하면 본래 부모의 원본 메소드를 불러올 수 있다.
@Override
public void methodExample(int parameter) {
super.methodExample(parameter); // 부모의 원본 메소드 호출
}
오버로딩은 동일 클래스 내에서의 메소드 혹은 생성자를 다양하게 두면서 여러 호출에 대응할 수 있는 기능이다. 오버로딩의 조건은 다음과 같다.
- 메소드 이름은 동일해야 한다.
- 리턴 타입은 동일하든, 다르든 상관 없다.
- 매개변수의 타입, 개수, 순서가 달라야 한다.
멀리 갈 것 없이, 대표적인 오버로딩의 예시가 System.out.println()
이다.
System.out.println("오버로딩");
System.out.println(1);
System.out.println(1.1);
// 메소드명은 println으로 동일하다.
// 리턴 타입은 상관없다.
// 매개변수의 타입이 다르다.