[Section1] 다형성(Polymorphism), 추상화(Abstraction)
다형성
1. 다형성이 가지는 의미와 장점
2. 참조변수의 타입 변환, 업캐스팅과 다운캐스팅의 차이
3. instanceof 연산자
일반적인 의미에서 다형성이란 하나의 객체가 여러 가지 형태를 가질 수 있는 성질을 의미한다.
자바 프로그래밍에서의 다형성은 상위 클래스 타입의 참조변수를 통해서 하위 클래스의 객체를 참조할 수 있도록 허용한 것이라 할 수 있다.
//참조변수의 다형성 예시
class Friend {
public void friendInfo() {
System.out.println("나는 당신의 친구입니다.");
}
}
class BoyFriend extends Friend {
public void friendInfo() {
System.out.println("나는 당신의 남자친구입니다.");
}
}
class GirlFriend extends Friend {
public void friendInfo() {
System.out.println("나는 당신의 여자친구입니다.");
}
}
public class FriendTest {
public static void main(String[] args) {
Friend friend = new Friend(); // 객체 타입과 참조변수 타입의 일치
BoyFriend boyfriend = new BoyFriend();
Friend girlfriend = new GirlFriend(); // 객체 타입과 참조변수 타입의 불일치
friend.friendInfo();
boyfriend.friendInfo();
girlfriend.friendInfo();
}
}
// 출력값
나는 당신의 친구입니다.
나는 당신의 남자친구입니다.
나는 당신의 여자친구입니다.
위 코드를 보면 GirlFriend와 Friend 즉 객체 타입과 참조변수의 타입이 일치하지 않아도 상위 클래스를 참조변수의 타입으로 지정했기 때문에 인스턴스의 생성이 가능하다.
또한 상위 클래스를 참조변수의 타입으로 지정했기 때문에 자연스럽게 참조변수가 사용할 수 있는 멤버의 개수는 상위 클래스의 멤버의 수가 된다.
한 가지 더 기억해야 할 것은 상위 클래스의 타입으로 하위 클래스 타입의 객체를 참조하는 것은 가능하지만 그 반대는 불가능하다.
참조변수의 타입 변환
참조 변수도 타입변환이 가능하다.
참조 변수의 타입 변환은 다르게 설명하면 사용할 수 있는 멤버의 개수를 조절하는 것을 의미하는데, 이는 자바의 다형성을 이해하기 위해서 꼭 필요한 개념이다.
타입 변환을 위해서는 다음 세 가지 조건을 충족해야 한다.
1. 서로 상속관계에 있는 상위 클래스 - 하위 클래스 사이에만 타입 변환이 가능하다.
2. 하위 클래스 타입에서 상위 클래스 타입으로의 타입 변환(업캐스팅)은 형변환 연산자(괄호)를 생략할 수 있다.
3. 상위 클래스에서 하위 클래스 타입으로 변환(다운캐스팅)은 형변환 연산자(괄호)를 반드시 명시해야 한다.
여기서는 서로 상속관계에 있는 클래스들 사이에서만 타입 변환이 가능하다는 것만 기억하면 충분하다.
instanceof 연산자
instanceof 연산자는 참조변수의 타입 변환, 즉 캐스팅이 가능한 지 여부를 boolean 타입으로 확인할 수 있다.
참조_변수 instanceof 타입
만약 참조_변수 instanceof 타입 을 입력했을 때 리턴 값이 true가 나오면 참조 변수가 검사한 타입으로 변환이 가능하며, 반대로 false가 나오는 경우에는 타입 변환이 불가능하다.
참조 변수가 null인 경우 false를 반환한다.
public class InstanceOfExample {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal instanceof Object); //true
System.out.println(animal instanceof Animal); //true
System.out.println(animal instanceof Bat); //false
Animal cat = new Cat();
System.out.println(cat instanceof Object); //true
System.out.println(cat instanceof Animal); //true
System.out.println(cat instanceof Cat); //true
System.out.println(cat instanceof Bat); //false
}
}
class Animal {};
class Bat extends Animal{};
class Cat extends Animal{};
Animal 클래스가 있고, Bat과 Cat 클래스가 각각 Animal 클래스를 상속 받고 있다.
그리고 각각 객체를 생성하여 Animal 타입의 참조변수에 넣고 instanceof 키워드를 사용하여 형변환 여부를 확인하고 있다.
Cat 객체를 예로 들어보면, 생성된 객체는 Animal 타입으로 선언되어있지만 다형적 표현 방법에 따라 Object와 Animal 타입으로도 선언될 수 있다는 점을 확인할 수 있다.
다형성의 활용
package package2;
public class PolymorphismEx {
public static void main(String[] args) {
Customer customer = new Customer();
customer.buyCoffee(new Americano());
customer.buyCoffee(new CaffeLatte());
System.out.println("현재 잔액은 " + customer.money + "원 입니다.");
}
}
class Coffee {
int price;
public Coffee(int price) {
this.price = price;
}
}
class Americano extends Coffee {
public Americano() {
super(4000); // 상위 클래스 Coffee의 생성자를 호출
}
public String toString() {return "아메리카노";}; //Object클래스 toString()메서드 오버라이딩
};
class CaffeLatte extends Coffee {
public CaffeLatte() {
super(5000);
}
public String toString() {return "카페라떼";};
};
class Customer {
int money = 50000;
void buyCoffee(Coffee coffee) {
if (money < coffee.price) { // 물건 가격보다 돈이 없는 경우
System.out.println("잔액이 부족합니다.");
return;
}
money = money - coffee.price; // 가진 돈 - 커피 가격
System.out.println(coffee + "를 구입했습니다.");
}
}
// 출력값
아메리카노를 구입했습니다.
카페라떼를 구입했습니다.
현재 잔액은 41000원 입니다.
위 코드를 보면 Coffee라는 상위 클래스를 만들고
Americano와 CaffeLatte 클래스를 하위 클래스로 만들어 Coffee 클래스를 상속시켰다.
여기서 buyCoffee 라는 메서드를 보면 매개변수로 Coffee 클래스의 참조변수를 받는 것을 볼 수 있다.
여기서 Coffee 클래스의 참조변수가 아니라, Americano와 CaffeLatte의 참조변수를 각각 받는다면 메서드를 따로 만들어야 했을 것이다.
하지만 상속을 받을 때 상위 클래스의 타입으로 형변환이 되는 걸 이용한다면 상위 클래스의 참조 타입을 매개변수로 지정하고 하위 클래스의 객체를 참조할 수 있어 메서드 하나로 코드가 간결해진다.
추상화
추상화의 핵심 개념은 공통성과 본질을 모아 추출하는 것이다.
자바에서의 추상화는 객체의 공통적인 속성과 기능을 추출하여 정의하는 것을 의미한다.
앞서 학습한 상속이 하위 클래스를 정의하는데 상위 클래스를 사용하는 것이라고 한다면 추상화는 반대로 기존 클래스들의 공통적인 요소들을 봅아서 상위 클래스를 만들어 내는 것이라고 할 수 있다.
위 그림을 보면 자동차와 오토바이의 공통된 분모들을 모아 이동수단이라는 클래스에 담았다.
반대로 이동수단이 가지는 공통적인 특징을 자동차와 오토바이에 내려줬다고 해도 공식은 성립된다.
이렇게 공통적인 속성과 기능을 모아서 정의해주면 코드의 중복을 줄일 수 있고, 보다 효과적으로 클래스 간의 관계를 설정할 수 있으며, 유지/보수가 용이해진다.
abstract 제어자
abstract 제어자의 의미는 '미완성' 이다.
메서드 앞에 abstract가 붙는 경우 "추상 메서드"
클래스 앞에 abstract가 붙는 경우 "추상 클래스" 라 부른다.
추상메서드는 말 그대로 미완성 메서드이며,
미완성 메서드를 포함하는 클래스는 미완성 클래스를 의미하는 추상 클래스가 된다.
추상 클래스는 미완성 설계도이기 때문에 메서드의 바디가 완성될 때 까지는 객체 생성이 불가능하다.
추상 클래스의 장점
1. 상속 관계에 있어 새로운 클래스를 작성하는데 매우 유용하다.
2. 메서드의 내용이 상속을 받는 클래스에 따라서 종종 달라지기 대문에 상위 클래스에서는 선언부만 작성하고, 실제 구체적인 내용은 상속을 받는 하위 클래스에서 구현하도록 비워둔다면 설계하는 상황이 변하더라도 유연하게 대응할 수 있다.
인터페이스
인터페이스는 기본적으로 추상 메서드와 상수만을 멤버로 가질 수 있다.
즉, 추상 클래스에 비해 추상화 정도가 더 높다고 할 수 있다.
인터페이스를 작성하는 것은 기본적으로 클래스를 작성하는 것과 유사하지만
class 키워드 대신 interface 키워드를 사용한다는 점에서 차이가 있다.
또한 내부의 모든 필드가 public static final로 정의 되고, static과 default 메서드 이외의 모든 메서드가 public abstract로 정의된다는 차이가 존재한다.
인터페이스도 추상클래스와 마찬가지로 그 자체로 인스턴스를 생성할 수 없고, 메서드 바디를 정의하는 클래스를 따로 작성해야한다.
이러한 과정은 extends 키워드를 사용하는 클래스의 상속과 기본적으로 동일하지만 '구현하다'라는 의미를 가진 implements 키워드를 사용한다는 점에서 차이가 있다.
특정 인터페이스를 구현한 클래스는 해당 인터페이스에 정의된 모든 추상메서드를 구현해야한다.
인터페이스는 상속과는 다르게 다중적 구현이 가능하다.
인터페이스의 장점
1) 코드 변경의 번거로움을 최소화하고 손쉽게 해당 기능을 사용할 수 있도록 한다.
2) 선언과 구현을 분리시켜 개발시간을 단축
3) 독립적인 프로그래밍을 통해 한 클래스의 변경이 다른 클래스에 미치는 영향을 최소화할 수 있다.
'[Section1]' 카테고리의 다른 글
[Section1] 회고 (0) | 2022.07.20 |
---|---|
[Section1] 제네릭(Generic), 컬렉션 프레임워크(Collection Framework) (0) | 2022.07.14 |
[Section1] 캡슐화 (0) | 2022.07.13 |
[Section1] 상속(Inheritance) (0) | 2022.07.11 |
[Section1] 생성자(Constructer) (0) | 2022.07.10 |
댓글