본문 바로가기
Java

[Java OOP 심화] 추상화 (Abstract)

by jphwany 2022. 5. 29.

추상화


한 문장으로 표현하자면
필요한 모든 세부 사항만 보여주고 나머지는 숨기는 과정이라고 볼 수 있다

자바에선 일종의 미완성 클래스 라고 할 수 있는 추상 클래스를 제공하며
추상클래스는 직접적으로 객체 인스턴스를 생성할 수 없지만, 새로운 클래스를 작성하는데
밑바탕이 되는 역할을 해준다는 것에서 의미가 있다

또한 추상 클래스를 사용하기 위해선 상속을 받아야하고, 상속받은 모든 추상 메소드는 반드시 구현해줘야 한다
설계로서 틀을 갖추고 클래스를 작성할 수 있게 한다는 기능적인 측면으로 바라보면 이해가 편하다


abstract 제어자


abstract 의 사전적 의미는 추상적인 이라는 형용사인데

자바의 맥락에서 이 단어가 내포하는 의미는 미완성 이라고 할 수 있다

주로 클래스와 메소드를 형용하는 키워드로 사용되는데
이 메소드 앞에 붙은 경우를 추상 메소드, 클래스 앞은 추상 클래스 라고 부른다

어떤 클래스에 추상 메소드가 포함되어있는 경우엔 그 클래스도 추상 클래스가 된다

abstract class Main{
abstract void example();
}

 

추상 메소드가 최소 하나 이상 포함되어 있는 클래스로 보이고
메소드 바디가 없는 추상 메소드도 보인다

메소드 시그니처만 존재하고 바디가 없는 메소드를 앞에 abstract을 붙여서 추상 메소드임을 명시한다

메소드 바디가 없기 때문에 객체 생성을 할 수가 없다 

그러면 이건 왜 사용하는건가?

상속 관계에 있어서 새로운 클래스를 작성하는데 유용하다는 점을 들 수 있고, 

메소드 내용이 상속 받는 클래스에 따라 달라질 수 있기 때문에 
상위 클래스에서 선언부만 작성하고, 실제 구체적인 내용은 상속 받는 하위 클래스에서 구현하도록
비워두면,  설계하는 상황이 변한다고 해도 좀 더 유연하게 대처할 수 있는 것이다

여기서 사용하게 될 개념은 오버라이딩이다

오버라이딩을 통해 추상 클래스로부터 상속받은 추상 메소드의 내용을 구현해 메소드를 완성시킬 수 있고
이렇게 완성된 클래스를 기반으로 객체를 생성할 수 있다

이러면 중복, 공통적인 부분은 미리 다 만들어진 것만 사용하고 이걸 받아 사용하는 쪽에선
필요한 부분만 재정의해서 생산성이 올라가고 배포가 쉬워진다는 장점이 있다

abstract 반환타입 메소드이름();

 

선언부만 있고 구현부가 없어서 선언부 끝에 ;만 붙여준다

abstract class Fruit {
    // 메소드 바디가 없다. 과일 속성을 데이터 타입과 함께 적어줬었던 것과 다르게..
    public abstract void flavor();
    // flavor() 추상 메소드 선언, 이 부분을 포함하는 Fruit 클래스 또한 abstract 추상클래스화
    public void color(){
        System.out.println("빨간 색");
    }
}

class Apple extends Fruit{
    public void flavor(){
        // flavor() 메소드 오버라이딩 구현부 완성
        System.out.println("사과 맛");
    }
}


class Main_Fruit{
    public static void main(String[] args) {

        Fruit apple = new Apple();
        apple.color();
        apple.flavor();
    }
}

//output 
빨간 색
사과 맛

 

추상 클래스를 활용했을 때 상속을 받는 하위 클래스에서 오버라이딩을 통해 메소드 구현이 가능하다는 것을 볼 수 있다

객체의 공통적 속성과 기능을 추출해서 정의하는 것이라고 정리할 수 있는데
Fruit(과일)의 flavor(맛), color(색깔) 이라는 공통적 특성을 모아서 추상클래스로 선언해주고

상속된 하위 클래스에서 오버라이딩을 통해 클래스의 구체적인 내용을 결정해줬다  (사과 맛)

여러 사람이 같이 개발하는 환경이라고 가정했을 때, 공통된 속성, 기능임에도 각기 다른 변수와 메소드로 정의되는
그런 불상사, 오류를 미연에 방지할 수 있는 것이다


final

final 키워드는 필드, 지역변수, 클래스 앞에 위치할 수 있고 위치에 따라 의미가 조금씩 다르다

클래스 앞일 때는 변경, 확장이 불가능한 클래스이며 상속 불가
메소드 앞일 때는 오버라이딩 불가
변수 앞일 때는 값 변경 불가한 상수

아무튼 공통점은 변경이 불가능하고 확장할 수 없다는 점이다


 

인터페이스


흔히 인터페이스라고 하면 서로 다른 시스템, SW 같은 것들을 서로 이어주는 부분이나 그런 접속 장치를 의미하는데

자바에서 인터페이스도 이러한 기능을 가진다

동일한 목적 아래에 동일한 기능을 수행하게 강제하는, 자바의 다형성을 극대화해서 소스 코드 수정을 줄이고
프로그램 유지보수성을 높이기 위한 기능이다

추상클래스와 비교하는데 그 이유는

인터페이스도 추상 클래스처럼 객체 지향 추상화를 구현한다는 점에서 비슷한 역할을 수행하지만
추상 클래스랑 다르게 1가지 차이점이 있다

인터페이스가 추상클래스보다 더 높은 추상성을 가지고 있다는 것

이게 대체 무슨 소리지 ?

추상성을 더 높게 가지고 있다니

추상 클래스가 구현부가 미완성된 추상 메소드, 멤버변수를 포함할 수 있는 반면에
인터페이스는 오로지 추상 메소드와 "상수"만 멤버로 가질 수 있고 다른 요소는 포함 될 수 없다

구조를 살펴보면, 기본적으로 클래스 작성하는 것과 유사하지만
class 대신 interface를 사용한다 
인텔리제이에서 클래스 추가할 때 보면 인터페이스가 있는 것을 볼 수 있다

일반 클래스랑은 다르게 내부 모든 필드가 이미 public static final 로 정의되고
static, default 메소드 이외의 모든 메소드가 public abstract 으로 정의된다는 차이점도 있다

그렇지만 모든 인터페이스 필드와 메소드에는 위 요소가 내포되어 있기 때문에 명시하진 않는다 (생략되니까)

객체 생성을 할 수 없고 생성자도 포함할 수 없다

그럼 왜 사용할까 ? 

간단하게 예시를 들어보면

군 부대에 쓰이는 스파이더망 이라고 부르는 유선 망이 있는데 
각 노드엔 망 구축 장비가 존재한다

어느 한 노드에서 보안 부분에서 치명적인 결함이 발생해 전부 교체할 일이 생겼다고 하면
다른 노드에서 쓰는 기존의 장비들도 교체해야 한다

그런데, 장비 모듈이 제각각 다른 회사 외주로 만든 것들이었다면? 

전부 제각각 다시 모듈 프로그램을 만들고 망을 재구축해야 할 것이다

이런 일이 발생하게되면 말도 안되는 큰 혼란이 벌어질 것이다

그러니 군 장비는 대체로 규격에 맞는 같은 모듈로 통합해서 관리된다

유선 망을 책임지고 원활하게 보안성있게 작동하는 이러한 동일한 기능을 제공하는 것이

인터페이스라고 할 수 있고 이런 부분 때문에 사용한다


인터페이스 구현 문법

interface 키워드를 통해 선언, implements 키워드를 이용해 일반 클래스에서 인터페이스를 구현할 수 있다

public interface 인터페이스명 {
	//상수
타입 상수명 = 값;

	//추상 메소드
타입 메소드명(매개변수, ... );

	//디폴트 메소드
default 타입 메소드명(매개변수, ... ){
	  //구현부
}

//정적 메소드
static 타입 메소드명(매개변수) {
  	//구현부
 }
 
}

상수 : 절대적으로 변하지 않는 값

추상 메소드 : 오버라이딩으로 구현

디폴트 메소드 : 기본 구현부를 제공하고 이게 마음에 들지 않으면 각자 오버라이딩을 해서 재구현할 수 있도록
선택적인 메소드를 가이드한다.
추상 메소드를 인터페이스에 추가한다고 하면 이걸 implements 했던 모든 클래스에서 강제적으로 추상 메소드를 구현해야 하고 안하면 전부 에러가 나기 때문에.. 불가피하게 수정이 필요할 때 효과적이다

정적 메소드 : 제공해주는 것으로 무조건 사용

예시를 들어보면,

public interface Fruit_interface {
    public void flavor(); // 메소드 바디가 없다
    public void color();
}

class Melon implements Fruit_interface{
    public void flavor(){
        System.out.println("메론 맛");
    }

    public void color() {
        System.out.println("초록 색");
    }
}

class Main{
    public static void main(String[] args) {
        Melon melon = new Melon();
        melon.color();
        melon.flavor();
    }
}
// Output
초록 색
메론 맛

Fruit_interface 라는 인터페이스를 생성
모든 인터페이스 필드와 메소드에는 public static final로 정의되기 때문에 이러한 요소들은 생략이 가능하다

그리고 static, default 메소드 외의 모든 메소드들은 public abstract 으로 정의된다

인터페이스를 구현하기 위해 implements를 사용해서 Melon 이라는 클래스를 상속시킨다
flavor(), color() 메소드를 사용할 수 있다 (메소드 오버라이딩)

뒤에 나올 컬렉션 부분에서도 인터페이스가 요긴하게 쓰이니까 자주 봐주면 좋을 것 같다

'Java' 카테고리의 다른 글

[Java 컬렉션] 제네릭 (Generic)  (0) 2022.05.29
[Java OOP 심화] 다형성(Polymorphism)  (0) 2022.05.29
[Java OOP 심화] 캡슐화 (Encapsulation)  (0) 2022.05.25
[Java OOP 심화] 상속( Inheritance)  (0) 2022.05.25
[Java OOP 기초] 생성자  (0) 2022.05.25

댓글