앞서 8번에서 배웠던 상속과 7번 오버로딩을 합하여 오버로딩의 한계를 해결할 수 있는 방법을 알아보도록 하자.
다음과 같은 코드가 있다.
class Animal{
}
class Dog extends Animal{
final String NAME="강아지";
}
class Cat extends Animal{
final String NAME="고양이";
}
class Bird extends Animal{
final String NAME="새";
}
public class AnimalEx01 {
static void attack(Dog u1, Cat u2) {
System.out.println(u2.NAME+"가 "+u1.NAME+"에게 공격당했습니다.");
}
static void attack(Dog u1, Bird u2) {
System.out.println(u2.NAME+"가 "+u1.NAME+"에게 공격당했습니다.");
}
static void attack(Cat u1, Bird u2) {
System.out.println(u2.NAME+"가 "+u1.NAME+"에게 공격당했습니다.");
}
강아지, 고양이, 새, 이렇게 다 따로따로 만들면 3X3 =9개의 매서드 오버로딩이 일어나야 한다. 이는 동물이 늘어나며 제곱하며 점점 늘어나게 될 것이다.
이를 매서드 오버라이딩으로 해결하고자 한다.
매서드 오버라이딩의 예시를 보자.
class Car{
void run() {
System.out.println("기본 움직임");
}
}
class Sonata extends Car{
void run() {
super.run();
System.out.println("소나타만의 움직임");
}
}
public class CarEx01 {
public static void main(String[] args) {
Car s1=new Sonata();
s1.run();
}
}
일단 Sonata(부모)와 Car(자식)은 부모자식 관계이다.
부모자식간에 똑같은 run 메소드가 있는데 이 때, 오버라이드 실행 시에 메모리에 두 개의 클래스가 뜨게 될 경우 부모의 매서드가 무시되고 실행 이후 동적으로 본인이 필요한 매서드를 찾아가게 된다. 이를 동적 바인딩이라고 한다.
자식 클래스 내에 super 이라는 레퍼런스가 있다. 이 super은 부모 클래스를 가리키는 레퍼런스이다. 이 레퍼런스가 있으면 부모 클래스 내의 매서드 값을 함께 참조하게 된다.
한번 super 래퍼런스가 있는 메소드를 지운 값을 보자.
class Car{
void run() {
System.out.println("기본 움직임");
}
}
class Sonata extends Car{
void run() {
System.out.println("소나타만의 움직임");
}
}
public class CarEx01 {
public static void main(String[] args) {
Car s1=new Sonata();
s1.run();
}
}
다음과 같이 부모 클래스의 "기본 움직임" 출력문을 무효화하고 "소나타만의 움직임" 출력문이 출력된 것을 볼 수 있다.
(오버라이딩은 재사용이 아니다. 무효화다..!)
다음은 변수를 오버라이딩해보자.
원래 변수는 오버라이딩만 할 수 있고 매서드만 오버라이딩이 가능하다.
다음과 같은 매서드를 보자.
class A {
void run() {
System.out.println("A 달린다.");
}
}
class B extends A {
@Override
void run() {
System.out.println("B 달린다.");
}
}
class C extends B {
String name="C";
@Override
void run() {
System.out.println(name+" 달린다.");
}
}
이 함수들을 한번 호출해보자.
public class OverrideEx01 {
public static void main(String[] args) {
A a1 = new A();
a1.run();
A a2 = new B();
a2.run();
A a3 = new C();
a3.run();
당연히 A a1 = new A();는 호출된 주소가 A, 그리고 로드 된 메모리 값 역시 A밖에 없으니 오버라이딩이 일어나지 않는다.
A a2 = new B(); 는 호출된 주소가 A, 로드된 메모리가 부모인 A와 자식인 B이므로 A에 있는 run 메소드가 무시되고 B의 run 메소드가 호출된다.
A a3 = new C(); 역시 마찬가지이나, 메소드를 자세히 보자. 다른 곳과 다르게 String name="C";가 선언된 것이 보인다.
이 변수를 한번 불러보자.
보다시피 오류가 난다.
매서드 오버라이딩에서 변수를 호출하기 위해서는 변수의 은닉화와 마찬가지로 변수를 직접 호출하지 않고 매서드를 통해 간접적으로 호출해야한다.
자, 그럼 다시 동물 이야기로 돌아오자.
상속을 이용해서 오버로딩을 하나로 줄여보자.
그러나 오류가 나는 것을 볼 수 있다.
왜 오류가 날까?
class Animal{
}
Animal 클래스에서 선언된 NAME변수가 없기 때문이다. 그러나 변수는 오버라이딩 되지 않기 때문에 간접적인 방법을 통해 함수로 오버라이딩을 한다.
abstract class Animal1 {
abstract String getNAME() ;
}
class Dog1 extends Animal1 {
String NAME = "강아지";
@Override
String getNAME() {
return NAME;
}
}
class Cat1 extends Animal1 {
String NAME = "고양이";
@Override
String getNAME() {
return NAME;
}
}
class Bird1 extends Animal1 {
String NAME = "새";
@Override
String getNAME() {
return NAME;
}
}
public class AnimalTest01 {
static void attack1(Animal1 u1, Animal1 u2) {
System.out.println(u2.getNAME() + "가 " + u1.getNAME() + "에게 공격당했습니다.");
}
변수의 은닉화와 마찬가지로 다음과 같이 getNAME를 통해 문자열 NAME을 리턴 받아, 그 함수값을 NAME 자리에 집어넣으면 된다. 이 때, abstract를 이용해 부모 클래스를 추상화 시키는데, 동물이라는 이름을 가진 동물이 없듯, 이 클래스는 인스턴스화 될 필요가 없기 때문이다. 따라서 실체를 가지지 않게 추상 클래스로 생성한다.
'JAVA' 카테고리의 다른 글
라이브러리(lombok) 설치하기 (0) | 2020.03.31 |
---|---|
JAVA 실습 6. 매서드 오버라이딩을 이용한 오버로딩 줄이기 (0) | 2020.03.30 |
8. 상속 (0) | 2020.03.30 |
7. 오버로딩 (0) | 2020.03.27 |
6. 생성자 (0) | 2020.03.27 |