[Java] 재정의(Override)
💡 1. Override(재정의)
강아지.class 에게 먹다 기능 eat() 메서드는 "개처럼 먹다" 를 출력하고
고양이.class 에게 먹다 기능 eat() 메서드는 "고양이 처럼 먹다" 를 출력할 때
이 공통된 메서드를 animal(부모클래스)에서 eat을 공동 관리 했을때
강아지와 고양이 클래서에서 eat 메서드를 상속받아서 사용할 때 각 클래스 특징에 맞게
개처럼 먹다 와 고양이 처럼 먹다 를 출력할 수 있게 해주는게 Override ( 재정의) 라고 한다.
Override(재정의)
➡상속관계에서 상속 받은 하위 클래스가 상위 클래스의 동작을 수정하는 것
부모 클래스의 eat() 메서드는 dog나 cat 과는 다르게 ? 로 추상적으로 되어있다.
이때 dog 나 cat 클래스에서 상속받은 eat() 메서드를 재정의 하지 않으면 ? 라는 출력값이 그대로 나올 것이다.
포괄적이고 추상적인 부모의 결과물(메소드)을 상속받은 클래스에 맞게 사용하기 위해 재정의 한다.
💡 2. 재정의를 한 자식 객체를 생성할 때 2가지 방법
우리가 흔히 객체를 생성하는 방법이다.
Dog 라는 클래스 변수를 만들고 그 안에 클래스 생성자로 생성한 메모리 주소 값을 넣는 것이다.
이때의 Dog 클래스에서 만들어진 변수는 Dog 뿐만아니라
상속받은 Animal의 부분까지 모두 접근이 가능하다.(exntends)
클래서 Dog의 변수 d에서 eat 메서드를 실행시키면 부모것이 아닌 클래스 Dog의 d 의 메서드가 실행된다.
이러한 경우는 Dog 내부의 구조를 모두 알고 있는 경우에 사용 될 수있다.
⚡Override 중요 방법!
이 경우에는 부모 클래스인 Animal 클래스타입으로 변수를 선언하고
그 안에 Dog 객체 생성자로 만든 객체의 주소를 넣는다.(자동으로 형이 바뀐다)
이러한 방법을 upcasting 이라고 한다.
이때는 변수에 d 가 접근이 가능한 곳은 오로지 Animal 구역 뿐이다. Dog에는 접근이 불가능 하다.
하지만! eat() 이라는 메서드를 Animal에서 상속받아서 Dog에서 재정의를 했다면
Animal에 eat()을 실행 시키면 실행 시키는 순간 동적 바인딩으로 Dog에서 상속 받아 재정의 한 eat()메서드가 호출된다.
Animal의 eat에 처음 접근하다가 어? 내 자식 클래스가 eat()을 오버라이딩 했을까? 확인하고
오버라이딩이 되어 있으면 자식의 eat()메서드에 접근해서 실행시킨다.
🔍 동적바인딩 : 호출 될 메서드가 실행 시점에서 결정되는 바인딩.
프로그렘의 속도가 떨어지는 원인이 되지만 이점이 더 많기 때문에 사용한다.
이렇게 부모 클래스로 접근해서 동적바인딩을 통해 다른 클래스의 메서드를 실행시키는 방법을 통해서
모든 코드의 구조를 공개하지 않고 해당 클래스의 기능을 모두 사용할 수 있다는 점이 큰 이점이다.
💡 3. 코드로 알아보는 Override.
부모 클래스의 eat() 메소드가 ? 가 들어있는 상황에서
Dog 클래스와 Cat 클래스에 eat() 메소드가 override 재정의 되어 있지 않으면
똑같이 물음표가 출력된다.
Dog 클래스에 eat메소드가 없는 상태에서는 ? 가 출력된다.
물음표가 안나오게 하려면 Dog 클래스로 가서 재정의 해줘야 한다.
부모에 있는 method 를 자식 클래스에서 재정의(override) 한것이다.
이를 출력해보면 Dog에서 재정의한 값이 출력된다.
Dog d = new Dog 로 생성한 인스턴스 변수를 통해 d.eat() 메소드에 접근했을때
Anmail의 eat과 Dog 의 eat에 모두 접근이 가능하다. 하지만 Dog 클래스로 접근했기 때문에
Animal의 eat은 무시하고 Dog의 eat()이 출력되게 된다.
cat 같은 경우도 eat이 정의가 안됐을 경우 Anmail eat() 메소드의 출력값인 ? 가 출력될 것이다.
이를 cat 클래스에 가서 eat()을 출력해보면.
고양이 클래스에는 개인적인 메소드인 night() 외에는 eat 메소드가 없다.
이때 부모 클래스 메소드를 재정의를 쉽게하는 방법은
빈칸에 마우스 오른쪽 마우스를 눌러서 source 탭에 overide 가 있다.
그러면 다음과 같은 창이 뜨는데
부모 클래스인 Animal의 eat 메소드를 만들 수 있다.
이렇게 자동으로 완성된다. (//TODO 부분은 그냥 지우면 된다)
상속 받은 메소드를 클래스에 맞게 재정의를 하면 된다.
이렇게 재정의 한 값들을 통해서
각 클래스들의 재정의(Override) 메소드 들이 잘 출력되는 것을 확인 할 수있다.
이 경우에는 Animal 부모클래스를 사용하지 않았다.-> 상속의 이점을 살리지 못한 코드이다.
상속을 통한 객체 생성하기.
Dog와 Aninal 은 타입이 달라서 잘못됐다고 할 수 있지만
Anmal 과 Dog 가 상속관계이므로
부모(왼쪽) = 자식(오른쪽) 인 경우 자동 형 변환이 가능하다.
이를 다른말로 object casting 또는 upcasting 이라고 한다.
Animal 형을 갖은 ani 변수는 Animal 영역에만 접근이 가능하지만 상속을 한 시점에서
Dog에 상속받아 재정의 된 함수가 있으면 Animal의 메서드로 접근이 가능하다.
(컴파일 시점에는 Animal eat인데 실행후엔 Dog의 eat 에 접근이 가능하다)
Dog 클래스의 동작 방식을 전혀 알 수 없더라도 부모클래스인 Animal 만 있으면 Dog의 클래스를 구동시킬수 있다.
고양이 클래스도 마찬가지이다. Animal 클래스인 ani 로 작동해도 Cat의 eat이 작동된다.
Dog.java, Cat.java 파일이 아닌 Dog.class Cat.class 로 제공받아
하위 클래스 내부의 클래스 정보를 아예 모르더라도 상속을 해준 부모클래스를 통해서 구동이 가능하다.
upcasting 이 되기 때문에 가능한 것이다.
Cat만 가지고 있는 night() 메소드에 ani 를 통해서 접근이 가능할까???
ani 형태를 Cat으로 형변환( 즉 Down casitng) 을 통해서 night 에 접근이 가능하다.
강제로 바꾸는 것이다.( 강제형변환)
💡 4. 상속 다시한번 총 정리.
부모 클래스인 Animal 에는 eat 이라는 메서드가 있다.
그 부모 클래스 Animal를 상속받아 eat을 재정의한 Dog 가 있다.
여기서 Dog 클래스가 소스파일이 아닌 class 파일로 안의 정보를 확인이 불가능한 상태라고 하자.
이때 Animal 클래스(부모 클래스)를 이용해 메서드 접근이 가능하다. (재정의 한 메서드)
Cat 클래스와 Dog 클래스의 정보를 모두 아는 경우에는 이렇게 직접 Cat 클래스와 Dog 클래스에 접근해서
각 클래스의 기능들을 사용하면 된다.
하지만 class 파일만 제공 받은 경우는 Animal 을 통해 접근해야하는데 그 과정을 자세히 살펴보자.
Animal 의 형을 갖은 ani 의 변수를 만둘어 준다.이러한 객체 생성방법은 Dog 와 Animal이 상속관계가 전제되어야 한다.
(upcasting)
new Dog() 를 통해 Dog 클래스 내부의 생성자가 작동한다.
그 내부에 super 를 통해 부모클래스의 Animal의 생성자를 호출해서 Animal 클래스의 객체가 먼저 생성이 된다.
그래서 Animal 객체가 먼저 생기고 Dog 객체가 생긴다.(new Dog 로 생성해서 메모리에 함께 생긴다.)
이때 Animal 형태를 갖은 ani 라는 변수로는 Animal 영역만 접근이 가능한데.
Animal 의 eat을 실행시키면 Animal의 eat에 먼저 접근 하다가 상속관계인 Dog에
재정의(Override)된 메소드 eat() 이 있는지 확인하고 있으면 Dog의 eat에 접근한다.
Animal 에 없는 자식 메서드에 접근하기!
Animal 에는 Cat의 night 의 메서드가 없다. 이때 Cat의 메서드에 접근 하고자 한다면
Downcasting 으로 해당 자식 클래스의 형을 맞춰주면 접근이 가능하다(상속 관계이기 떄문에 가능)
(Cat)연산자보다 .연산자의 우선순위가 더 높다. 그래서 ((Cat)ani).night();를 해줘야 한다.
Animal 로 생성한 객체를 단 메모리에 new Cat으로 생성한 ani 변수이다.(메모리에 함께 있어야 하므로.)
Animal ani = new Cat(); 이렇게 선언된 ani여아 한다.
이 ani 변수를 down casting 하여서 animal 에 없고 Cat 클래스 에만 있는 메서드에 접근이 가능한 것이다.
✨마찬가지로!! 중요중요!
최상위 클래스 Object 로도 하위클래스의 메소드를 사용할 수 있다!
최상위 클래스인 Object 클래스형태로 Dog으 생성자 메소드로 객체를 만들게 되면(upcasting)
다시 Dog 클래스로 downcasting 을 통해 Dog 메소드에 접근이 가능하다!!
(Object에는 eat() 메소드가 없으므로 downcasting 을 통해서 접근해야 한다.)
부모 객체로 자식객체에게 접근이 가능하고 해당 메소드를 사용 할 수 있다는 이점은 매우 크다.
(이를 통해 Object 클래스는 모든 클래스의 메소드에 접근이 가능한것이다.)
upcasting은 자동형변환이며 downcasting은 강제 형 변환이다.
🔍 다형성 맛보기.
상위 클래스가 하위 클래스들에게 동일한 메서드를 보낸다(eat)
하위클래스가 그 메서드를 반응을 하는데 서로 다르게 동작 되는 원리를
다형성 이라고 한다.
상속 관계이고 upcasting 으로 객체를 만들게 되면 다형성 이론이 적용이 된다!
다음 포스트에는 다형성에 대해서 더 깊게 알아보겠다.
자바 기초를 더 단단하게 하기 위해 선택한 강의입니다.
출처: Java TPC (생각하고, 표현하고, 코딩하고) 대시보드 - 인프런 | 강의 (inflearn.com)