다형성을 사용하는 이유는?
자바에서 다형성을 사용하는 주된 이유는
코드의 재사용성과 확장성
을 높이기 위해서입니다. 다형성을 사용하면, 상위 클래스나 인터페이스를 이용해 여러 하위 클래스들을 참조할 수 있기 때문에,
코드의 일부분만 변경하거나 추가함으로써 쉽게 기능을 확장
할 수 있습니다.
조상 타입의 참조 변수를 사용하는 것이 인스턴스의 일부 멤버만 사용할 수 있다고 느낄 수도 있지만, 실제로는 상속받은 클래스 간에 공통적인 기능을 사용하는 것이 중요합니다. 이렇게 함으로써
다른 클래스들과의 결합도를 낮추고, 유지보수와 확장에 용이한 코드를 작성
할 수 있습니다.
예를 들어, 동물의 특징을 모델링하는 상위 클래스인 Animal이 있고, 이를 상속받는 Dog, Cat, Bird 등의 하위 클래스가 있다고 가정해 봅시다. 이 경우, 다형성을 사용하면 Animal 타입의 참조 변수를 사용하여 Dog, Cat, Bird 등의 인스턴스를 가리킬 수 있습니다. 이렇게 하면 각 동물의 종류에 따라 다르게 구현된 메소드를 호출할 수 있으며, 새로운 동물의 종류를 추가하거나 변경할 때 코드의 다른 부분을 수정할 필요가 없습니다.
결론적으로, 다형성은 자바에서 코드의 재사용성과 확장성을 높이는 중요한 개념입니다. 조상 타입의 참조 변수를 사용하면서 일부 멤버만 사용할 수 있다고 생각할 수 있지만, 이는 공통된 기능을 활용하고 유연한 코드를 작성하기 위한 기본 원칙입니다.
조상 타입으로 형변환할 때의 장점
- 코드의 유연성: 조상 타입의 참조 변수를 사용하면, 다양한 자손 타입들을 통합하여 처리할 수 있습니다. 이를 통해 코드의 유연성이 향상되며, 다양한 타입의 객체를 동일한 방식으로 처리하는 로직을 작성할 수 있습니다.
- 다형성 구현: 조상 타입으로 형변환을 통해 다형성을 구현할 수 있습니다. 이를 통해 메소드 오버라이딩을 활용하여 자손 클래스의 인스턴스가 조상 클래스의 메소드를 대체하여 실행되도록 할 수 있습니다. 이러한 방식은 유지 보수와 확장성에 이점을 제공합니다.
- 인터페이스 활용: 인터페이스를 구현한 클래스들을 인터페이스 타입으로 형변환하여 사용하면, 서로 다른 클래스들이 공통된 기능을 구현할 수 있습니다. 이를 통해 유연한 코드 설계와 기능 확장이 가능해집니다.
- 메소드 매개변수: 조상 타입을 메소드의 매개변수로 사용하면, 다양한 자손 타입의 객체를 인수로 전달받을 수 있습니다. 이를 통해 메소드를 보다 일반화시키고, 다양한 객체를 처리하는 로직을 작성할 수 있습니다.
단점으로는,
조상 타입으로 형변환된 참조 변수로는 자손 타입의 멤버에 직접 접근할 수 없다는 점
이 있습니다. 이 경우, 다시 자손 타입으로 형변환을 해야 자손 클래스의 멤버를 사용할 수 있습니다. 그러나 이러한 단점도 적절한 설계를 통해 극복할 수 있습니다.
다형성 활용 예시
예를 들어, 도형을 나타내는 상위 클래스 Shape
가 있고, 이를 상속받는 Circle
, Rectangle
, Triangle
등의 하위 클래스들이 있다고 가정해 봅시다. 이 때, 각 도형마다 넓이를 구하는 calculateArea()
메소드를 구현하려고 합니다.
먼저, 상위 클래스 Shape
를 선언하고, calculateArea()
메소드를 추상 메소드로 선언합니다.
abstract class Shape {
abstract double calculateArea();
}
그 다음, Shape
를 상속받는 Circle
, Rectangle
, Triangle
클래스를 정의하고, 각 클래스에 맞게 calculateArea()
메소드를 오버라이딩하여 구현합니다.
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
double calculateArea() {
return width * height;
}
}
class Triangle extends Shape {
private double base;
private double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
double calculateArea() {
return 0.5 * base * height;
}
}
이제 조상 타입으로 형변환을 사용하여 코드의 유연성을 살펴보겠습니다.
다양한 도형들의 넓이를 구하는 메소드를 작성해 보겠습니다.
public static void printArea(Shape shape) {
System.out.println("도형의 넓이: " + shape.calculateArea());
}
위의 printArea()
메소드는 Shape
타입의 매개변수를 받아서 해당 도형의 넓이를 출력합니다. 이 메소드는 Shape
클래스를 상속받은 어떤 도형 객체도 처리할 수 있습니다.
다음은 메인 메소드에서 이를 사용하는 예입니다.
public static void main(String[] args) {
Circle circle = new Circle(5);
Rectangle rectangle = new Rectangle(4, 6);
Triangle triangle = new Triangle(3, 7);
printArea(circle);
printArea(rectangle);
printArea(triangle);
}
위의 예시에서, printArea()
메소드는 Shape
타입의 매개변수를 받기 때문에, Circle
, Rectangle
, Triangle
객체를 모두 처리할 수 있습니다. 만약 나중에 새로운 도형 클래스를 추가하더라도, printArea()
메소드를 수정할 필요 없이 동일하게 활용할 수 있습니다. 이렇게 조상 타입으로 형변환을 사용하면, 코드의 유연성과 확장성이 향상되며, 다양한 자손 타입을 동일한 방식으로 처리할 수 있습니다.
또한, 도형 배열을 생성하여 각 도형의 넓이를 한 번에 출력할 수도 있습니다. 이를 통해 조상 타입으로 형변환의 장점을 확인할 수 있습니다.
public static void main(String[] args) {
Shape[] shapes = new Shape[3];
shapes[0] = new Circle(5);
shapes[1] = new Rectangle(4, 6);
shapes[2] = new Triangle(3, 7);
for (Shape shape : shapes) {
printArea(shape);
}
}
위의 예시에서, Shape
타입의 배열에 각각의 도형 객체를 저장할 수 있습니다. 이렇게 하면, for
문을 사용하여 도형 배열을 순회하면서 각 도형의 넓이를 출력할 수 있습니다. 여기서도 다형성을 활용하여 다양한 도형 객체를 처리할 수 있습니다.
결론적으로, 조상 타입으로 형변환을 사용하면 다양한 자손 클래스의 객체를 동일한 방식으로 처리할 수 있습니다. 이를 통해 코드의 유연성과 확장성이 향상되며, 다형성을 구현할 수 있습니다. 이러한 방식은 객체 지향 프로그래밍에서 중요한 설계 원칙 중 하나로 간주되며, 실제 프로젝트에서도 많이 활용되고 있습니다.
'Java' 카테고리의 다른 글
[Java] 함수형 프로그래밍(Functional Programming) (0) | 2023.09.08 |
---|---|
[Java] 예외 처리 (0) | 2023.08.26 |
[Java] 클래스명 작성 규칙 5가지 (0) | 2023.04.05 |
[Java] 오버로딩과 오버라이딩 (0) | 2023.04.04 |
[Java] Java에서 this, this()란 무엇인가 (0) | 2023.04.03 |