[JAVA] 추상클래스

업데이트:



2023.06.15 내용 추가

오늘은 추상클래스에 대해서 알아보자! 추상클래스에서 가장많이 사용하는것이 추상메소드이므로 추상메소드부터 차근차근 알아보자!





✅ 추상메소드란?

추상 메소드(abstract method) 란 자식 클래스에서 반드시 오버라이딩해야만 사용할 수 있는 메소드를 의미한다.

자바에서 추상 메소드를 선언하여 사용하는 목적은 추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 하기 위함이다.

예를 들면 모듈처럼 중복되는 부분이나 공통적인 부분은 미리 다 만들어진 것을 사용하고, 이를 받아 사용하는 쪽에서는 자신에게 필요한 부분만을 오버라이딩하여 사용함으로써 생산성이 향상되고 배포 등이 쉬워지기 때문이다.

이러한 추상 메소드는 선언부만 존재하며, 구현부는 작성하지 않는다.

바로 이 작성되어 있지 않은 구현부를 자식 클래스에서 오버라이딩하여 사용하는 것이다.





✅ 추상메소드의 선언

 접근제어자 abstract 반환타입 메소드이름();
 // 예시
 public abstract void Calculate();
 public abstract String PrintResult();
 void Reset();      // 접근제어자와 abstract는 생략가능




✅ 추상 클래스란?

하나 이상추상메소드를 포함하는 클래스를 추상 클래스라고 한다. 추상 클래스는 추상메소드를 포함하고 있는것을 제외하면 일반 클래스와 완전히 동일하다.



📌 추상클래스의 특징

  • 추상메소드를 포함하고 있기 때문에 실체성, 구체성이 없어 인스턴스 (객체)를 생성할 수 없다.
  • 추상클래스와 실체 클래스는 상속관계!
  • 추상클래스에서 선언된 추상메소드는 여러개의 하위클래스에서 자유롭게 사용가능 하지만, 결국 모든 추상메소드는 반드시 오버라이딩이 되어있어야만 한다!





✅ 추상클래스 예제

import calculate_Ver_Adapter.CalResult;
import lombok.Data;

@Data
public abstract class CalAbstract implements CalResult {
	
	private int a;
	private int b;
	private String op;
	private double sum=0;
	
	private StringBuffer sb = new StringBuffer();
	private double result=0;

	
	public void add() {
		setSum(getSum()+getA());
		setResult(getSum());
	}
	public void sub() {
		setSum(getSum()-getA());
		setResult(getSum());
	}
	public void mul() {
		setSum(getSum()*getA());
		setResult(getSum());
	}
	public void div() {
		setSum(getSum()/getA());
		setResult(getSum());
	}
	public char judgeOp(String op) {
		if (op.equals("+")) {
			return op.charAt(0);
		} else if (op.equals("-")) {
			return op.charAt(0);
		} else if (op.equals("*")) {
			return op.charAt(0);
		} else if (op.equals("/")) {
			return op.charAt(0);
		}
		return 'X';
	}
	
	// 실제 계산 수행
	public void calculate() {
		if (getOp().equals("+")) {
			add();
		} else if (getOp().equals("-")) {
			sub();
		} else if (getOp().equals("*")) {
			mul();
		} else if (getOp().equals("/")) {
			div();
		}
	}

	// 입력받은게 숫자인지 기호인지 판단하는 객체
	public boolean judgeCal(boolean temp) {
		if (temp == true)
			return true;
		else
			return false;
	}

	// 숫자면 true, 기호면 false return
	public boolean judgeNumOrOp(String temp) {
		return temp.matches("-?\\d+");
	}

	
	// 추상메소드
	public abstract void cal();
	
	@Override
	public void PrintResult(double result) {
		System.out.println("결과 : "+ result);
	}
	
	@Override
	public void PrintProcess(StringBuffer sb) {
		System.out.println("과정 : " + sb);
	}
}


일반클래스처럼 보이지만.. 추상메소드가 단 한개! 포함된 추상클래스이다. 이렇게 추상메소드가 단 한개만 있어도 우리는 이를 추상클래스라고 정의한다. 그러나 추상클래스에 반드시 추상메소드에 포함될 필요는 없다. public abstract class CalAbstract implements CalResult 처럼 선언부에 abstract class 를 선언하면 추상클래스라고 볼 수 있다.

그렇다면 이 추상클래스를 상속받은 클래스를 한번 살펴보자.

import java.util.Scanner;
import java.util.StringTokenizer;

import lombok.Data;

@Data
public class ContinuousCal extends CalAbstract{
	
	Scanner sc = new Scanner(System.in);
	CalAdapter calAdapter = new CalAdapter();
	

	// 연속 계산 수행
	// 추상메소드 구현부
	@Override
	public void cal() {
		while (true) {
			String str;
			str = sc.nextLine();

			StringTokenizer st = new StringTokenizer(str, "\\+|\\-|\\*|\\/", true);

			String temp = st.nextToken();
			if (temp.equals("=")) {
				/////////// 얘를 res.result()로 하면 왜안됨???
				//res.result();
				//System.out.println("연산과정 : " + getSb().toString());
				//System.out.println("결과 : " + getResult());
				
				calAdapter.Display(getSb(),getResult());
				

				
				
				getSb().setLength(0);
				setResult(0);
				setSum(0);
				break;
			}

			// 첫번째 문자가 숫자인지 기호인지를 판단하는 코드
			// 숫자면 true, 기호면 false
			boolean IntOrNot = judgeNumOrOp(temp);

			// StringBuffer가 비어있는 경우
			if (getSb().isEmpty()) {
				if (judgeCal(IntOrNot)) {
					int a = Integer.parseInt(temp);
					// a는 int형식으로 저장
					setA(a);
					//System.out.println("a : " + getA());
					
					// StringBuffer에 String형식으로 저장
					getSb().append(temp);
					setSum(getSum()+a);
					//System.out.println("sum : " + getSum());
					setResult(getResult()+a);
					//System.out.println("Result : " + getResult());
				} else {
					System.out.println("숫자를 입력하세요!");
					continue;
				}
			}

			// StringBuffer가 비어있지 않은 경우
			else {
				// 첫번째 문자가 숫자일경우 그대로 연산 진행
				// 숫자인경우(int)
				if (judgeCal(IntOrNot)) {
					int a = Integer.parseInt(temp);
					// a는 int형식으로 저장
					setA(a);
					// System.out.println("a : " + getA());
					// StringBuffer에 String형식으로 저장
					getSb().append(temp);
					calculate();
					//System.out.println("sum : " + getSum());
					//System.out.println("Result : " + getResult());
				}

				// 첫번째 문자가 기호일 경우
				else if (!judgeCal(IntOrNot)) {
					String judgedOp = String.valueOf(judgeOp(temp));
					// judgedOp를 상위생성자에 넣어서 보내준다.
					setOp(judgedOp);
					
					// judgedOp를 Is_A_Cal의 StringBuffer에 넣어준다.
					getSb().append(judgedOp);	
				}
			}
		}
	}
}


지금 인터페이스랑 추상클래스 등등 객체지향 공부를 다시하면서 만들고 있는 계산기 코드인데 좀 보기 지저분해 보일수 있다.. 아무튼 추상클래스에서 받아온 추상메소드 cal() 을 구현해놓은 것을 볼 수 있다.

근데 여기서 하나 드는 의문점! 아니 애초에 추상클래스에 추상메소드로 선언해서 쓸 필요없이 그냥 일반클래스에 직접 코드를 작성하면 되는거 아닌가?

물론 그래도 상관없다. 지금은 간단한 계산기 코드기 때문에 상관없지만, 메소드가 엄청나게 많아지게 되고, 디자인패턴이 적용되고, 반드시 써야하는 메소드, 선택적 메소드, 이렇게 코드가 복잡해지게 되면 정해진 틀에 맞춰 개발하는게 훨씬 편리해진다.


추상클래스는 일종의 틀 을 제공한다고 보면 된다. 추상메소드를 통해 사용해야만 하는 메소드를 강제하여 일종의 규격을 만들어주는 것이다.

그런데 공부를 하다보면 추상클래스와 인터페이스의 차이점을 잘 모르겠다. 추상클래스가 인터페이스의 역할을 전부다 하고 있는데 인터페이스는 왜필요한걸까? 이는 다음포스팅에 다뤄보도록 하겠다..!

댓글남기기