본문 바로가기

프로그래밍언어/JAVA

[JAVA] 다형성 (Polymorphism)

반응형

1. 다형성 (polymorphism)

다형성이랑 '여러가지 형태를 가질 수 있는 능력'을 의미한다. 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다. 이 말은 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하였다는 의미이다.

 

class Parent {
	...
}

class Child extends Parent {
	...
}

...

// 각각의 참조 변수의 선언 타입과 일치하는 인스턴스를 참조한다.
Parent p = new Parent();
Child c = new Child();

// 각각의 타입과 일치하는 자손 인스턴스를 참조한 위의 예시와 달리
// 조상 타입의 참조 변수로 자손 인스턴스를 참조한다.
Parent o = new Child();

 

위의 소스코드에서는 기존과 같이 각자의 선언된 타입과 같은 인스턴스를 생성하여 참조한 변수와 부모 타입으로 선언하고 자손 타입의 인스턴스를 생성하여 참조한 변수가 있다. 이들의 차이점은 참조 변수를 통해서 사용할 수 있는 멤버에 있다.

 

// Parent 클래스에 선언된 멤버들을 사용할 수 있다.
Parent p = new Parent();

// Parent 클래스와 Child 클래스에 선언된 멤버들을 모두 사용할 수 있다.
Child c = new Child();

// Child 클래스 타입의 인스턴스를 참조하지만 Parent 타입으로 선언되었기에
// Parent 클래스의 멤버만 사용할 수 있다.
// Child 클래스에 선언된 멤버는 상속된 멤버를 제외하고는 사용할 수 없다.
Parent o = new Child();

 

반대로 자손 클래스 타입의 참조 변수에서 조상 클래스 타입의 인스턴스를 참조하려는 경우 컴파일 에러가 발생한다. 그 이유는 자손 클래스에는 조상 클래스에 선언되지 않은 멤버를 가질 수 있기 때문이다.


2. 참조변수의 형변환

기본형 변수와 같이 참조변수도 형변환이 가능하다. 단 서로 상속관계에 있는 클래스 사이에서만 가능하기 때문에 자손 타입 변수를 조상 타입으로 조상 타입 변수를 자손 타입으로만 가능하다. 또한 기본형 변수와 같이 자동 형변환이 가능한데, 자손 타입을 조상 타입으로 변환할 때 타입 명시를 하지 않아도 형변환이 된다.

조상 타입을 자손 타입으로 형변환 하는 것을 다운 캐스팅, 반대로 자손 타입을 조상 타입으로 형변환 하는 것을 업캐스팅 이라고 한다. 자손 타입의 참조변수를 조상 타입으로 변환하는 업캐스팅은 형변환을 생략해도 자동 형변환 되지만 조상 타입의 참조변수를 자손 타입으로 변환은 괄호를 사용하여 명시적으로 타입을 기입해주어야 한다.

같은 조상을 상속하는 경우의 변수 간에는 형변환이 일어날 수 없다. 같은 조상을 상속하고 있지만 각 타입 서로 간에는 상속 관계가 아니기 때문에 서로간에 직접적인 형변환은 허용되지 않는다.

다형성에서 자손 타입의 참조 변수에 조상 타입의 인스턴스를 참조할 수 없다는 것을 배웠다. 그 이유는 사용할 수 있는 멤버가 더 많은 자손이 조상에 포함될 수 없기 때문이다. 결국 상속 관계에 있는 타입 간의 형변환은 사용할 수 있는 멤버의 제한에 있다는 것을 알 수 있다.


3. instanceof

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용한다. 주로 조건문에서 타입을 비교할 때 사용한다. instanceof의 왼쪽에 참조변수를 오른쪽에는 타입을 피연산자로 주어 연산을 수행하도록 한다. 결과값으로는 boolean 값인 true 또는 false를 반환한다.

 

// 참조변수 a가 B 타입인 경우 true를 아닌 경우 false를 반환한다.
// 만약 a의 타입 클래스가 B 클래스를 상속한 경우에도 true가 반환된다.
a instanceof B

 

참조변수의 타입 클래스 또는 해당 클래스의 조상 클래스를 피연산자로 주는 경우 true를 반환한다. 이는 곧 참조변수가 해당 클래스 타입으로 변환이 가능하다는 의미이다.


4. 참조변수와 인스턴스의 연결

조상 타입의 참조변수와 자손 타입의 참조변수 간의 차이점은 사용할 수 있는 멤버의 개수에 있다. 또한 두 클래스 간에 중복된 이름의 멤버가 있을 때 어떤 멤버를 사용할 것인가에 대한 기준이 다르다.

메서드의 경우 자손 클래스에서 조상 클래스의 메서드를 오버라이딩 한 경우에도 실제 참조 변수의 인스턴스의 메서드를 호출하지만 멤버 변수의 경우 참조변수의 타입에 따라 달라진다. 조상 타입은 조상 클래스의 멤버 변수를, 자손 타입은 자손 클래스의 멤버 변수를 사용한다.


5. 매개변수의 다형성

참조변수의 다형성은 메서드의 매개변수에도 적용된다.
메서드의 매개변수에 여러가지 타입이 올 수 있는 경우, 해당 클래스들의 조상 클래스로 메서드의 매개변수를 선언하면 해당 클래스를 비롯한 자손 타입의 클래스를 모두 매개변수로 사용할 수 있기 때문에 구현 및 운영이 편해진다.

 

class Parent { ... }
class Child extends Parent { ... }

...

public void method(Parent param) { ... }

...

// method의 매개변수 param이 Parent이기 때문에 Parent, Child 두 타입을 모두 사용할 수 있다.
method(new Parent());

// 자손 클래스인 Child 타입의 인스턴스가 자동 형변환 되어 method의 매개변수로 넘어간다.
method(new Child());

 

6. 여러 타입의 객체를 배열로 다루기

조상 타입의 참조변수로 자손 타입의 객체를 참조하는 것이 가능하기 때문에 조상 타입으로 선언한 배열에 서로 다른 타입의 객체를 저장할 수 있다.

 

class Parent { ... }
class A extends Parent { ... }
class B extends Parent { ... }
class C extends Parent { ... }

...

Parent[] arr = new Parent[3];
arr[0] = new A();
arr[1] = new B();
arr[2] = new C();

 

- Vector 클래스

객체를 저장할 때 배열의 크기를 동적으로 가져가고 싶다면 Vector 클래스를 사용할 수 있다. Vector 클래스는 내부적으로 Object 타입의 배열을 가지고 있기 떄문에 이 곳에 다양한 객체를 추가 / 삭제할 수 있다. 또한 배열의 크기를 알아서 동적으로 조절하기 때문에 인스턴스의 개수를 따로 신경쓰지 않아도 된다.

 

Vector 클래스

- Vector(): 기본적으로 10개의 객체를 저장할 수 있는 Vector 인스턴스를 생성한다. 10개 이상의 인스턴스가 저장되면 자동적으로 크기가 증가한다.

- boolean add(Obejct o): Vector에 객체를 추가한다. 추가의 성공 여부를 반환한다.

- boolea remove(Object o): Vector에 저장되어 있는 객체를 제거한다. 제거의 성공 여부를 반환한다.

- boolean isEmpty(): Vector가 비어있는지 검사한다.

- Object get(int index): 지정된 index의 객체를 반환한다. 반환 타입이 Object 이기 때문에 적절한 타입으로의 형변환이 필요하다.

- int size(): Vector에 저장된 객체의 개수를 반환한다.
반응형

'프로그래밍언어 > JAVA' 카테고리의 다른 글

[JAVA] 인터페이스 (Interface)  (1) 2021.11.23
[JAVA] 추상클래스 (abstract class)  (0) 2021.11.20
[JAVA] 제어자 (Modifier)  (0) 2021.11.16
[JAVA] 자바 패키지  (0) 2021.11.08
[JAVA] 오버라이딩 (Overriding)  (0) 2021.11.08