본문 바로가기
Java/Basic

오버로딩과 오버라이딩

by 최로이 2021. 3. 25.

자바 관련된 기본 면접 질문란을 보면 항상 단골처럼 나오는 "오버로딩과 오버라이딩의 차이가 뭔가요?"의 주인공들이다. 사실 둘은 비슷해보이지만 완전히 다르다.

 

1. 오버로딩(재정의)

메소드도 변수와 마찬가지로 같은 클래스 내에서 서로 구별될 수 있어야 하기 때문에 각기 다른 이름을 가져야 한다. 그러나 자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메소드가 있더라도 매개변수의 개수 또는 타입이 다르면 같은 이름을 사용해 메소드를 정의할 수 있다.

즉, 한 클래스 내에 같은 이름의 메소드를 여러개 정의하는 것을 메소드 오버로딩 또는 오버로딩이라 한다.

 

1.1 오버로딩의 충족 조건

① 메소드명이 같아야 한다.
② 매개변수의 개수 또는 타입이 달라야 한다.

두 가지 조건을 만족하지 못하는 메소드는 중복 정의로 간주되어 컴파일 시에 에러가 발생한다. 그리고 오버로딩된 메소드들은 매개변수에 의해서만 구별될 수 있으므로 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.

 

대표적인 오버로딩의 예로는 println메소드가 있다. println메소드는 괄호 안에 값만 지정해주면 화면에 출력하는데 아무런 어려움이 없다. 하지만 실제로는 println메소드를 호출할  매개변수로 지정하는 값의 타입에 따라 호출되는 println메소드가 달라진다.

 

오버로딩의 예제 1)

class OverloadingTest {
	public static void main(String args[]) {
		MyMath3 mm = new MyMath3();
		System.out.println("mm.add(3, 3) 결과:"    + mm.add(3,3));
		System.out.println("mm.add(3L, 3) 결과: "  + mm.add(3L,3));
		System.out.println("mm.add(3, 3L) 결과: "  + mm.add(3,3L));
		System.out.println("mm.add(3L, 3L) 결과: " + mm.add(3L,3L));

		int[] a = {100, 200, 300};
		System.out.println("mm.add(a) 결과: " + mm.add(a));
   }
}

class MyMath3 {
	int add(int a, int b) {
		System.out.print("int add(int a, int b) - ");
		return a+b;
	}
	
	long add(int a, long b) {
		System.out.print("long add(int a, long b) - ");
		return a+b;
	}
	
	long add(long a, int b) {
		System.out.print("long add(long a, int b) - ");
		return a+b;
	}

	long add(long a, long b) {
		System.out.print("long add(long a, long b) - ");
		return a+b;
	}

	int add(int[] a) {		// 배열의 모든 요소의 합을 결과로 돌려준다.
		System.out.print("int add(int[] a) - ");
		int result = 0;
		for(int i=0; i < a.length;i++) {
			result += a[i];
		}	
		return result;
	}
}
--------------------------------------------------
int add(int a, int b) - mm.add(3, 3) 결과:6
long add(long a, int b) - mm.add(3L, 3) 결과: 6
long add(int a, long b) - mm.add(3, 3L) 결과: 6
long add(long a, long b) - mm.add(3L, 3L) 결과: 6
int add(int[] a) - mm.add(a) 결과: 600

 

1.2 오버로딩의 장점

① 명칭을 짧게 할 수 있고 오류의 가능성을 줄일 수 있다.

② 오버로딩 이름만 보고서도 해당 메소드들은 이름이 같으니 같은 기능을 제공하는 것을 예측할 수 있다.

③ 메소드 이름을 짓는데 하나의 이름으로 여러 개의 메소드를 정의할 수 있으므로 다른 메소드를 만드는 데 있어 시간을 절약할 수 있다.

 

 

1.3 가변인자(varargs)와 오버로딩

JDK1.5부터 매개변수 개수를 동적으로 지정해 줄 수 있게 되었으며, 이 기능을 '가변인자' 라고도 한다. 가변인자는 '타입... 변수명'과 같은 형식으로 선언한다.

public PrintStream printf(String format, Object... args) { ... }

위와 같이 가변인자 외에도 매개변수가 더 있다면, 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다. 그렇지 않으면 컴파일 에러가 발생한다. 가변인자인지 아닌지를 구별할 방법이 없기 때문에 허용하지 않는 거다.

public PrintStream printf(Object... args, String format) { } //컴파일 에러

 

예제 1)

class VarArgsEx {
	public static void main(String[] args) {
		String[] strArr = { "100", "200", "300" };
		
		System.out.println(concatenate("", "100", "200", "300"));
		System.out.println(concatenate("-", strArr));
		System.out.println(concatenate(",", new String[]{"1", "2", "3"}));
		System.out.println("["+concatenate(",", new String[0])+"]");
		System.out.println("["+concatenate(",")+"]");
	}

	static String concatenate(String delim, String... args) {
		String result = "";

		for(String str : args) {
			result += str + delim;
		}
		
		return result;
	}

/*
	static String concatenate(String... args) {
		return concatenate("",args);
	}
*/
} // class
------------------------
100200300
100-200-300-
1,2,3,
[]
[]

concatenate는 매개변수로 입력된 문자열에 구분자를 사이에 포함시켜 결합하여 반환한다. 가변인자로 매개변수를 선언했기 때문에 문장 개수를 제약없이 매개변수로 지정할 수 있다.

 


2. 오버라이딩(변경)

조상 클래스로부터 상속 받은 메소드의 내용을 변경하는 것이다. 보통 상속 받은 메소드를 그대로 사용하기도 하지만, 자식 클래스 스스로에게 맞게 변경해야 하는 경우가 많다. 이럴 때 부모의 메소드를 오버라이딩한다.

 

오버라이딩 간단한 예제)

class Parents {
  int x;
  int y;

  String getLocation(){
      return "x: "+x ", y: "+y;
    }
}

class Child extends Parents {
  int z;

  String getLocation(){
      return "x: "+x ", y: "+y+ ", z: "+z;
    }
}

 

위 코드를 보면 부모클래스를 상속한 뒤 메소드에 멤버변수에 대한 리턴값을 추가하여 사용하고 있다.

 

2.1 오버라이딩의 충족 조건

① 자식 클래스에서 오버라이딩 하려는 메소드는 부모 클래스의 메소드명과 일치해야 한다.
② 매개변수가 같아야 한다.
③ 반환타입이 같아야 한다.

간단히 정리해서 말하자면 메소드의 선언부가 서로 일치해야 한다는 의미다.

 

부모 클래스의 메소드를 오버라이딩 하기 위해 주의해야 할 사항

① 오버라이딩 하려는 부모 메소드의 예외 개수보다 작아야 한다.
② 부모 클래스의 메소드보다 접근제어자의 범위가 작아서는 안 된다.
③ 인스턴스 메소드를 static 메소드 또는 그 반대로 변경할 수 없다.

2.2 super

super는 자식 클래스에서 부모 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수다. 만약 상속받은 부모 멤버와 자식 클래스에 정의된 멤버명이 같을 때, super를 이용해 구별할 수 있다.

 

부모 클래스로부터 상속받은 멤버도 자식 클래스 자신의 멤버가 됨으로 super대신 this를 사용할 수 있다. 그래도 부모 클래스의 멤버와 자식 클래스의 멤버가 중복 정의되어 서로 구별해야하는 경우에는 super를 사용하는 것이 좋다.

 

예제1)

class SuperTest {
	public static void main(String args[]) {
		Child c = new Child();
		c.method();
	}
}

class Parent {
	int x=10;
}

class Child extends Parent {
	void method() {
		System.out.println("x=" + x);
		System.out.println("this.x=" + this.x);
		System.out.println("super.x="+ super.x);
	}
}
----------------------
//결과
x=10
this.x=10
super.x=10

x, this.x, super.x 모두 같은 변수를 의미하여 같은 값이 출력된다.

 

예제2)

class SuperTest2 {
	public static void main(String args[]) {
		Child2 c = new Child2();
		c.method();
	}
}
class Parent2 {
	int x=10;
}

class Child2 extends Parent2 {
	int x=20;

	void method() {
		System.out.println("x=" + x);
		System.out.println("this.x=" + this.x);
		System.out.println("super.x="+ super.x);
	}
}
----------------
//결과
x=20
this.x=20
super.x=10

같은 이름의 멤버변수가 부모 클래스에도, 자식 클래스에도 있기 때문에 위와 달리 super.x와 this.x는 서로 다른 값을 참조하게 된다. super.x는 부모 클래스로부터 상속 받은 멤버변수 x를 의미하고, this.x는 자식 클래스에 선언된 멤버변수를 의미한다.

 

2.3 super()

this()와 마찬가지로 super() 역시 생성자다. this()는 같은 클래스의 다른 생성자를 호출하는 데 사용되지만, super()는 부모 클래스의 생성자를 호출하는데 사용된다.

Object클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자 this() 또는 super()를 호출해야 한다. 그렇지 않을 경우 컴파일러가 자동적으로 super()를 생성자의 첫 줄에 삽입한다.

예제3)

class PointTest2 {
	public static void main(String argsp[]) {
		Point3D2 p3 = new Point3D2();
		System.out.println("p3.x=" + p3.x);
		System.out.println("p3.y=" + p3.y);
		System.out.println("p3.z=" + p3.z);
	}
}

class Point3 {
	int x=10;	
	int y=20;

	Point3(int x, int y) {

		this.x = x;
		this.y = y;
	}
}

class Point3D2 extends Point3 {
	int z=30;

	Point3D2() {
		this(100, 200, 300);	// Point3D2(int x, int y, int z)를 호출한다.
	}
	
	Point3D2(int x, int y, int z) {
		super(x, y);			// Point3(int x, int y)를 호출한다.
		this.z = z;
	}
}
-------------
//결과
p3.x=100
p3.y=200
p3.z=300

 

'Java > Basic' 카테고리의 다른 글

다형성(polymorphism)  (0) 2021.03.30
접근제어자(Access Modifier)와 제어자(Modifier)  (0) 2021.03.26
상속(Inheritance)  (0) 2021.03.24
변수의 초기화  (0) 2021.03.23
생성자(Constructor)  (0) 2021.03.23

댓글