2020년 10월 22일
업데이트:
다형성
객체지향 프로그래밍 3대 특징 중 하나로 ‘여러 개의 형태를 갖는다’는 의미이다. 하나의 행동으로 여러 가지 일을 수행하는 개념이며, 상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로 상속 관계에 있는여러 타입의 자식 객체를 참조할 수 있는 기술이다.
업 캐스팅(Up Casting)
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입 객체의 주소를 참조할 수 있다.
다운 캐스팅(Down Casting)
자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 부모 클래스 타입의 참조형 변수를 자식 클래스 타입으로 형변환 하는 것. 자동 처리되지 않기 때문에 반드시 자식 타입을 명시
하여 형변환
바인딩
정적 바인딩
프로그램이 실행되기 전 컴파일 단계에서 메소드와 메소드 호출부를 연결해준다.
동적 바인딩
컴파일 시 정적 바인딩 된 메소드를 실행할 당시의 객체 타입을 기준으로 바인딩 된다.
// Animal.java
public class Animal {
public void eat() {
System.out.println("영양분 섭취한다.");
}
public void breath() {
System.out.println("숨을 쉰다.");
}
}
// Fish.java
public class Fish extends Animal {
private int fin;
@Override
public void eat() {
System.out.println("물고기는 밥을 뻐끔뻐끔 먹는다.");
}
@Override
public void breath() {
System.out.println("물고기는 아가미로 숨을 쉰다.");
}
public void swim() {
System.out.println("물고기는 헤엄을 칠 수 있다.");
}
}
// Person.java
public class Person extends Animal {
private String name;
public Person() {
super(); // 상속 관계에 있는 부모 객체를 자식 객체 생성 시 추가해야 하므로 자식 생성자 첫 번째 줄에 super() 생성자 작성
// 미작성 시 컴파일러가 자동으로 추가한다.
}
@Override
public void eat() {
System.out.println("사람은 식기를 이용해서 음식을 먹는다.");
}
@Override
public void breath() {
System.out.println("사람은 코와 입으로 숨을 쉰다.");
}
public void studyJava() {
System.out.println("사람은 누구나 자바를 공부할 수 있다.");
}
}
// Snake.java
public class Snake extends Animal {
private String poison;
@Override
public void eat() {
System.out.println("뱀은 통째로 삼킨다.");
}
@Override
public void breath() {
System.out.println("뱀은 피부 호흡을 한다.");
}
public void lock() {
System.out.println("뱀은 먹이를 조른다.");
}
}
// PolyService.java
public class PolyService {
public void example1() {
Animal a = new Animal();
// 업캐스팅 : 참조 변수의 타입이 부모 타입으로 변경되는것
// -> 부모 타입의 참조 변수로 자식 객체 참조
Animal person = new Person();
// person.studyJava(); // 자식 객체의 고유 기능을 참조할 수 없음
a.eat();
person.eat();
// Person 객체 내부에 존재하는 Animal 객체의 eat() 호출
// --> 정적 바인딩
// Person(Animal)이 참조하고 있는 객체의 eat()를 호출하면
// 내부에 존재하는 Animal 객체의 eat()가 아닌
// 오버라이딩 된 Person의 eat()를 호출함
// ---> 동적 바인딩
Animal[] arr = new Animal[4];
arr[0] = new Animal();
arr[1] = new Person();
arr[2] = new Fish();
arr[3] = new Snake();
for(int i = 0; i < arr.length; i++){
arr[i].breath();
}
// arr[0], [1], [2] 인덱스 요소가 참조하고 있는
// 자식 객체들이 부모인 Animal로부터 상속받은
// breath() 메소드를 오버라이딩하였기 때문에
// 컴파일 전에는 부모의 breath()로 바인딩 되어 있다가(정적 바인딩)
// 실행 중에는 각자 오버라이딩 된 breath()가 실행됨 (동적 바인딩)
// 다운 캐스팅
// 부모 참조 변수 = 자식 객체
// 참조변수가 부모 -> 자식으로 변함으로써
// 자식 객체를 온전히 참조할 수 있게 된다.
((Person) arr[1]).studyJava();
((Fish) arr[2]).swim();
((Snake) arr[3]).lock();
}
}
instanceof 연산자
현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인할 때 사용한다. 클래스 타입이 맞으면 true, 맞지 않으면 false를 반환한다.
// PolyService.java
for(int i = 0; i < arr.length; i++){
if(arr[i] instanceof Person){
((Person) arr[i]).studyJava();
} else if (arr[i] instanceof Fish){
((Fish) arr[i]).swin();
} else if(arr[i] instanceof Snake) {
((Snake) arr[i]).lock();
} else {
System.out.println("그냥 Animal 객체입니다.");
}
}
추상 클래스
몸체 없는 메소드를 포함한 클래스며 추상 클래스일 경우 클래스 선언부에 abstract
키워드를 사용한다.
추상 메소드
몸체 없는 메소드이며 추상 메소드의 선언부에 abstract
키워드를 사용한다. 상속 시 반드시 구현해야 하는, 오버라이딩
이 강제화되는 메소드이다.
추상 클래스 특징
- 미완성 된 클래스로 자체적으로 객체 생성이 불가능하다. → 반드시
상속
하여 객체를 생성해야 한다. - abstract 메소드가 포함된 클래스는 반드시 abstract 클래스 abstract 메소드가 없어도 abstract 클래스 선언이 가능하다.
- 클래스 내에 일반 변수, 메소드를 포함할 수 있다.
- 객체 생성은 안되지만 참조형 변수 타입으로는 사용이 가능하다.
장점
- 일관된 인터페이스를 제공한다.
- 꼭 필요한 기능을 강제화(공통적이나 자식 클래스에서 특수화 되는 기능) 한다.
인터페이스
상수형 필드와 추상 메소드만을 작성할 수 있는 추상 클래스의 변형체이며, 메소드의 통일성을 부여하기 위해 추상 메소드만 따로 모아놓은 것으로 상속 시 인터페이스 내에 정의된 모든 추상 메소드를 구현해야 한다.
[접근제한자] interface 인터페이스명 {
// 상수도 멤버로 포함 가능
public static final 자료형 변수명 = 초기값;
// 추상 메소드만 선언 가능
[public abstract] 반환자료형 메소드명([자료형 매개변수]);
//public abstact가 생략되기 때문에 오버라이딩 시 반드시 public 표기
}
특징
- 모든 인터페이스의 메소드는 묵시적으로
public
이고abstract
이다. - 변수는 묵시적으로
public static final
따라서 인터페이스 변수의 값 변경 시도 시 컴파일 에러가 발생한다. - 객체 생성은 불가하나 참조형 변수로는 가능하다.
장점
- 상위 타입 역할로 다형성을 지원하여 연결한다.
- 해당 객체가 다양한 기능 제공 시에도 인터페이스에 해당하는 기능만을 사용하게 제한한다.
- 공통 기능 상의 일관성을 제공한다.
- 공동 작업을 위한 인터페이스를 제공한다.
댓글남기기