개요
API 서버를 개발하던 중 여러 형태의 Request 클래스를 이용하여 하나의 DB 테이블에 저장하기 위해 하나의 entity 클래스로 변환해야하는 일이 생겼다. 각 Request별로 메소드를 만들어 변환시켜 주기에는 재사용되는 소스코드도 많았고 번거로웠다. 다행히 Request 클래스들은 중복되는 필드가 많아 하나의 부모를 상속받고 있는 하위클래스였다.
그래서 이 상속 관계를 활용해 불필요한 소스코드를 줄이고 깔끔하게 설계하기 위해서 Java 언어의 특징인 '다형성'에 대해 다시 한 번 정리해 보았다.
다형성
다형성은 다양한 형태를 가질 수 있는 성질을 의미한다. Java 언어가 가진 특성 중 하나라고 할 수 있다.
장점은 반복되는 형태를 개선하여 불필요한 소스코드를 줄이고 유지 보수를 용이하게 하는 데 있다.
구현 방법은 추상 클래스, 인터페이스 상속, 오버라이딩, 업캐스팅 등을 활용하는 복합적인 형태로 구현한다. 글만 봐서는 잘 모르겠다. 바로 예시를 통해 알아보겠다.
예시
아래 예시는 Product 부모 클래스의 상속을 받은 Shoe, Laptop 클래스의 관계를 나타낸다.
- 중복되는 name, price 필드는 부모 클래스의 것을 이용하고 각 자식 클래스별 고유 성질을 나타내는 필드는 따로 추가하였다.
- 객체 생성 시 초기화를 위한 생성자 함수를 작성했다.
- 각 자식클래스는 getDetail() 메소드를 오버라이딩했고 일부 필드 값을 확인하기 위해 getter도 넣어줬다.
public class PloymorphismTest {
}
class Product {
private String name;
private String price;
public Product(String name, String price) {
this.name = name;
this.price = price;
}
public void getDetail() {
System.out.println("product name : " + name +
", product price : " + price);
}
}
class Shoe extends Product{
private String size;
private String color;
public Shoe(String size, String color) {
super("신발", "10000");
this.size = size;
this.color = color;
}
public String getSize() {
return this.size;
}
@Override
public void getDetail() {
System.out.println("size : " + this.size + ", color : " + this.color);
}
}
class Laptop extends Product{
private String os;
private String ram;
private String hdd;
public Laptop(String os, String ram, String hdd) {
super("노트북", "2000000");
this.os = os;
this.ram = ram;
this.hdd = hdd;
}
public String getOs(){
return this.os;
}
@Override
public void getDetail() {
System.out.println("os : " + this.os + ", ram : " + this.ram + ", hdd : " + this.hdd);
}
}
위에 작성된 클래스를 이용한 테스트에 앞서 업캐스팅에 대해 간략히 설명 하자면,
업캐스팅
캐스팅은 형변환을 말한다.
업캐스팅은 자식 클래스를 부모클래스로 형변환 한다는 것이다.
위 소스코드를 활용해 예시로 작성해 보자면 Product product = new Shoe("250", "노랑"); 와 같이 표현할 수 있다.
이렇게 업캐스팅을 사용하는 주된 이유는 '다형성'에 있다. 풀어서 설명하자면 상위 클래스(Person) 하나만으로 다양한 하위 클래스 (Shoe, Laptop 등)를 활용할 수 있다. 예를 들어, Shoe, Laptop 클래스는 상위 클래스의 메소드인 getDetail() 메소드를 오버라이딩 했는데, 업캐스팅을 한 Product는 getDetail() 메소드를 호출하면 하위 클래스의 메소드가 호출된다. 어떻게 보면 Product 클래스로 하위 클래스를 동적으로 사용 가능한 것 같다.
이제 좀더 이해가 쉽게 테스트한 결과를 확인해 보자.
테스트
public class PloymorphismTest {
public static void main(String[] args) {
Product product = new Product("기본상품", "100");
Product product1 = new Shoe("250", "노랑");
Product product2 = new Laptop("윈도우", "8", "1");
product.getDetail();
product1.getDetail();
product2.getDetail();
System.out.println("=========================================");
methodTest(product);
methodTest(product1);
methodTest(product2);
}
public static void methodTest(Product product) {
if(product instanceof Shoe) {
System.out.println("shoe instance!!");
System.out.println(((Shoe) product).getSize());
}
if(product instanceof Laptop) {
System.out.println("Laptop instance!!");
System.out.println(((Laptop) product).getOs());
}
}
}
===================== 결과 ===========================
product name : 기본상품, product price : 100
size : 250, color : 노랑
os : 윈도우, ram : 8, hdd : 1
=========================================
shoe instance!!
250
Laptop instance!!
윈도우
위 테스트의 결과를 보면
- 업캐스팅하여 getDetail() 메소드를 호출하면 각 자식클래스의 오버라이딩된 메소드가 호출되는 것을 알 수 있다. 만약 오버라이딩 되어 있지 않다면, 부모 클래스의 메소드가 호출될 것이다.
- 난 이게 제일 마음에 드는데, 각 자식클래스를 매개변수로한 메소드를 여러개 만들 필요 없이 부모클래스를 매개변수로 받는 메소드를 하나만 만들어도 그 매개변수의 instance는 자식 클래스의 instance로 유지된다. 하위 클래스에만 있는 필드에 값을 저장하고 업캐스팅을 하여 매개변수로 넘긴다 하더라도 그 값은 유지되는 것이다.
'언어' 카테고리의 다른 글
[Java] Java8 - 함수형 프로그래밍 (4) (0) | 2022.12.01 |
---|---|
컴파일 언어 vs 인터프리터 언어 (0) | 2022.11.29 |
[Java] Java8 - 함수형 프로그래밍 (3) (0) | 2022.10.22 |
[Java] Java8 - 함수형 프로그래밍 (2) (0) | 2022.10.17 |
[Java] Java8 - 함수형 프로그래밍 (0) | 2022.06.09 |