공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 

 

 

다형성(Polymorphism)

  자바에서는 한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다. 그래서 부모 클래스 타입의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있다.

  그러나 같은 타입의 인스턴스라도 참조 변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다는 점에 주의해야 한다. 부모 클래스 타입의 참조 변수로 자식 클래스의 인스턴스를 참조하게 되면 부모 클래스에 정의된 멤버들(상속받은 멤버 포함)만 사용할 수 있게 되고 반대의 경우(자식 클래스 타입의 참조 변수로 부모 클래스의 인스턴스를 참조하는 것)는 불가능하다. 자식 클래스의 멤버는 부모 클래스를 확장한 것이기 때문에 부모 클래스보다 같거나 더 많기 때문이다. 부모 클래스에 없는 멤버를 사용할 가능성이 있어서 허용하지 않는다. (모든 참조 변수는  null 또는  4byte 의 주소 값이 저장되며, 참조 변수의 타입은 참조할 수 있는 객체의 종류와 허용할 수 있는 멤버의 수를 결정한다.)

 

 

Car와 Ambulance 클래스의 상속관계

예를 들어  Car 클래스(부모 클래스)와  Car 클래스를 상속받은  Ambulance 클래스(자식 클래스)가 있다고 가정한다.

 

 

 

참조변수에 따른 인스턴스 멤버의 사용범위(Object클래스 부분은 제외)

(  c   Car 타입의 참조 변수이고,  a   Ambulance 타입의 참조 변수이다.)

자식 클래스인  Ambulance 타입으로 2개의 인스턴스를 생성해도 참조 변수의 타입을  Car (부모 클래스)로 했는지  Ambulance (자식 클래스)로 했는지에 따라 접근할 수 있는 멤버의 범위가 달라진다. 또한 위의 그림에서도 마찬가지로 자식 클래스의 멤버는 무조건 부모 클래스와 같거나 크기 때문에 자식 타입의 참조 변수로 부모 타입의 인스턴스를 참조할 수는 없다.

 

 

 

 

참조 변수의 형변환

 

자식타입 → 부모타입(Up-casting) : 형변환 생략 가능
부모타입 자식타입(Down-casting) : 형변환 생략 불가

  참조형 변수도 기본형 변수처럼 형변환이 가능한데 상속관계에 있는 클래스끼리만 가능하다. 참조 변수의 형변환 역시 캐스트 연산자를 사용하고 괄호 ( ) 안에 변환하고자 하는 타입의 이름(클래스명)을 적어주면 된다. 또한 기본형 변수의 형변환에서 작은 자료형에서 큰 자료형으로의 형 변환은 생략이 가능한 것처럼 참조형 변수에서도 자식 타입의 참조 변수를 부모 타입으로 형변환하는 경우에는 형변환을 생략할 수 있다.

 

  Up-casting을 생략하는 이유도 위에서 말한 내용과 동일하다. 자식 타입에서 부모 타입으로 변환하는 것은 참조 변수가 다룰 수 있는 멤버의 개수가 실제 인스턴스가 갖고 있는 멤버의 개수보다 줄어드는 것이 분명하므로 생략 가능하도록 만든 것이다. 하지만 Down-casting의 경우에는 참조 변수가 다룰 수 있는 멤버의 개수를 늘리는 것이므로, 실제 인스턴스의 멤버 개수보다 참조 변수가 사용할 수 있는 멤버의 개수가 많아져서 존재하지 않는 것을 참조할 가능성이 있으므로 생략할 수 없게 만든 것이다. 또한 형변환은 참조 변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조 변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다. 사용할 수 있는 멤버의 범위(개수)를 조절하는 것뿐이다. 

 

 

 

 

instanceof연산자

void doWork(Car c){	//부모클래스인 Car타입을 매개변수로 받는 doWork함수 선언
	if(c instanceof Ambulance){	//매개변수인 c가 Ambulance타입으로 형변환이 가능한 경우
    	Ambulance a = (Ambulance)c;	//Ambulance타입의 참조변수 a에 참조변수 c에 저장된 주소를 Ambulance타입으로 형변환해서 저장한다.
        a.siren();	//a의 멤버변수인 siren()함수를 사용한다.
        ...
    }
}

  참조 변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해  instanceof 연산자를 사용한다. 주로 조건문에 사용되며,  instanceof 의 왼쪽에는 참조 변수, 오른쪽에는 타입(클래스명)이 온다. 연산의 결과로는  boolean 값인  true  false 중 하나를 반환한다.  instanceof 의 연산 결과가  true 라는것은 참조 변수가 검사한 타입으로 형변환이 가능하다는 의미이다. 

 

 

package test;

class test{
	public static void main(String[] args) {
		
		Ambulance a = new Ambulance();
		
		if(a instanceof Ambulance) {	//a가 Ambulance의 instance인 경우
			System.out.println("Ambulance의 instance");
		}
		
		if(a instanceof Car) {	//a가 Car의 instance인 경우
			System.out.println("Car의 instance");
		}
		
		if(a instanceof Object) {	//a가 Object의 instance인 경우
			System.out.println("Object의 instance");
		}
		
	}
	
	static class Car { }	//부모클래스인 Car클래스 선언
	static class Ambulance extends Car { }	//Car클래스를 상속받은 Ambulance클래스(자식클래스)선언
}

위의 코드를 실행하면 아래와 같은 결과값이 출력된다. 

 

 

실행결과

모든 클래스는  Object 클래스를 상속받기 때문에  Ambulance 클래스는  Car 도 상속받았지만  Object 클래스도 상속받는다. 그래서  Ambulance 의 멤버에는  Car 의 멤버와  Object 의 멤버도 포함되어있다. 그러므로 실제 인스턴스와 같은 타입의  instanceof 연산이나 부모 타입의  instanceof 모두  true 를 결과값을 얻으며 형변환해도 문제가 없다.

 

 참조변수.getClass( ).getName( ) 은 참조 변수가 가리키고 있는 인스턴스 클래스 이름을  String 으로 반환한다.

 

 

 

 

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

 부모 타입의 참조 변수와 자식 타입의 참조 변수의 차이점이 사용 개수 말고도 하나 더 있다. 메서드의 경우 부모 클래스의 메서드를 자식 클래스에서 오버 라이딩한 경우에도 참조 변수의 타입에 관계없이 항상 실제 인스턴스의 메서드(오버라이딩된 메서드)가 호출되지만, 멤버 변수의 경우 참조 변수의 타입에 따라 달라진다. 멤버 변수가 부모 클래스와 자식 클래스에 중복으로 정의된 경우, 부모타입의 참조변수를 사용했을 때는 부모클래스에 선언된 멤버 변수가 사용되고, 자식타입의 참조변수를 사용했을 때는 자식클래스에 선언된 멤버변수가 사용된다. 중복 정의되지 않은 경우에는 차이가 없다. 

 

 

 

 

매개변수의 다형성

public void print(Object obj){
	write(String.valueOf(obj));	//valueof()가 반환한 문자열을 출력한다.
}
public static String valueOf(Object obj){
	return (obj == null) ? "null" : obj.toString();	//문자열을 반환한다.
}

  참조 변수의 다형적인 특징은 메서드의 매개변수에도 적용된다.  PrintStream 클래스에 정의되어있는  print(Object o) 메서드를 예시로 들면 원래는 매개변수에 들어오는 인스턴스의 종류마다  print 함수를 다르게 만들어야 한다. 그러나 메서드를 보면  print(Object o) 로 선언되어있다. 매개변수를 이렇게 하면  Object 클래스를 상속받은 자식 타입의 참조 변수면 어느 것이나 매개변수로 받아들일 수 있다는 뜻이다.  Object 클래스는 모든 클래스의 부모 클래스가 되므로 매개변수를  Object 타입으로 하면 하나의 메서드로 모든 타입의 인스턴스를 처리할 수 있게 된다. 

 

 

 

 

여러 종류의 객체를 배열로 다루기

   부모 타입의 참조 변수로 자식 타입의 객체를 참조하는 것이 가능하므로, 부모 타입의 참조 변수 배열을 사용하면 공통의 부모를 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰  수 있다. 또는 묶어서 다루고 싶은 객체들의 상속관계를 따져서 가장 가까운 공통 부모 클래스 타입의 참조 변수 배열을 생성해서 객체를 저장할 수도 있다. 

 

  참조 배열을 만들 때  Vector 클래스를 사용하면 편리하다.  Vector 는 동적으로 크기가 관리되는 객체 배열이다. 내부적으로  Object 타입의 배열을 가지고 있어서 배열에 객체를 추가하거나 제거할 수 있게 작성되어있다. 배열의 크기를 알아서 관리해주기 때문에 저장할 인스턴스의 개수에 신경 쓰지 않아도 된다.

메서드 / 생성자 설명
Vector( ) 10개의 객체를 저장할 수 있는  Vactor 인스턴스를 생성한다.
10개 이상이 저장되면 자동적으로 크기가 증가된다.
boolean add(Object o)  Vector 에 객체를 추가한다.
성공하면 true, 실패하면 false를 반환한다.
boolean remove(Object o)  Vector 에 저장되어있는 객체를 제거한다.
제거에 성공하면 true, 실패하면 false를 반환한다.
boolean isEmpty( )  Vector 가 비어있는지 검사한다.
비어있으면 true, 비어있지 않으면 false를 반환한다.
Object get(int index) 지정된 위치(index)의 객체를 반환한다. 반환타입이  Object 타입이므로 적절한 타입으로의 형변환이 필요하다.
int size( )  Vector 에 저장된 객체의 개수를 반환한다.

 

 

 

+ Recent posts