본문 바로가기

Development/Java

객체지향의 네 가지 특성 : 캡슐화, 추상화, 다형성, 상속성

객체지향의 핵심적인 네 가지 특성인 캡슐화와 추상화, 다형성, 상속성에 대해 정리해 보았다.

 

 

1. 캡슐화

 

 

객체지향 프로그래밍에서는 구조노출, 객체 외부에서의 비정상적인 접근 또는 필드 값의 잘못변경 등의 위험성으로부터 보호하기 위해 구성멤버를 캡슐화할 수 있다. 접근 제한자를 이용해 중요한 정보를 담고 있는 필드와 직접적인 호출이 바람직하지 않은 메소드보호하고, 대체 수단을 통해 외부 객체와 상호작용하게 된다.

 

클래스 내부에서는 항상 멤버 간 자유롭게 상호작용이 가능하지만, 외부에서는 접근 제한 등급에 따라 공개된 구성요소에만 접근할 수 있다. 이 구성요소들은 외부와의 상호작용을 매개하며 잘못된 이용을 방지해 주는 역할을 담당하게 된다.

 

public class Encapsulation {
    private int a;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        if (a < 0) {
            return;
        } else {
            this.a = a;
        }
    }
}

 

위의 예시에서 field a private 접근 제한이 걸려 있어 객체 외부에서의 접근이 제한된다. 따라서 외부에서 직접 값을 변경할 수 없다. 대신 getA method(Getter)와 setA method(Setter)를 통해 상호작용하게 된다.

 

setA 메소드는 외부 객체로부터 전달된 매개변수의 값이 음수인 경우, field a의 값을 변경하지 않고 그대로 돌려보낸다. 따라서 field a가 속도나 길이, 잔액 등 음수값을 가질 수 없는 변수일 경우 외부 객체에 의해 잘못된 값 변경이 이루어지는 것을 방지할 수 있다.

 

getA 메소드는 setA 메소드의 필요성에 의해 외부에서 접근할 수 없게 된 field의 값을 외부에서 이용할 수 있도록 전달해 주는 역할을 한다. 또한 객체 외부에서 실제 저장되어 있는 field값보다 적절한 전달값이 필요한 경우, 변경한 값을 보내 주는 역할을 할 수 있다.

 

 

2. 추상화

 

 

추상화는 구체적인 실체들의 공통특성을 추출한 것이다.

 

추상 클래스에는 자식 클래스들의 공통필드메소드가 선언되어 있기 때문에, 자식 클래스들은 이를 일일이 작성할 필요 없이 편리하게 상속받아 사용할 수 있다. 여기에 각자의 고유 특성에 기반한 필드와 메소드만 추가해 사용하면 된다.

 

추상 메소드는 자식들이 공통으로 가지는 메소드의 선언부만 정의되어 있을 뿐 실행 내용없다. 일종의 분류와 가이드 역할이라고 할 수 있다. 자식 클래스들은 이를 재정의해 각자의 특성에 맞게 커스터마이징하여 사용할 수 있다.

 

public abstract class Abstraction {
    public abstract void a();
}

class Sub1 extends Abstraction {
    @Override
    public void a() {
        // 오버라이딩
    }
}

class Sub2 extends Abstraction {
    @Override
    public void a() {
        // 오버라이딩
    }
}

 

추상 클래스는 선언 시 class 앞에 abstract 키워드를 추가해야 한다. Abstraction은 부모 클래스이고, Sub1과 Sub2는 자식 클래스이다. 자식 클래스는 클래스명 뒤에 'extends 부모 클래스명'을 추가해야 한다.

 

Abstraction 클래스의 a 메소드는 추상 메소드로서, 실행 내용을 가지지 않는다. 이는 자식 클래스들에서 각자의 목적과 특성에 맞게 재정의하여 사용한다.

 

 

3. 다형성

 

 

다형성은 같은 방법을 통해 다양한 결과물을 추구할 수 있게 해 주는 특성이다. 상속인터페이스를 이용하면 같은 메소드를 호출해 서로 다른 결과를 얻을 수 있다. 이는 객체지향 프로그래밍의 주된 목적 중 하나이다.

 

다형성을 구현하기 위한 기술에는 promotion(자동 타입 변환)과 method overriding(메소드 재정의)이 있으며, 상속과 구현을 통해 이를 달성할 수 있다. 이는 아래의 상속성 항목에서 보다 상세히 설명한다.

 

 

 

4. 상속성

 

 

상속은 구현과 함께 다형성을 구현하는 핵심 역할을 한다.

 

여러 클래스들에서 가지는 공통특성을 부모 클래스에서 작성해 두면, 자식 클래스들은 이를 상속받아 편리하게 이용할 수 있다. 이에 따라 코드재사용성이 높아지게 되어 매번 새로운 클래스를 일일이 작성할 필요성이 줄어든다. 여러 클래스에서 한 부모 클래스를 상속받은 경우, 유지보수 과정에서도 부모의 필드 또는 메소드만 수정해 여러 자식 클래스에 적용할 수 있어 편의성이 증대된다. 또한 개별 클래스마다 개성을 추구하는 요소의 경우, 해당 메소드의 재정의(overriding)를 통해 다형성을 추구할 수 있다.

 

Java에서 모든 클래스는 단 하나부모만을 가질 수 있다. 이는 인터페이스 구현과의 차이점이다.

 

public class Inheritance {
    public static void main(String[] args) {
     Super super1 = new Sub1();
     Super super2 = new Sub2();
    }
}

class Super {
    public void method1(int a) {
        a += 10;
    }
    public void method2(int b) {
        b += 20;
    }
}

class Sub1 extends Super {
    @Override
    public void method1(int a) {
        a += 30;
    }
}

class Sub2 extends Super {
    @Override
    public void method2(int b) {
        b += 30;
    }
}

 

Super는 부모 클래스이고, Sub1과 Sub2는 자식 클래스이다.

 

main method에서 Sub1과 Sub2의 생성자를 호출해 객체를 생성하고, 해당 객체에 부모 타입의 변수를 대입했다. 이는 자식 클래스타입이 부모(혹은 조상) 클래스의 타입으로 자동 변환이 되기 때문이다. 부모의 필드와 메소드에 접근이 가능함과 동시에 자동 타입 변환된 이후에도 객체를 생성한 자식 클래스의 overriding적용된다. 이를 통해 다형성을 구현할 수 있다. 다만 자식 클래스메소드를 호출하려면 다시 자식 클래스의 타입으로 강제 타입 변환을 해야 한다.

 

Sub1은 method1을 재정의하고 Sub2는 method2를 재정의했다. 재정의하지 않은 나머지 메소드는 그대로 상속받는다. 따라서 각 자식 클래스가 원하는 method만 재정의할 수 있다. 이는 자동 타입 변환과 함께 다형성의 핵심이 된다. 재정의할 때 @Override annotation을 작성하면, 재정의가 제대로 이루어지지 않은 경우 compile 과정에서 오류를 출력하게 해 준다. 이는 생략이 가능하다.