오버로딩, 오버라이딩

박영준·2022년 12월 15일
1

Java

목록 보기
32/111

오버로딩 (Overloading)

1. 정의

  • Overloading = "과적하다"

  • 자바의 한 클래스 내에
    이미 사용하려는 이름과 같은 이름을 가진 메소드가 있더라도,
    매개변수의 개수 or 타입이 다르면
    같은 이름을 사용해서 메소드를 정의 O

2. 사용 조건

  • 메서드의 이름이 같고,
    매개변수의 개수 or 타입 or 순서가 달라야 한다.

  • 주의

    • '응답 값만' 다른 것은 오버로딩 불가능
    • 접근 제어자만 다른 것도 오버로딩 불가능
  • 결론

    • 오버로딩은 매개변수의 차이로만 구현 가능

3. 사용법

1) 매개변수의 개수가 다른 경우

public int overloadTest() {
	return 0;		// 매개변수의 개수: 0개
}

public int overloadTest(String test) {
	return 1;		// 매개변수의 개수: 1개
}

2) 매개변수의 타입이 다른 경우

public int overloadTest(String test) {		// 매개변수의 타입: String
	return 1;
}

public int overloadTest(int test) {		// 매개변수의 타입: int
	return 1;
}

4. 예시

예시 1

// Q. 다음의 add메서드를 바르게 오버로딩한 것은?
long add (int a, int b) {
	return a+b;
}

// A1.
long add (int a, long b) {		// 반환타입 long은 고려 대상 X
	return a+b;
}

// A2.
int add (int a, long b) {		// 반환타입 int는 고려 대상 X
	return a+b;
}

예시 2 : 메서드 오버로딩

public class MyClass {
	// 오버로딩 될 plus메서드
	public int plus (int x, int y) {
    	return x + y;
    }
    
    // 오버로딩 1 : 매개변수의 타입은 동일, 개수 다름
    public int plus (int x, int y, int z) {
    	return x + y + z;
    }
    
    // 오버로딩 2 : 매개변수의 타입은 다름, 개수는 동일
    public string plus (string x, string y) {
    	return x + y;
    }
    

}

예시 3 : 생성자 오버로딩

public class Car {
    String name;
    int number;

    public Car (String name) {
        this.name = name;
    }

    public Car() {

    }

    public Car (String name, int number) {
        this.name = name;
        this.number = number;
    }
}
public static void main(String[] args) {
	Car c1 = new Car("소방차");
	Car c2 = new Car();
	Car c3 = new Car("구급차", 111);
}

예시 4

public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable
{
			...
			
		public void println() {
        newLine();
    }

    public void println(boolean x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(char x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(int x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(float x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(char[] x) {
        if (getClass() == PrintStream.class) {
            writeln(x);
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(String x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(Object x) {
        String s = String.valueOf(x);
        if (getClass() == PrintStream.class) {
            // need to apply String.valueOf again since first invocation
            // might return null
            writeln(String.valueOf(s));
        } else {
            synchronized (this) {
                print(s);
                newLine();
            }
        }
    }
		  ...
}
  • 메서드 이름은 println() 으로 동일하지만,
    매개 변수의 타입이 모두 다르다.

5. 장점

  1. 메서드의 이름을 절약

    • 만약 오버로딩이 안된다면
      println()printlnInt(), printlnDouble() 처럼 메서드명이 길어지고 낭비 되었을 것이다
  2. 메서드 이름 하나로, 상황에 따른 동작을 개별로 정의 할 수 있다

    • println()로 메세지 출력 할 때, 매개변수로 int, double, String, boolean 등... 다양하게 넣을 수 있다
  3. 입력하는 값(매개변수)이 다르지만 같은 기능을 수행하는 경우가 많을 때, 사용성 및 효율을 높일 수 있다

오버라이딩 (overriding)

1. 정의

Point 클래스

clasa point {
	int x;
    int y;
    
    String getLocation() {
    	return x + y;
    }
}

Point3D 클래스

clasa Point3D {
	int z;
    
    String getLocation() {		// 오버라이딩 -> 상속받은 메서드를 자신에게 맞게 재정의했다(+ z 추가)
    	return x + y + z;
    }
}
  • overriding = "... 의 위를 덮다."

  • 부모 클래스로부터 상속받은 메서드의 내용을 변경(재정의)하여 사용하는 것
    → 부모로부터 받은 메소드의 로직을 원하는대로 변경
    → 다형성(객체지향 언어의 특징)

    주의!
    부모 메소드와 동일하거나 추가되어야 함.
    감소되면 X

@override 는 필수인가?
이 어노테잉션은 일종의 안전장치다.
어떤 라이브러리에 속한 클래스를 상속받아, 특정 메서드를 override 했다.

해당 라이브러리가 업데이트되면서 메서드의 시그네쳐가 바뀌는 경우,
이전엔 override 한 메서드가 새 버전에서는 그냥 새로 추가된 메서드로 인식될 뿐, 컴파일 오류가 발생하지 않기 때문에
개발자는 동작이 달라진 걸 알아채기 어렵다.
→ @override 이 없는 경우, 해당 메서드를 overriding 한 것인지 여부를 직접 상위 클래에 가서 찾아야 하는데
단순 추상 클래스(or 인터페이스) 하나만 있다면 상관없지만, 여러가지 상속 깊이가 있는 경우에는 바로 찾아서 확인하기도 힘들다.

이런 부류의 문제를 방지하기 위해 @Override 를 사용하는 것 (= 클래스 구조를 더 쉽게 인식 가능하다.)
→ 만약, @Override 이 붙은 메서드가 부모 메서드를 override 하지 않는다면, 컴파일 시 오류가 발생!

2) 사용 조건

  • 접근 제한 정도 : = 또는 ↓ (같거나 더 약하게)

  • 예외의 개수 : 조상 클래스 > 자식 클래스

    부모 클래스

    class Parent {
        void parentMethod() throws IOException, SQLException {		// 2개
            ...
        }
    }

    자식 클래스

    class Child extencs Parent {
        void parentMethod() throws IOException {		// 1개
            ...
        }
    }

3) 예시

예시 1

public class Parent {

    public void overridingTest() {
        System.out.println("부모 메서드의 내용");
    }
}

// Child 클래스(자식)가 Parent 클래스(부모)의 overridingTest()메서드를 상속받아 재사용
class Child extends Parent{
 
 	// 메서드의 선언부는 조상의 메서드와 일치
    // 메서드의 내용만 재정의하여 사용
    @Override
    public void overridingTest() {
        System.out.println("상속받은 부모메서드의 내용을 수정하여 자식메서드의 내용으로 재사용");
    }
}

부모 클래스

public class Parent {
    public void method1() {
        System.out.println("Parent-method1()");
    }

    public void method2() {
        System.out.println("Parent-method2()");
    }
}

자식 클래스

public class Child extends Parent {

	// 부모 클래스의 메소드 재정의
    @Override       
    public void method2() {
        System.out.println("Child-method2()");
    }

    public void method3() {
        System.out.println("Child-method3()");
    }
}

ChildExample

public class ChildExample {
    public static void main(String[] args) {
        Child child = new Child();
        
        Parent parent = child;      // 자동 타입 반환
        
        parent.method1();
        parent.method2();       // 부모 클래스가 아닌, 자식 클래스에서 재정의된 메소드가 호출됨
        parent.method3();       // 호출 불가능
    }
}

원칙 : 부모 타입으로 자동 타입 변환 된 이후 --> 부모 클래스의 필드, 메소드만 접근 가능
예외 : 자식 클래스에 메소드 재정의를 한다면, 자식 클래스의 메소드가 대신 호출됨 ('다형성'과 관련)

참고: 상속 - 8) 다형성

예시2

public class Car {
	public void run() {
    	System.out.println("Car의 run메서드")
    }
}
public class Bus extends Car {		// 자식 클래스 Bus
	public void run() {
    	super.run();		// 부모 클래스 Car의 메서드run 호출
        System.out.println("Bus의 run메서드")
    }
}

/* 실행 결과
Car의 run메서드
Bus의 run메서드
*/
  • super 키워드를 사용하면, 자식클래스에서 부모클래스의 메서드를 호출 가능

오버로딩 vs 오버라이딩

  • 오버로딩 : 기존에 없는 새로운 메서드를 추가 new

  • 오버라이딩 : 상속받은 메서드 내용을 변경 modify change


상속

참고: 상속


참고: 오버로딩(Overloading)과 오버라이딩(overriding) 차이
참고: [Java] 오버로딩 & 오버라이딩 차이 비교 정리(오버로딩, 오버라이딩 차이점)
참고: 오버로딩과 오버라이딩 차이와 예제

profile
개발자로 거듭나기!

0개의 댓글