java

Java_Chapter08_객체 지향 4대 요소

강용민 2022. 10. 9. 19:28

객체지향개념의 중요한 특징 4가지가 존재하는데, 캡슐화(Encapsulation), 상속(Inheritance), 다형성(Polymorphism), 추상화(Abstraction)이다.

 

상속(inheritance)

상속은 객체지향 프로그래밍의 4개 개념(Abstraction, Polymorphism, Inheritance, Encapsulation) 중 Inheritance에 해당하는 중요한 개념이다.

프로그램에서의 상속은 기존 클래스의 재산을 다른 클래스에서 재사용하기 위한 것이다.

여기서 말하는 재산이란 기존 클래스에 있던 멤버(변수와 메서드)를 이야기한다. 따라서 생성자와 초기화 블록은 상속의 대상이 아니다.

 

상속의 관계에 있어서 물려주는 클래스를 부모클래스라 하며, 상속받는 클래스를 자식 클래스라 한다.

상속받은 클래스는 물려받은 멤버들을 자신의 것처럼 사용할 수 있기 때문에 코드의 절감 효과를 가져오게 된다. 또한, 부모의 코드를 변경하면 모든 자식 클래스에게도 적용되므로 유지보수성 역시 향상된다.

프로그램 코드에서 상속을 적용할 떄는 자식 클래스의 선언부에서 extends 키워드와 함께 조상 클래스 이름을 표시한다.

더 자세히 알고 싶다면 https://goldtree-of-ce.tistory.com/96?category=1039756 서 확인하자.

 

다형성(Polymorphism)

다형성은 상속과 깊은 관계가 있으므로 학습하기에 앞서 상속에 대해 충분히 알고 있어야 한다.

객체지향개념에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미하며, 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다.

이를 좀 더 구체적으로 말하자면, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다.

 

지금까지 우리는 생성된 인스턴스를 다루기 위해서, 인스턴스의 타입과 일치하는 타입의 참조변수만을 사용했다.

일반적인 경우 인스턴스의 타입과 참조변수의 타입이 일치하는 것이 보통이지만, 서로 상속관계에 있을 경우, 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.

반대로 아래와 같이 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 불가능하다.

class Tv {
    boolean power;
    int channel;

    void power(){
        power = !power;
    }
    void channelUp() { ++channel;}
    void channelDown() { --channel;}
}

class CaptionTv extends Tv{
    String text;
    void caption(){
        System.out.println("captionTV");
    }
}

public Polymorphism(){
    public static void main(String[] args){
        //success!!
        Tv t = new Tv();
        CaptionTv c = new CaptionTv();
        
        //Success!!
        Tv t = new CaptionTv();
        
        //Fail!!
        CaptionTv c = new Tv();
    }
}

 

참조변수의 형변환

기본형 변수와 같이 참조변수도 형변환이 가능하다. 단, 서로 상속관계에 있는 클래스사이에서만 가능하기 때문에 자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능하다.

조상타입의 참조변수를 자손타입의 참조변수로 변환하는 것을 다운캐스팅이라고 하며, 자손타입의 참조변수를 조상타입의 참조변수로 변환하는 것을 업캐스팅이라 한다.

 

기본형 변수의 형변환에서 작은 자료형에서 큰 자료형의 형변환은 생략이 가능하듯이, 참조형 변수의 형변환에서는 자손타입의 참조변수를 조상타입으로 형변환하는 경우에는 형변환을 생략할 수 있다.

하지만, 조상타입의 참조변수를 자손타입으로 형변환하는 것은 참조변수가 다룰 수 있는 멤버의 개수를 늘이는 것이므로, 실제 인스턴스의 멤버 개수보다 참조변수가 사용할 수 있는 멤버의 개수가 더 많아지므로 문제가 발생할 가능성이 있다.

그래서 자손타입으로의 형변환은 생략할 수 없으며, 형변환을 수행하기 전에 instanceof연산자를 사용해서 참조변수가 참조하고 있는 실제 인스턴스의 타입을 확인하는 것이 안전하다.

CaptionTv c = new CaptionTv();
Tv t = (Tv)c;

형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 떄문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.

단지 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것뿐이다.

 

Instanceof연산자

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof연산자를 사용한다.

주로 존건문에 사용되며, instaceof의 왼쪽에는 참조변수를 오른쪽에는 타입(클래스명)이 피연산자로 위치한다.

 

참조변수와 인스턴스의 연결

조상 타입의 참조변수와 자손 타입의 참조변수의 차이점이 사용할 수 있는 멤버의 개수에 있다고 배웠다.여기서 한 가지 더 알아두어야 할 내용이 있다.

조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스변수를 자손 클래스에 중복으로 정의했을 때, 조상타입의 참조변수로 자손 인스턴스를 참조하는 경우와 자손타입의 참조 변수로 자손 인스턴스를 참조하는 경우는 서로 다른 결과를 얻는다.

메서드의 경우 조상 클래스의 메서드를 자손의 클래스에서 오버라이딩한 경우에도 참조 변수의 타입에 관계없이 항상 실제 인스턴스의 메서드(오버라이딩된 메서드)가 호출되지만, 멤버변수의 경우 참조변수의 타입에 따라 달라진다.

 

멤버변수가 조상 클래스와 자손 클래스에 중보으로 정의된 경우, 조상 타입의 참조변수를 사용했을 때는 조상 클래스에 선언된 멤버변수가 사용되고, 자손타입의 참조변수를 사용했을 때는 자손 클래스에 선언된 멤버변수가 사용된다.

class BindingTest{
	public static void main(String[] args){
        Parent p = new Child();
        Child c = new Child();
        
        //p.x = 100
        //Child Method
        System.out.println("p.x ="+p.x);
        p.method();
        
        //c.x = 200
        //Childe Method
        System.out.println("c.x ="+c.x);
        c.method();
    }
}

class Parent{
	int x = 100;
    
    void method(){
        System.out.println("Parent Method");
    }
}

class Child extends Parent{
	int x = 200;
    
    void method(){
    	System.out.println("Child Method");
    }
}

 

매개변수의 다형성

참조변수의 다형적인 특징은 메서드의 매개변수에도 적용된다.

class Product{
    int price;
    int bonusPoint;
}

class Tv extends Product {}
class Computer extends Product {}
class Audio extends Product {}

class Buyer{
    int money = 1000;
    int bonusPoint = 0;
	
    void buy(Product p){
        money -= p.price;
        bonusPoint += p.bonusPoint;
}

 

추상클래스(abstrcat class)

클래스를 설계도에 비유한다면, 추상클래스는 미완성 설계도에 비유할 수 있다.

미완성 설계도로 완성된 제품을 만들 수 없듯이 추상클래스로 인스턴스는 생성할 수 없다. 추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.

추상클래스 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖는다.

 

추상클래스는 키워드 'absract'를 붙이기만 하면 된다.

추상클래스는 추상메서드를 포함하고  있다는 것을 제외하고는 일반클래스와 전혀 다르지 않다. 추상클래스에도 생성자가 있으며, 멤버변수와 메서드도 가질 수 있다.

 

추상메서드

메서드를 이와 같이 미완성 상태로 남겨 놓는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 떄문에 조상 클래스에서는 선언부만 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려 주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워 두는 것이다.

추상클래스로부터 상속받는 자손클래스는 오버라이딩을 통해 조상인 추상클래스의 추상메서드를 모두 구현해주어야 한다.

만일 조상으로부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자손클래스 역시 추상클래스로 지정해 주어야 한다.

상속이 자손 클래스를 만드는데 조상 클래스를 사용하는 것이라면, 이와 받대로 추상화는 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것이라고 할 수 있다.

 

인터페이스

인터페이스는 일종의 추상클래스이다.

인터페이스는 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다.

일반적인 클래스의 멤버들과 달리인터페이스 멤버들은 다음과 같은 제약사항이 있다.

  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.

 

인터페이스의 상속

인터페이스는 인터페이브로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속, 즉 여러 개의 인터페이스로부터 상속을 받는 것이 가능하다.

클래스의 상속과 마찬가지로 자손 인터페이스는 조상 인터페이스에 정의된 멤버를 모두 상속받는다.

인터페이스를 구현하기 위해서는 'extends'가 아닌 'implements'를 사용한다.

 

인터페이스를 이용한 다중상속

두 조상으로부터 상속받는 멤버 중에서 멤버변수의 이름이 같거나 메서드의 선언부가 일치하고 구현 내용이 다르다면 이 두조상으로부터 상속받는 자손클래스는 어느 조상의 것을 상속받게 되는 것인지 알 수 없다.

그래서 다중상속은 자바에서는 허용하지 않지만 예외적으로 다중 상속을 할 수 있는 것이 인터페이스다.

하지만 다중상속을 위해 인터페이스가 탄생한것이 아니고 그렇기에 다중상속을 구현하는 경우는 거의 없다.

 

장점

  • 개발시간을 단축시킬 수 있다.
    • 메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문이다.
    • 동시에 다른 한 쪽에서는 인터페이스를 구현하는 클래스를 작성하도록 하여, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행할 수 있다.
  • 표준화가 가능하다.
    • 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.
  • 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
    • 서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지 않은 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.
  • 독립적인 프로그래밍이 가능하다
    • 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램을 작성하는 것이 가능하다. 즉, 결합도를 약화시킬 수 있다.

 

캡슐화(Incapsulation)

생략.

 

[참고]

Java의 정석

 

'java' 카테고리의 다른 글

java_chapter12_generics, enumeration, annotation  (0) 2023.03.11
java_Chapter08_예외처리  (0) 2023.03.10
Java_Chapter?_builder pattern  (0) 2022.10.06
Java_Chapter07_클래스의 관계  (0) 2022.09.15
Java_Chapter06_클래스와 객체  (0) 2022.09.14