Java 4일차

진창호·2023년 1월 19일

Java

목록 보기
4/9

Java는 다형성을 지원한다.

다형성이란 상속 관계에서 조상 클래스의 타입으로
자식 클래스 객체를 레퍼런스 할 수 있는 것이다.

아래 사진과 코드로 이를 이해해보자.
다형성

public class PolyTest {
	SpiderMan onlyOne = new SpiderMan("피터 파커", false);
	
	SpiderMan sman = onlyOne;
	Person person = onlyOne;
	Object obj = onlyOne;
	// Venom venom = onlyOne;
}

SpiderMan의 부모 객체는 Spider 객체의 주소값을 받을 수 있다.
하지만 SpiderMan의 자식 객체는 Spider 객체의 주소값을 받을 수 없다.

다형성은 다른 타입의 객체를 다루는 배열을 생성할 수 있게 해준다.
아래 코드를 살펴보자.

public void useObjectArray() {
        Object[] arr = new Object[4];
        
        arr[0] = 1;
        arr[1] = "Hello";
        arr[2] = new Person();
        arr[3] = new SpiderMan("피터파커", true);
        
        for (int i = 0; i < 4; i++) {
        	System.out.println(arr[i].getClass());
        }
    }

출력 결과는 아래와 같다.

class java.lang.Integer
class java.lang.String
class c_inheritance.person.Person
class c_inheritance.person.SpiderMan

즉, Object의 다양한 자식 클래스를 하나의 배열에 저장할 수 있다.

※ int는 기본 자료형이라 Object를 상속받지 않았는데 왜 저장이 가능한가?
int는 Wrapper 클래스인 Integer로 autoboxing 되고, Integer는 Object의 자식 클래스라서 저장이 가능하다.

또한, 다형성은 다양한 매개변수를 받을 수 있도록 한다.


Java에선 부모 클래스가 자식 클래스를 접근하는 데 제약이 있다.

Person person = new SpiderMan(); 로 객체를 정의했다.
그러면 person 객체는 Person 클래스의 멤버 메서드는 사용 가능하지만,
SpiderMan 클래스의 멤버 메서드는 사용할 수 없다.

객체 간 형변환은 아래와 같다.

  1. 묵시적 형변환 : 자손 타입을 조상 타입으로
    SpiderMan spiderMan = new SpiderMan();
    Person person = spiderMan;
  2. 명시적 형변환 : 조상 타입을 자손 타입으로
    Person person = new Person();
    SpiderMan spiderMan = (SpiderMan) Person;

그렇다면 명시적 형변환을 하면 자식 클래스의 멤버 메서드를 사용 가능한 것일까?
아래 코드를 살펴보자.

Person person = new Person();
SpiderMan spiderMan = (SpiderMan) person;

여기서 spiderMan은 SpiderMan 클래스의 멤버 메서드를 사용할 수 없다.
person 객체가 생성될 때 SpiderMan 클래스를 위한 메모리 공간은 할당하지 않았기 때문이다.

그래서 애초에 메모리 공간이 할당된 경우에만 자식 클래스의 멤버 메서드에 접근할 수 있다.
아래 코드를 살펴보자.

SpiderMan sMan = new SpiderMan();
Person person = sMan;

if (person instanceof SpiderMan) {
        	SpiderMan spiderMan = (SpiderMan) Person;
        	spiderMan.jump(); // 이제 SpiderMan 클래스의 멤버 메서드 접근 가능
        } else {
        	System.out.println(obj.getClass().getName());
        }

여기서 spiderMan은 SpiderMan 클래스의 멤버 메서드를 사용할 수 있다.


Java는 오버라이딩된 메서드는 부모 클래스가 자식 클래스를 접근할 수 있도록 한다.

아래 코드로 이를 이해해보자. jump 메서드는 오버라이딩된 메서드이다.

public class AppropriateParameter {
    public void useJump(Person person) {
        person.jump();
    }

    public static void main(String[] args) {
        Person person = new Person();
        SpiderMan spiderMan = new SpiderMan("피터 파커", true);
		
        AppropriateParameter ap = new AppropriateParameter();
        
        ap.useJump(person);
        ap.useJump(spiderMan);
    }
}

실행 결과는 아래와 같다.

ap.useJump(person) : Person class의 jump 메서드를 실행한다.
ap.useJump(spiderMan) : SpiderMan class의 jump 메서드를 실행한다.

이는 아래 사실을 상기시킨다.

ap.useJump(spiderMan)에서 매개변수를 받을 때
Person person = new SpiderMan("피터 파커", true)인 상황임에도
부모 클래스인 Person이 자식 클래스인 SpiderMan에서
오버라이딩된 메서드인 jump 메서드를 사용한다.


Java는 매개변수를 최상위 객체로 사용하길 권장한다.

아래 코드를 살펴보자.

public class AppropriateParameter {
    public void useJump1(Object obj) {
        if (obj instanceof Person) {
            Person casted = (Person) obj;
            casted.jump();
        }
    }

    public void useJump2(Person person) {
        person.jump();
    }

    public void useJump3(SpiderMan spiderMan) {
        spiderMan.jump();
    }

    public static void main(String[] args) {
        Object obj = new Object();
        Person person = new Person();
        SpiderMan sman = new SpiderMan("피터 파커", true);

        AppropriateParameter ap = new AppropriateParameter();
        
        // 1번 경우
        ap.useJump1(obj);
        ap.useJump1(person);
        ap.useJump1(sman);
        
        // 2번 경우
        ap.useJump2(person);
        ap.useJump2(sman);
        
        // 3번 경우
        ap.useJump3(sman);
    }
}

1번 경우는 모든 클래스의 가장 최상위 객체인 Object로 매개변수를 받는다.
2번 경우는 사용할 클래스 중 가장 최상위 객체인 Person으로 매개변수를 받는다.
3번 경우는 특정 클래스인 SpiderMan으로 매개변수를 받는다.

이때, 2번 경우로 매개변수를 받는게 가장 적절하다.


배운 내용 정리

부모 클래스와 자식 클래스 관계를 정리해보자.
관계 정리

Person p = new SpiderMan()
  1. p.a는 Person 클래스의 멤버변수 a 값을 불러온다.
  2. p.eat()은 Person 클래스의 멤버메서드 eat()을 호출한다.
  3. p.getClass()는 Object 클래스의 멤버메서드 getClass()를 호출한다.
  4. p.fireWeb()은 호출할 수 없다.
  5. p.jump()는 SpiderMan 클래스의 오버라이딩된 멤버메서드 jump()를 호출한다.
profile
백엔드 개발자

0개의 댓글