본문 바로가기
TIL/Java | Spring Boot

2021.8.12 TIL : [Java] 기초 문법2 - 객체지향 프로그래밍 응용(2)

by yeon_zoo 2021. 8. 12.

1. 추상 클래스

추상 클래스는 일반 클래스와 인스턴스를 생성할 수 없다. 따라서 추상 클래스를 사용하려면 먼저 서브 클래스에서 추상 클래스를 상속받은 후 서브 클래스의 인스턴스를 생성해야 한다. 

자동차 클래스를 추상 클래스로 지정하면 자동차 클래스는 직접 인스턴스를 만들 수 없고 슈퍼 클래스의 역할만 한다. 그리고 추상 클래스를 만들기 위해서는 클래스 이름 앞에 abstract 키워드를 사용하면 된다.

abstract class Car8{
	int speed = 0;
	String color;
	
	void upSpeed(int speed) {
		this.speed += speed;
	}
}
class Sedan8 extends Car8{
}
class Truck8 extends Car8{
}
public class Ex12_08 {

	public static void main(String[] args) {
		//Car8 car8 = new Car8();
		Sedan8 sedan8 = new Sedan8();
		System.out.println("추상 클래스 세단 생성 완료~");
		Truck8 truck8 = new Truck8();
		System.out.println("추상 클래스 트럭 생성 완료~");
	}

}

주석 처리된 부분을 코드로 돌리면 에러가 난다. Car8이 추상클래스이기 때문에 인스턴스를 만들 수 없기 때문이다. 

 

추상 메소드

메소드 앞에 abstract를 붙인 추상 메소드는 본체 코드가 존재하지 않는다. 즉 본체가 없는 껍데기 메소드이다. 다음은 추상 메소드를 만드는 형식이다.

abstract 반환형메소드이름(파라미터);

예를 들면 다음과 같다

abstract void upSpeed(int speed);

그런데 본체 코드가 없다면 어떻게 작동하는 걸까? 추상 메소드의 목적은 상속 받아 메소드를 오버라이딩해 사용하는 것이다. 슈퍼 클래스에서는 이런 메소드가 있다고 껍데기를 만들어 놓고 각 서브 클래스에서 채워넣는 방식이다. 코드의 예를 들면 다음과 같다. 

 

추상 메소드가 하나라도 포함된 클래스는 추상 클래스로 지정해야 한다. 추상 메소드를 슈퍼클래스에 만들지 않고 서브 클래스에 별도로 메소드들을 생성해줘도 된다. 하지만 추상 메소드로 만드는 목적은 해당 추상 클래스(슈퍼 클래스)를 상속받은 서브 클래스들은 반드시 추상 메소드를 만들어야 한다는 의미이다. (추상 메소드가 있는 추상 클래스를 상속받은 서브 클래스에서 추상 메소드를 오버라이딩 하지 않으면 오류가 발생한다.)

abstract class Car9{
	int speed = 0;
	String color;
	
	void upSpeed(int speed) {
		this.speed+=speed;
	}
	abstract void work();
}

class Sedan9 extends Car9{
	void work() {
		System.out.println("승용차가 사람을 태우고 있습니다.");
	}
}
class Truck9 extends Car9{
	void work() {
		System.out.println("트럭이 짐을 싣고 있습니다.");
	}
}
public class Ex12_09 {

	public static void main(String[] args) {
		Sedan9 sedan9 = new Sedan9();
		sedan9.work();
		Truck9 truck9 = new Truck9();
		truck9.work();
	}

}

2. 인터페이스

인터페이스는 추상 클래스와 거의 비슷하며 다중 상속을 위해 사용한다. 인터페이스 역시도 추상 클래스처럼 직접 인스턴스를 생성할 수 없다. 둘의 차이점이라면 인터페이스는 일반 메소드, 생성자를 가질 수 없다는 것이다. 또한 필드도 static final을 붙인 상수화(변하지 않음)한 필드만 사용할 수 있으며 반드시 초기화해야 한다. 인터페이스의 형태는 다음과 같다.

interface 인터페이스 이름{
    // static final 필드 및 추상 메소드 정의
}

이 때 상속은 extends 대신 implements 를 쓴다. 

interface 자동차 {
    abstract 동작한다();
}

class 승용차 implements 자동차{
    동작한다() { //오버라이딩
        /*사람을 태우는 동작*/
    }
}

인터페이스를 상속받는 것도 추상 클래스 상속과 크게 다르지 않다. 인터페이스에는 추상 메소드만 가능하기 때문에 동작한다() 추상 메소드 앞 abstract는 생략해도 된다. 인터페이스는 주로 상속받는다는 표현 보다는 구현한다고 표현한다. 

 

 

인터페이스 구현

interface Car10 {
	static final int CAR_COUNT = 0;
	
	abstract void work();
}
class Sedan10 implements Car10{
	public void work() {
		System.out.println("승용차가 사람을 태우고 있습니다.");
	}
}
class Truck10 implements Car10{
	public void work() {
		System.out.println("트럭이 사람을 태우고 있습니다.");
	}
}
public class Ex12_10 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Sedan10 sedan10 = new Sedan10();
		sedan10.work();
		Truck10 truck10 = new Truck10();
		truck10.work();
	}
}

인터페이스를 만들 때는 void, int 등의 반환형은 지정하지 않는다. 또한 CAR_COUNT는 final 클래스 변수로 지정되었기 때문에 Car10.CAR_COUNT와 같이 직접 접근이 가능하지만 값을 변경할 수는 없다. 이 때 인터페이스에 들어가는 필드는 당연히 static final로 인식되기 때문에 int CAR_COUNT= 0 과 같이 생략해서 써도 된다. 

 

다중 상속

다중 상속은 하나의 서브 클래스에서 여러 개의 클래스에서 상속받는 것을 의미한다. 예를 들어 탱크는 자동차의 속성도 가지고 있고 대포의 속성도 가지고 있다. 따라서 2개의 슈퍼 클래스인 자동차와 대포를 이용하여 서브 클래스인 탱크를 만드는 것이 다중 상속이다. 이를 코드로 표현하면 다음과 같다.

class 자동차{
	동작한다();
}

class 대포{
	발사한다();
}

class 탱크 extends 자동차, 대포{
	동작한다(){ //오버라이딩
    	/*탱크가 움직이는 코드를 구현*/
    }
	발사한다(){ //오버라이딩
    	/*탱크에서 대포를 발사하는 코드를 구현*/
    }
}

하지만 자바에서는 클래스 다중 상속을 허용하지 않는다. 그래서 그 대안으로 인터페이스 다중 상속을 하는 것이다. 위의 내용에서 자동차, 대포 클래스만 인터페이스로 변환해 주면 된다.

interface Car11 {
	 void work();
}
interface Cannon{
	void fire();
}
class Tank implements Car11, Cannon{
	public void work() {
		System.out.println("탱크가 사람을 태우고 있습니다.");
	}
	public void fire() {
		System.out.println("탱크가 대포를 발사합니다.");
	}
}

public class Ex12_11 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Tank tank = new Tank();
		tank.work();
		tank.fire();
	}

}

 

3.  객체지향 프로그래밍 총정리

필드(변수) 앞에 붙는 키워드 :

  • private : 클래스로는 접근 불가 / 메서드 이용 필요
  • static : 클래스 필드 - 각 인스턴스 필드 아니고 클래스에 해당하는 필드
  • final : 상수화 (오버라이딩 금지)
  • protected : 같은 클래스나 패키지에서만 접근 가능

 

메소드 앞에 붙는 키워드 :

  • public
  • static : 클래스 매서드 - 각 인스턴스 매서드 아니고 클래스에 해당하는 메서드
  • final : 오버라이딩 금지
  • abstract : 추상 메서드 (본체 코드 없음. 상속 받은 서브 클래스에서 반드시 정의 필요)

 

그 외 :

  • 메소드 상속에 쓰이는 extends (다중 상속 불가능)
  • 인터페이스 상속에 쓰이는 implements (다중 상속 가능)
  • 클래스 앞에 쓰이는 abstract (추상 클래스 - 인스턴스 생성 불가. 상속만 가능)

댓글