발걸음/일지

[JAVA] 객체지향 프로그래밍 심화(feat. 추상화)

리꾸므 2022. 11. 8. 11:18

추상화

  • 공통적인 속성과 기능을 추출하여 정의하는 것을 의미
  • 기존 클래스들의 공통적인 요소들을 뽑아서 상위 클래스를 만들어 내는 것
  • 즉 공통 적인 속성과 기능을 정의하고 하위클래스를 생성할 수 있고, 반대로 하위 클래스 공통성을 모아 상의 클래스를 정의 할 수 있다.
  • 자바에서는 주로 추상클래스와 인터페이스라는 문법요소를 사용하여 추상화를 구현

 

abstract 제어자

  • 메서드 앞에 붙은 경우 추상 메서드(abstract method), 클래스 앞에 붙은 경우 추상 클래스(abstract class)라 부른다.
  • 어떤 클래스에 추상 메서드가 포함되어 있는 경우 해당 클래스는 자동으로 추상 클래스가 된다.
  • '추상적이다'의미는 다른 말로 표현하면 '구체적이지않다'라는 의미이기도하며 이는 추상 메서드는 충분히 구체화하지 못한 '미완성 메서드'라 할 수 있으며 미완성 메서드를 포함하는 클래스는 '미왼성 클래스'를 의미하는 추상 클래스가 된다.
  • 추상 클래스는 미완성 클래스이기때문에 메서드 바디가 완성되기전까지 이를 기반으로 객체 생성이 불가하다.

 

 

추상 클래스

  • 추상 메서드를 하나 이상 포함한다는 점 외에는 기본적으로 일반 클래스와 동일하다고 할 수 있다.
  • 메서드 시그니처만 존재하고 바디가 선언되어있지 않은 추상 메서드를 포함하는 ‘미완성 설계도’
  • 객체를 생성하는 것이 불가능 하다.
  • 상속 관계에 있어 새로운 클래스를 작성하는데 매우 유용하다.
    • 메서드의 내용은 상속받는 클래스를 따라 달라지기때문에 상위 클래스에서는 선언분만 작성하고, 실제 내용은 상속 받는 하위 클래스에서 구현하도록 비워둔다면 설계하는 상황이 변화더라도 유연하게 대처 가능하다.
  • 오버라이딩을 통해 추상클래스로부터 상속받은 추상 메서드의 내용을 구현하여 메서드 완성 가능, 이를 기반으로 객체 생성이 가능하다
  • 상속계층도의 상층부에 위치할 수록 추상화의 정도가 높고 그 아래로 내려갈수록 구체화된다.
  • 상층부에 가까울수록 더 공통적인 속성과 기능들이 정의되어 있다
abstract class Animal {
	public String kind;
	public abstract void sound();
}

class Dog extends Animal { // Animal 클래스로부터 상속
	public Dog() {
		this.kind = "포유류";
	}

	public void sound() { // 메서드 오버라이딩 -> 구현부 완성
		System.out.println("멍멍");
	}
}

class Cat extends Animal { // Animal 클래스로부터 상속
	public Cat() {
		this.kind = "포유류";
	}

	public void sound() { // 메서드 오버라이딩 -> 구현부 완성
		System.out.println("야옹");
	}
}

class DogExample {       
    public static void main(String[] args) throws Exception {
       Animal dog = new Dog();
       dog.sound();

       Cat cat = new Cat();
       cat.sound();
    }
 }

// 출력값
멍멍
야옹

 

 

final 키워드

  • 더이상 변경이 불가하거나 확장되지 않는 성질
  • 필드, 지역 변수, 클래스 앞에 위치할 수 있다. 위치할 시 공통적으로 변경 불가능하고 확장할 수 없다.
    • 클래스 : 변경 또는 확장 불가능한 클래스, 상속 불가
    • 메서드 : 오버라이딩 불가
    • 변수 : 값 변경이 불가한 상수
final class FinalEx { // 확장/상속 불가능한 클래스
	final int x = 1; // 변경되지 않는 상수

	final int getNum() { // 오버라이딩 불가한 메서드
		final int localVar = x; // 상수
		return x;
	}
}

 

 

인터페이스

  • 추상클래스를 '미완성 설계도'에 비유하자면, 인터페이스는 더 높은 추상성을 가진 기초적인 '밑그림'에 비유할 수 있다.
  • 기본적으로 추상메서드와 상수만을 멤버로 가질 수 있어 추상화 정도가 높다.
  • 기본적으로 추상 메서드의 집합으로 이뤄져 있다.

 

 구조

  • class 키워드 대신 interface 키워드 사용
  • 내부의 모든 필드가 public static final로 정의되고, static, default 메서드 이외 모든 메서드가 public abstract로 정의 된다.
  • 모든 인터페이스에 위 요소가 내포되어있기때문에 생략가능하다.
  • 상수 정의는 'public static final'로 메서드 정의는 'public abstract'로 정의되어야하나 일부분 또는 전부 생략가능하다.
public interface InterfaceEx {
    public static final int rock =  1; // 인터페이스 인스턴스 변수 정의
    final int scissors = 2; // public static 생략
    static int paper = 3; // public & final 생략

    public abstract String getPlayingNum();
		void call() //public abstract 생략 
}

 

 

 구현

  • 인터페이스도 그 자체로 인스턴스를 생성할 수 없고 메서드 바디를 정의하는 클래스를 따로 작성해야한다.
  • implements 키워드를 사용하여 구현한다.
  • 특정 인터페이스를 구현한 클래스는 해당 인터페이스에 정의된 모든 추상메서드를 구현해야한다.
  • 어떤 클래스가 특정 인터페이스를 구현한다는 것은 클래스에게 인터페이스의 모든 추상 메서드를 반드시 구현하도록 강제하는 것을 의미한다.
  • 다른 말로는 모든 추상 메서드들을 해당 클래스내에서 오버라이딩하여 바디를 완성한다라고 할 수 있다.
class 클래스명 implements 인터페이스명 {
		... // 인터페이스에 정의된 모든 추상메서드 구현
}

 

 

 다중 구현

  •  상속은 다중 상속이 허용되지 않으나 인터페이스는 다중적 구현이 가능하다.
  • 하나의 클래스가 여러 개의 인터페이스 구현 가능, 다만 인터페이스는 인터페이스로부터만 상속 가능
  • 클래스와 달리 Object 클래스와 같은 최고조상이 없다.
  • 특정 클래스는 다른 클래스로부터의 상속을 받으면서 동시에 인터페이스를 구현 할 수 있다.
class ExampleClass implements ExampleInterface1, ExampleInterface2, ExampleInterface3 { 
}
interface Animal { // 인터페이스 선언. public abstract 생략 가능.
	public abstract void cry();
} 

interface Pet {
	void play();
}

class Dog implements Animal, Pet { // Animal과 Pet 인터페이스 다중 구현
    public void cry(){ // 메서드 오버라이딩
        System.out.println("멍멍!"); 
    }

    public void play(){ // 메서드 오버라이딩
        System.out.println("원반 던지기");
    }
}

class Cat implements Animal, Pet { // Animal과 Pet 인터페이스 다중 구현
    public void cry(){
        System.out.println("야옹~!");
    }

    public void play(){
        System.out.println("쥐 잡기");
    }
}

public class MultiInheritance {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();

        dog.cry();
        dog.play();
        cat.cry();
        cat.play();
    }
}

// 출력값
멍멍!
원반 던지기
야옹~!
쥐 잡기

 

  왜 다중 상속이 가능할까?

  •  클래스에 다중 상속이 불가능한 이유는 부모 클래스에 동일한 이름의 필드 또는 메서드 존재시 충돌하기 때문이다.
  • 인터페이스는 애초 미완성 멤버를 가지고 있어 충돌이 발생할 여지가 없고, 따라서 다중 구현이 가능하다.

 

 

 장점

  • 역할과 구현을 분리시켜 사용자 입장에서 복잡한 구현의 내용 또는 변경과 상관없이 해당 기능 사용 가능하다.
  • 코드 변경의 번거로움을 최소화하고 손쉽게 해당 기능을 사용할 수 있게 한다.
  • 개발자의 입장에서도 선언과 구현을 분리시켜 개발시간을 단축할 수 있고, 독립적인 프로그래밍을 통해 한 클래스의 변경이 다른 클래스에 미치는 영향을 최소화 할 수있다.