Java

[Java] 객체 지향 프로그래밍 - 추상화 (Abstraction)

공대생안씨 2024. 7. 9. 17:26

1. 추상화

  • 클래스 간 공통 속성을 찾아내서 공통 조상을 만드는 것

 

1-1. 추상화 예시 - 시작

공통 조상 도입 전
공통 조상 도입 후

public class Animal {
	public void sound() {
		System.out.println("동물 울음 소리");
	}
}

public class Dog extends Animal {
	@Override
	public void sound() {
		System.out.println("월월");
	}
}

// Cat, Cow 클래스 구현 ...
  • 개, 고양이, 소 ⇒ 실존 o
  • 동물 ⇒ 추상적인 개념일 뿐, 실존 x
                 직접 인스턴스 생성(사용) 하지는 않음!!
  • 개, 고양이, 소 클래스에서 메서드 오버라이딩 하지 않으면 동물 클래스의 메서드가 실행됨
📌 위의 예시와 같이
1. Animal 인스턴스 생성할 수 있는 문제
2. 자식 클래스에서 메서드 오버라이딩 하지 않을 수 있는 문제
이러한 문제가 발생하지 않도록 제약 추가 ⇒ 추상 클래스, 추상 메서드 통해서!

 

1-2. 추상 클래스

  • 부모 클래스는 제공하지만 실제 생성되면 안되는 클래스
  • “미완성 설계도”
  • 추상적인 개념 제공하는 클래스
    • ⇒ 인스턴스 존재 x
    • new 추상클래스(); 불가 : 인스턴스 생성할 수 없음!
  • 상속 목적으로 사용됨, 부모 클래스 역할
  • abstract 키워드 사용
abstract class AbstractAnimal { ... }

 

1-2-1. 추상 메서드

  • 자식 클래스가 반드시 오버라이딩 해야 하는 메서드
    • 자식 클래스가 추상 메서드를 오버라이딩 하지 않으면 자식 클래스도 추상 클래스가 되어야 함!
  • abstract 키워드
  • 메서드 바디 존재 x (선언만 해줌!)
    • 나머지는 일반적인 메서드와 동일
// 추상 메서드 : 자체로는 호출 불가 => 자식 클래스에서 반드시 오버라이딩 해야 함
public abstract void abstractMethod();
📌 추상 메서드를 1개라도 갖는 클래스는 추상 클래스로 선언해야 한다!

 

1-2-2. 추상 클래스, 추상 메서드 예제

// 추상 클래스
public abstract class AbstractAnimal {
	// 추상 메서드
	public abstract void sound();
	
	// 추상 메서드 x => 오버라이딩 여부 상관 x
	public void move() {
		System.out.println("동물 움직임");
	}
}

public class Dog extends AbstractAnimal {
	@Override
	public void sound() {
		System.out.println("월월");
	}
}

// Cat, Cow 클래스 구현 ...
public static void main(String[] args) {
	
	// 추상 클래스의 인스턴스 생성 불가!
	// AbstractAnimal abstractAnimal = new AbstractAnimal();	
}
📌 추상 클래스를 상속 받는 자식 클래스에서 메서드 오버라이딩을 하지 않으면

위의 그림과 같이 호출 불가 ⇒ 컴파일 오류 발생!

 

1-2-3. 순수 추상 클래스

  • 클래스 내의 모든 메서드가 추상 메서드인 추상 클래스
public abstract class AbstractAnimal {
	
	// 추상 메서드만 존재함 => 순수 추상 클래스
	public abstract void sound();
	public abstract void move();
}
  • 자식 클래스는 반드시 모든 메서드를 오버라이딩 해야 함!
  • 마치 규격 제시와 유사 ⇒ 인터페이스 같음 (USB 예시, USB 규격에 맞춰서 마우스, 키보드 등을 제작해야함)
  • 순수 추상 클래스를 더 편리하게 사용하는 자바 기능 : 인터페이스임!

 

1-3. 인터페이스

  • 순수 추상 클래스를 더 편리하게 사용하는 기능
  • 클래스 x ⇒ interface 키워드
// 순수 추상 클래스
public abstract class AbstractAnimal {
	public abstract void sound();
	public abstract void move();
}
// 순수 추상 클래스를 인터페이스로 변경
public interface InterfaceAnimal {
	public abstract void sound();
	public abstract void move();
}

 

1-3-1. 인터페이스 특징

  • 특징
    • 순수 추상 클래스와 동일 (+ 편의기능 살짝 추가)
    • 추가된 편의기능
      1. 인터페이스 내 모든 메서드는 public abstract 가 자동 추가됨 ⇒ 생략 권장
      2. 다중 구현 지원
    • 인터페이스는 “구현” 하는 것 (상속 x)
      인터페이스 : 물려받을 부모의 기능이 없고 구현해야 할 메서드만 존재
      상속 : 부모 기능 물려받아서 사용하는 것이 목적
  • 멤버 변수
    • 인터페이스가 제공하는 상수”라고 생각
    • 모든 멤버 변수는 public static final (생략 가능)
      • 인터페이스는 빈 껍데기만 제공해야 함 ⇒ 멤버 변수는 상수만 가능!
public interface InterfaceAnimal {
	
	// 멤버 변수 => 상수만 가능
	public static final int PI = 3.14;  // public static final 생략 가능
	int ONE_HUNDRED = 100;  // public static final 생략함
}

 

1-3-2. 인터페이스 구현

  • implements 키워드

// 인터페이스
public interface InterfaceAnimal {
	// public abstract : 생략 가능
	(public abstract) void sound();  
	void move();
}

// 인터페이스 구현 클래스
public class Dog implements InterfaceAnimal {
	@Override
	public void sound() {
		System.out.println("월월");
	}
	
	@Override
	public void move() {
		System.out.println("개 이동");
	}
}

  • 클래스 (상속)와 동일하게 new Dog() 로 인스턴스 생성 ⇒ 인스턴스에 InterfaceAnimal 또한 생성됨
📌 클래스, 추상 클래스, 인터페이스

     모두 메모리 구조 상 똑같음!!
     단지 제약이 추가될 뿐임
  • 자바 8, 자바 9 변경 내용
더보기

자바 8 ⇒ default 접근제어자의 메서드 사용하면 인터페이스에서도 메서드 구현 가능 (예외적 경우에만 사용할 것!)

자바 9 ⇒ private 접근제어자의 메서드도 가능

 

1-4. 인터페이스와 다중 구현

  • 인터페이스는 다중 구현이 가능함! (상속은 다중 상속 불가능)

 

  • 인터페이스 2개 모두 methodCommon() 이라는 메서드를 가짐
  • 상속 ⇒ 어떤 부모의 메서드 실행할지 결정해야 하는 다이아몬드 문제 발생
  • 인터페이스 ⇒ 동일한 이름의 메서드지만 구현은 자식에서 함
    + 어차피 오버라이딩 해서 Child의 methodCommon()이 실행될 것임!
  • 따라서 다이아몬드 문제 x ⇒ 다중 구현 허용

 

public interface InterfaceA {
	void methodA();
	void methodCommon();
}

public interface InterfaceB {
	void methodB();
	void methodCommmon();
}
public class Child implements InterfaceA, InterfaceB {

	@Override
	public void methodA() {
		System.out.println("Child.methodA");
	}
	
	@Override
	public void methodB() {
		System.out.println("Child.methodB");
	}
	
	@Override
	public void methodCommon() {
		System.out.println("Child.methodCommon");
	}
}

methodCommon() 은 양쪽 인터페이스에 모두 존재하지만 Child에서는 같은 메서드이므로 1개만 구현!

public static void main(String[] args) {
	InterfaceA a = new Child(); // 인터페이스도 다형적 참조 가능! ("부모는 자식을 담을 수 있다")
	a.methodA();
	a.methodCommon();
	
	InterfaceB b = new Child();
	b.methodB();
	b.methodCommon();
}

/* 실행결과
Child.methodA
Child.methodCommon
Child.methodB
Child.methodCommon
*/
  • methodCommon() 호출 시

 

1-5. 클래스와 인터페이스 활용

  • 동시에 클래스 상속 + 인터페이스 구현 가능! (이때도 인터페이스 다중 구현 가능)

// Bird 클래스 예시만
public class Bird extends AbstractAnimal implements Fly {
	
	@Override
	public void sound() {
		System.out.println("짹짹");
	}
	
	@Override
	public void fly() {
		System.out.println("새가 날아다님");
	}
}