Java

abstract

abstract

abstract란 한국어로는 추상으로 번역된다. 이에 대한 정의는 뒤에서 내리도록 하고 지금 단계에서는 abstract라는 것이 상속을 강제하는 일종의 규제라고 생각하자. 즉 abstract 클래스나 메소드를 사용하기 위해서는 반드시 상속해서 사용하도록 강제하는 것이 abstract다. 

추상 메소드

추상 메소드란 메소드의 시그니처만이 정의된 비어있는 메소드를 의미한다. 아래의 코드를 보자.

package org.opentutorials.javatutorials.abstractclass.example1;
abstract class A{
    public abstract int b();
	//본체가 있는 메소드는 abstract 키워드를 가질 수 없다.
	//public abstract int c(){System.out.println("Hello")}
	//추상 클래스 내에는 추상 메소드가 아닌 메소드가 존재 할 수 있다. 
	public void d(){
		System.out.println("world");
	}
}
public class AbstractDemo {
	public static void main(String[] args) {
		A obj = new A();
	}
}

위 코드의 실행 결과는 아래와 같다.

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Cannot instantiate the type A

	at org.opentutorials.javatutorials.abstractclass.example1.AbstractDemo.main(AbstractDemo.java:7)

 메소드 b의 선언 부분에는 abstract라는 키워드가 등장하고 있다. 이 키워드는 메소드 b는 메소드의 시그니처만 정의 되어 있고 이 메소드의 구체적인 구현은 하위 클래스에서 오버라이딩 해야 한다는 의미다. 이렇게 내용이 비어있는 메소드를 추상 메소드라고 부른다. 추상 메소드를 하나라도 포함하고 있는 클래스는 추상 클래스가 되고, 자연스럽게 클래스의 이름 앞에 abstract가 붙는다. 

abstract class A{
    public abstract int b();
}

아래 코드는 오류를 발생시키는데 본체인 {System.out.println("Hello")}가 존재하는데 추상 메소드를 의미하는 abstract를 사용하고 있기 때문이다.

public abstract int c(){System.out.println("Hello")}

추상 클래스에는 추상 메소드가 아닌 메소드가 존재할 수 있다.

public int d(){
	System.out.println("world");
}

아래와 같이 추상 클래스 A를 인스턴스화하면 오류가 발생한다. 그것은 추상 클래스는 구체적인 메소드의 내용이 존재하지 않기 때문에 인스턴스화시켜서 사용할 수 없기 때문이다. 그럼 어떻게 해야 클래스 A를 사용할 수 있을까? 또 이렇게 불편한 추상 클래스는 왜 사용하는 것일까?

A obj = new A();

추상 클래스의 상속

위의 문제를 해결하기 위해서는 클래스 A를 상속한 하위 클래스를 만들고 추상 메소드를 오버라이드해서 내용있는 메소드를 만들어야 한다. 아래 코드를 보자.

package org.opentutorials.javatutorials.abstractclass.example2;
abstract class A{
    public abstract int b();
	public void d(){
		System.out.println("world");
	}
}
class B extends A{
	public int b(){return 1;}
}
public class AbstractDemo {
	public static void main(String[] args) {
		B obj = new B();
		System.out.println(obj.b());
	}
}

차이점은 아래와 같다.

클래스 B는 클래스 A를 상속했다. 그리고 클래스 A의 추상 메소드인 메소드 b를 오버라이딩하고 있다. 그 결과 클래스 A를 사용할 수 있었다.

추상 클래스를 사용하는 이유

추상 클래스는 상속을 강제하기 위한 것이다. 즉 부모 클래스에는 메소드의 시그니처만 정의해놓고 그 메소드의 실제 동작 방법은 이 메소드를 상속 받은 하위 클래스의 책임으로 위임하고 있다. 사실 코드를 이런 식으로 작성하는 경우는 작은 규모의 프로젝트에서는 거의 없다. 예제를 통해서 추상 클래스의 용도를 생각해보자.

아래 코드는 계산기 예제에 추상 클래스의 개념을 도입한 것이다.

package org.opentutorials.javatutorials.abstractclass.example3;
abstract class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    } 
    public abstract void sum();  
    public abstract void avg();
    public void run(){
        sum();
    	avg();
    }
}
class CalculatorDecoPlus extends Calculator {
	public void sum(){
		System.out.println("+ sum :"+(this.left+this.right));
	}
	public void avg(){
		System.out.println("+ avg :"+(this.left+this.right)/2);
	}
} 
class CalculatorDecoMinus extends Calculator {
	public void sum(){
		System.out.println("- sum :"+(this.left+this.right));
	}
	public void avg(){
		System.out.println("- avg :"+(this.left+this.right)/2);
	}
} 
public class CalculatorDemo {
    public static void main(String[] args) { 
    	CalculatorDecoPlus c1 = new CalculatorDecoPlus();
        c1.setOprands(10, 20);
        c1.run();
        
        CalculatorDecoMinus c2 = new CalculatorDecoMinus();
        c2.setOprands(10, 20);
        c2.run();
    }
  
}

계산기 예제와의 차이점은 아래와 같다. (너무 많아서 의의가 있는지 모르겠다)

결과는 다음과 같다.

+ sum :30
+ avg :15
- sum :30
- avg :15

위의 예제는 합계(sum)를 실행하고 평균(avg)을 실행하는 절차를 메소드 run을 통해서 한 번에 실행되도록 한 코드이다. 그런데 경우에 따라서 합계와 평균을 화면에 출력하는 모습을 달리해야 하는 경우가 있다고 치자. 그런 경우에 상황에 따라서 동작 방법이 달라지는 메소드(sum, avg)는 추상 메소드로 만들어서 하위 클래스에서 구현하도록 하고 모든 클래스의 공통분모(setOprands, run)의 경우에는 상위 클래스에 두어서 코드의 중복, 유지보수의 편의성 등을 꾀할 수 있다.

디자인 패턴

 

지금 단계에서는 난이도가 있으면서 부가적인 내용이다. 다음 내용은 부담 없이 보자.

이러한 개발 방법을 template method pattern이라고도 한다. 아래는 추억의 템플릿이다.

위의 그림에 등장하는 템플릿은 자주 사용하는 모양을 모아둔 것이라고 할 수 있다. 템플릿은 모양을 결정하지만 템플릿을 통해서 그려질 도형은 팬의 종류나 색상에 따라서 달라진다. 즉 공통분모인 메소드 run은 메소드 sum과 avg가 어떻게 동작할지 알 수 없지만 sum이 실행되고 avg을 실행시킨다. 반면에 실행결과를 어떤 기호(+,-)로 시작할지는 하위 클래스에서 결정하고 있다.

즉, 위의 예제를 통해서도 알 수 있지만 프로그래밍이라는 것은 반복되는 패턴이 있다. 이런 패턴들을 모아서 정리한 것이 디자인 패턴(design pattern)이다. 물론 시각 디자이너들의 디자인이 아니라 좋은 소프트웨어를 만들기 위한 설계로서 디자인이라는 표현을 쓰고 있는 것이다. 디자인 패턴의 장점은 크게 두가지이다. 하나는 좋은 설계를 단기간에 학습할 수 있다는 점이다. 물론 비교적 단기간이라는 뜻이다. 다른 하나는 소통에 도움이 된다는 점이다. 설계 방법을 토의하거나 전달할 때 설계 방법에 따라 적절한 이름이 있다면 상호간에 생각을 일치시키는 데 큰 도움이 될 것이다.

지금 당장 디자인 패턴이 필요하지는 않겠지만 이러한 것이 있다는 것은 기억해두자. 프로그래밍에 대한 권태가 왔을 때 시작하기 딱 좋은 주제가 디자인 패턴이다.

댓글

댓글 본문
작성자
비밀번호
  1. 추상과 생성자
    앗 그러네요. 그리고 제가 생성자를 적을 때 좀 실수를 한것도 있었네요. 블루님 감사합니다 ~
    대화보기
    • 블루
      public class Playground {

      public static void main(String[] args) {
      T2 t2 = new T2();
      t2.t2();
      t2.sum();
      }

      로 수정하면 정상으로 나오네요
      대화보기
      • 추상과 생성자
        package org.opentutorials.javastudy;

        abstract class T1{
        int left, right;
        String name;

        public void T1(){
        left = 1;
        right = 1;
        name = "my name is T1";
        }

        public abstract void sum();
        }


        class T2 extends T1{
        public void T2(){
        super.T1();

        this.left = 2;
        this.right = 2;
        this.name = "my name is T2";
        }

        public void sum(){
        System.out.println(this.left + "와" + this.right + "와" + this.name + "입니다");
        }
        }


        public class Playground {

        public static void main(String[] args) {
        T2 t2 = new T2();
        t2.sum();
        }
        }

        /* -------------------------------------------------------------------------------- */
        0와0와null입니다 <------------------ 결과값

        T2를 인스턴스 할 시 임의로 설정한 T2의 생성자 값들이 T1의 전역변수로 들어가서 출력될거라 예상했는데
        결과는 빈 값으로 나옵니다.

        추상클래스의 특성 문제인지 아니면 생성자 설정의 문제인지 그 외 다른 문제인지 잘 모르겠습니다.
        뭐가 문제일까요....
      • Chris DW
        감사합니다~ 여러모로 정말 큰 도움 받고 있습니다~!! 감사합니다~ 복 받으실꺼에요~ :)
      • 감사합니다!!
      • joo0914krs
        감사합니다
      • JustStudy
        고맙습니다
      • 김트라슈
        감사합니다.
      • 찐똥구리구리
        저희 고객님께서 특수문자도 자기가 쓰고 싶다고 하셔서 ㅎㅎㅎ 조금 바꿔봤습니다... 짱 재밌어요~~

        package space.nobodj.javatutorials.abstractclass;

        abstract class Calculator{
        int left, right;
        String special;
        public void setOprands(int left, int right, String special){
        this.left = left;
        this.right = right;
        this.special = special;
        }
        public abstract void sum();
        public abstract void avg();
        public void run(){
        sum();
        avg();
        }
        int _sum(){
        return this.left + this.right;
        }
        }
        class CalculatorUserDeco1 extends Calculator {
        public void sum(){
        System.out.println( this.special + " sum :"+_sum());
        }
        public void avg(){
        System.out.println(this.special + " avg :"+_sum()/2);
        }
        }
        class CalculatorUserDeco2 extends Calculator {
        public void sum(){
        System.out.println(this.special + " sum :"+_sum());
        }
        public void avg(){
        System.out.println(this.special + " avg :"+_sum()/2);
        }
        }
        public class CalculatorDemo {
        public static void main(String[] args) {
        CalculatorUserDeco1 c1 = new CalculatorUserDeco1();
        c1.setOprands(10, 20,"$,.$");
        c1.run();

        CalculatorUserDeco2 c2 = new CalculatorUserDeco2();
        c2.setOprands(10, 20,"^,.^;;");
        c2.run();
        }

        }

        감사합니다~~^^
      • 오빠는다르다
        감사합니다.
      • 레니타키
        좋은 설명 감사합니다.
      • 초급코딩
        2번째 동영상에서 굳이 abstract 을 사용하지 않아도 가능한데 이를 사용하는 이유는 나의 coding 을 다른 사람이 읽을때 coding 을 훨씬 구조적으로 빨리 이해시키게끔하는데 있나요?
      • 박첩구드
        감사합니다...디자인 패턴이라...많은 생각을 하게하네요!
      • 이재혁
        감사합니다~
      • cocohodu
        좋은 강의 감사합니다
      • egoing
        모든 피드백은 다 고마운거죠. 화이팅 ^^
        대화보기
        • kadlrea@hanmail.net
          타이핑 때문에 그런건 아니에요 필요하다면 타이핑 연습도 할겸 되도록 쳐보는것도 좋은데
          물론 영문이요 ㅋ

          문제는 메소드 같은 이름들을 너무 길게 하셔서 구문을 보고 파악 할때 이름이 같은지 보는것만 으로도 너무 곤란해 질때도 많더라구요

          물론 이클립스 같은 곳에 구문을 복사해놓고 보면 되겠네요 ㅜ

          이런강의 보면서 배우는 입장에서 감사하다는 말보다 불평을 늘여 놓아 죄송합니다.
          각자의 강의 스타일이 있는것도 개성인데 제가 오지랍이 넓었네요 ; 자바 관련 동영상을 이것저것 찾아 보고 있지만 이해안가는 부분이 있으면 여기와서 해답을 찾아가곤해요 ^^ 이런부분에서는 언젠가 보답할 길이있으면 보답해드리고 싶구요

          댓글을 지우고 싶지만 이미 보셨기때문에 지울 필요성은 없을것 같고 저와 같은 의견이 있는 분들이 있으시면 답변을 공유하기 위해서라도 ㅋ

          두서 없이 막글을 쓰긴했는데 결론은 정말 감사드리는 맘으로 강의 잘보고 있습니다.
        • egoing
          최근엔 에디터가 발전해서 자동으로 타이핑을 해주니까요. 그런 기능을 잘 활용하시면 좀 덜 힘드실꺼예요. :)
          대화보기
          • kadlrea@hanmail.net
            메소드 이름 같은건 왜케 길게 하시는지....... ㅡ.ㅡ
          • 퍼플레인
            감사합니다~ 수업을 듣고 있는데도 처음 접하는 언어이다 보니 힘든 부분이 있었는데 이해하기 쉽게 설명해주시니 이해가 되네요~
          • 요타바이트
            굿
          • 비실이
            질문있습니다. 추상클래스 개념 도입한 계산기예제 돌려보니 c1.run이랑 c2.run부분에서 예외가 발생하는데
            그럼 코드가 잘못된건가요??
          • J.Yu
            정말 좋은강의 감사드립니다!
            JSP를 시작하기 전 JAVA의 기초 강의로써 정말 부족함 없는 강의인것 같습니다 ^^!

            이미 알고 배웠던 내용이지만, 다시 한번 복습을 통해서 확실히 이해하고
            Spring을 배우기 위한 밑거름이 될 수 있을것만 같아서 참 감사드립니다! ^^!
          • david20jazz
            정말 훌륭한 강의 입니다.
          • kanadel
            참 궁금했던게 많았는데
            이 수업은 기본적으로 왜 쓰이는지 왜 있는지에 대한 설명이 있어서
            배우는데 큰 도움이 되는거 같습니다.
            좋은 강의 감사해요
          • 심비행기
            강의를 볼때는 잘 이해가 되고 좋은데!!
            막상 아무것도 없는 백지에서 이걸 코딩할땐
            뭘 어떻게 쳐야될지.. 또 어떻게 써먹어야 할지.....
            미래를 생각하면 두렵기만 하네요.

            좋은 강의 감사합니다 ㅎㅎ
          • Sirhc
            부럽네요... 쉽다니...
            대화보기
            • Kitak Kim
              자바 공부가 쉬웠어요 ^^
            • 늦장공부
              겨우 두시간전 댓글들을 보니 ^^ 실시간 공부하는 것 같아서... 기분이 좋네요
              시즌2 부터는 잘 모르겠고 개념이 어렵네요..
              여러번 보는 마음으로 몰라도 일단 넘어가고 있어요
            • egoing
              수정했습니다. 고맙습니다!
              대화보기
              • Paul
                별로 중요한 것은 아닙니다만 abstract 철자가 틀린 곳이 몇군데 있네요.
                예를 들어 아래와 같은 문장에서,
                "즉 abstarct 클래스나 메소드를 사용하기 위해서는 반드시 상속해서 사용하도록 강제하는 것이 abstarct다."
                abstarct (X) -> abstract (O)

                중요한 것은 아니지만 일단 보고 드립니다.

                언제나 감사드립니다.!
              버전 관리
              egoing
              현재 버전
              선택 버전
              graphittie 자세히 보기