230328 - java(11)

팝콘·2023년 3월 28일

Java

목록 보기
10/17

1. 다형성

  • 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있는 것을 의미한다.

  • 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것이다.
    (반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.)

  • 자손객체를 생성해서 부모타입의 참조변수에 대입할 수 있다.
    - 실제 생성한 자손객체의 주소값이 참조변수에 대입되는 것이 아니라, 실제 생성한 객체와 연결된 조상객체 중에서 참조변수의 타입과 일치하는 조상객체의 주소값이 참조변수에 대입한다.

  • Object객체의 클래스의 최고 조상객체이므로 참조변수에는 자바의 모든 객체를 저장할 수 있다.

class Tv {
	boolean power;
    int channel;
    
    void power() { power = !power; }
    void channelUp() { ++channel; }
    void channelDown() { --channel; }
}

class SmartTv extends Tv {
	String text;
    void caption() { ... }
}

일반적인 객체 생성
	SmartTv s = new SmartTv();
    - SmartTv 인스턴스의 모든 멤버를 사용할 수 있다.
    
다형성을 이용한 객체 생성
	Tv t = new SmartTv();
    - Tv클래스의 멤버들(상속받은 멤버포함)만 사용할 수 있다.
    
    SmartTv s = new Tv();
    - 오류
  • 둘 다 같은 타입의 인스턴스를 생성했지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

  1. 참조변수의 타입과 생성한 객체의 타입이 서로 다르다
  2. super를 통해 생성한 객체의 조상타입 객체 중에서 참조변수의 타입과 같은 객체가 있는지 탐색한다
  3. 조상객체 중에서 참조변수의 타입과 같은 객체가 발견되면 해당 조상객체의 주소값을 참조변수에 대입한다.
    (참조변수는 해당 조상객체를 바라보고 있다.)

다형성이 필요한 이유

1. 하나의 타입으로 다양한 객체의 타입을 참조할 수 있다.

SmartPhone p;
- p참조변수는 SmartPhone Galaxy, Iphone객체를 참조할 수 있다.
- 하나의 타입으로 여러 객체를 관리(저장)할 수 있다.

Phone[] p = new Phone[10];
- p가 참조하는 배열은 Phone 타입객체 10개를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 Phone, SmartPhone, Iphone, FeaturePhone, Galaxy 객체를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 통화기능, 문자기능이 있는 모든 Phone종류를 저장할 수 있다.

SmartPhone[] p = new SmartPhone[10];
- p가 참조하는 배열은 SmartPhone타입객체 10개를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 SmartPhone, Iphone, Galaxy 요소를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 통화기능 문자기능, 게임기능, 인터넷 기능이 있는 모든 SmartPhone종류를 저장할 수 있다.

2. 참조변수의 타입으로 객체의 종류를 제한할 수 있다.

public void testInternet(Phone p) { ... }
- 통화 기능을 테스트하는 메소드
- 매개변수의 타입을 Phone타입으로 지정함으로써 call() 기능이 제공되는 모든 Phone류 객체를 전달받을 수 있다.

public void testInternet(SmartPhone p) { ... }
- 인터넷 기능을 테스트하는 메소드
- 매개변수의 타입을 SmartPhone타입으로 지정함으로써 internet() 기능이 제공되는 모든 SmartPhone류 객체를 전달받을 수 있다.

public void testApplePay(Ipone p) { ... }
- 애플페이 기능을 테스트하는 메소드
- 매개변수의 타입을 Iphone타입으로 지정함으로써 applePay() 기능이 제공되는 모든 Iphone류 객체를 전달받을 수 있다.

3. 참조변수의 타입으로 반환타입의 종류를 제한할 수 있다.

public Phone createPhone() { ... }
- 이 메소드는 모든 Phone류 객체를 반환할 수 있다.
    			
public SmartPhone createSmartPhone() { ... }
- 이 메소드는 모든 SmartPhone류 객체를 반환할 수 있다.

참조변수의 형변환

  • 기본형 변수처럼 참조변수도 형변환이 가능하다. 단, 서로 상속관계에 있는 클래스 사이에서만 가능하다.

  • 기본형의 형변환과 달리 참조형의 형변환는 변수에 저장된 값이 변환하는 것이 아니다.

  • 바로 윗 조상이나 자손이 아닌, 조상의 조상으로도 형변환이 가능하다.

  • 참조변수의 형변환은 그저 리모컨(참조변수)를 다른 종류의 것으로 바꾸는 것 뿐이다. 리모컨의 타입을 바꾸는 이유는 사용할 수 있는 멤버 개수를 조절하기 위한 것이다.

class Car { }
class FireEngine extends Car { }
class Ambulan

FireEngine f = new FireEngine();
Car c = (Car)f;					
- 조상인 Car타입으로 형변환이 가능하다.
- f의 값(객체의 주소)을 c에 저장한다.
- 자손타입을 조상타입으로 형변환하는 경우이기 때문에 생략이 가능하다.
- 다룰 수 있는 멤버의 개수가 줄어들기 때문에 항상 안전하다. 그러므로 형변환을 생략할 수 있다.

FireEngine f2 = (FireEngine)c;	
// 자손인 FireEngine타입으로 형변환 가능

Ambulance a = (Ambulance)f		
// 상속관계가 아닌 클래스 간의 형변환 불가능

형변환 연산자

  • 실제 생성한 객체의 기능을 실행하기 위해서는 클래스 형변화 연산자를 사용해서 실제 생성한 객체를 참조하게 해야된다.
Phone p1 = new FeaturePhone();
	p1.call();			// Phone
	p1.sms();			// Phone
	p1.toString();		// Object
	p1.mp3();			// 실행할 수 없다.

	- (FeaturePhone)p1은 p1참조변수가 참조하는 객체를 기준으로 FeaturePhone객체를 
       탐색해서 해당객체가 발견되면 그 객체의 주소값을 반환한다. 
       단, 생성된 객체에 포함된 객체만 가능하다.
	FeaturePhone x = (FeaturePhone)p1;
	x.mp3();							// 실행할 수 있다.
	SmartPhone y = (SmartPhone)p1;		// 형변환 오류
		
Phone p3 = new Iphone();
	p3.toString();
	p3.call();	
	p3.sms();
	p3.internet();	// 실행할 수 없다.
	p3.game();		// 실행할 수 없다.
	p3.facetime();	// 실행할 수 없다.
	p3.applePay();	// 실행할 수 없다.
    
    SmartPhone y = (SmartPhone)p3;
    y.internet();				// 실행할 수 있다.
    y.game();					// 실행할 수 있다.
    
    Iphone z = (Iphone)p3;		
    z.facetime();				// 실행할 수 있다.
    z.applePay();				// 실행할 수 있다.

instanceof 연산자

  • 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용된다.
  • 연산의 결과로 boolean값인 true와 false 중의 하나를 반환한다.
  • instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.
형식)		
		bolean result = 참조변수 instanceof 클래스 이름
    

2. 메서드 재정의(Overriding)

  • 부모클래스로부터 상속받은 기능을 자식클래스에서 자신에게 맞게 메소드의 기능을 재정의하는 것이다.
  • 오버라이딩을 사용하는 목적
    - 상속관계에 있는 클래스들에서 유사한 기능은 같은 사용법을 가지게 한다.
class Point {
	int x;
    int y;
    
    String getLocation() {
    	return "x :" + x + ", y :" + y;
    }
}

class Point3D extends Point {
	int z;
    
    // 메서드 재정의
    String getLocation() {
    	return "x :" + x + ", y :" + y + ", z :" + z;
    }
}
  • 메소드 재정의 관계가 있는 메소드는 클래스 형변환 되어서 실제 생성한 객체를 참조하지 않고도 재정의된 메소드를 실행할 수 있다.

  • 실제 생성한 객체에 재정의 메소드를 실행하기 위해서 실제 생성한 객체를 참조하도록 강제형변환할 필요가 없다.

오버라이딩의 조건

  • 메서드의 선언부(반환타입, 메서드 이름, 매개변수)는 조상의 것과 완전히 일치해야 한다.

  • 접근 제어자는 조상 클래스의 메서드와 일치하거나 더 넓어야한다.
    - 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면, 자손 클래스에 정의된 메서드의 접근 제어자는 protected나 public이어야 한다.

  • 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.

오버로딩 vs 오버라이딩

  • 오버로딩
    - 기존에 없는 새로운 메서드를 정의하는 것
  • 오버라이딩
    - 상속받은 메서드의 내용을 변경하는 것
class Parent {
	void parentMethod() {수행문1;}
}

class Child extends Parent {
	void parentMethod() {수행문2;}				// 오버라이딩
    void parentMethod(int x) {수행문1;}			// 오버로딩
}
profile
터짐

0개의 댓글