티스토리 뷰

Java

인터페이스와 추상클래스

kunypony 2022. 8. 9. 00:50
728x90

추상클래스

1️⃣ 정의

  • 보기
    • 추상 클래스는 여러 클래스들의 공통적인 특징을 포함하는 미완성 설계도이다.
    • A클래스, B클래스, C클래스가 있다고 가정하면 여기서 각 클래스 안에는 각자의 필드와 메서드가 존재한다. 추상 클래스는 A클래스, B클래스, C클래스들 간에 비슷한 필드와 메서드를 공통적으로 추출해 만들어진 클래스다.
    • 일반 클래스의 공통적인 부분을 추출해 어느 정도 규격(틀)을 잡아 놓는 추상적인 클래스이다.
    • 추상 메소드는 추상 클래스 내부에서 선언이 가능하며 자식 클래스에서 반드시 오버라이딩 해야만 사용할 수 있는 메소드를 의미한다.
    • 추상 클래스는 인터페이스의 역할도 하면서 클래스의 기능도 가지고 있는 자바의 돌연변이 같은 클래스이다.
    • 오버라이딩 =>  💡 오버라이딩(overriding)이란 상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식 클래스에서 같은 시그니쳐를 갖는 메소드로 다시 정의하는 것
class Parent {

    void display() { System.out.println("부모 클래스의 display() 메소드입니다."); }

}

class Child extends Parent {

    void display() { System.out.println("자식 클래스의 display() 메소드입니다."); }

}
public class Inheritance05 {

    public static void main(String[] args) {

        Parent pa = new Parent();

        pa.display();

        Child ch = new Child();

        ch.display();

        Parent pc = new Child();

        pc.display(); // Child cp = new Parent();

    }

}

2️⃣ 특징

  • 보기
    • 메소드와 클래스에 abstract 예약어를 사용한다.
    • 일부 메소드가 구현되지 않고, 선언만 되어있는 클래스
      • 자식 클래스에서 이것을 반드시 구현하게 끔 강제하는 것
      • 필요한 모든 클래스가 구현될 수 있도록 하여 안정성을 높이는 효과를 가져온다.
    • 구현 해야하는 메소드들은 상위 클래스에서 선언을 해놓고,구현의 책임을 하위클래스에 위임한다.
    • 일반 클래스는 실제 객체를 생성할 정도의 구체성을 가지는 반면, 추상 클래스는 아직 메서드와 내용이 추상적이기 때문에 객체를 생성할 수 없다. ⇒ new(인스턴스화) 할 수 없다.
    • 추상 메소드가 없어도 abstract 키워드를 사용하면 추상 클래스가 된다
    • 생성자를 가질 수 있고, 일반 메소드도 가질 수 있다.
    • 하나의 추상 메소드라도 포함을 하면 추상 클래스가 되야 한다.
      • 모든 메소드가 구현되있다고 해도 클래스에 abstract 키워드를 사용하면 추상 클래스가 된다.

3️⃣ 목적

  • 보기
    • 공통된 필드와 메서드를 통일할 목적
    • 실체 클래스 구현 시, 시간 절약
    • 규격에 맞는 실체 클래스 구현
    • 소스 수정 시 다른 소스의 영향도를 적게 가져가면서 변화에는 유연하게 만들기 위해 추상 클래스를 사용

4️⃣ 구현 예시

abstract class AbstractFoo{
    int x, y;

    public void method(){
        System.out.println("method");
    }
                                       
    public abstract void abstractMethod();
    // 선언만 하고 구현하지 않음. Virtual method call이여서 부모메소드는 비어있음.
}

class Foo extends AbstractFoo{

    @Override
    public void abstractMethod() {
        System.out.println("implemented abstractMethod");
    }
}

public class AbstractClass {
    public static void main(String[] args) {
       // AbstractFoo afoo = new AbstractFoo()
       //추상 클래스는 객체 생성이 불가합니다. 구현이 안되있으니 인스턴스화가 안된다.
        Foo foo = new Foo();
        foo.abstractMethod();

        AbstractFoo afoo = (AbstractFoo)foo;
        afoo.abstractMethod(); // virtual method call
        //추상클래스는 객체 생성은 불가하지만, 구현된 자식클래스의 객체는 받을 수 있다.
    }
}

 

인터페이스

1️⃣ 정의

  • 보기
    • 인터페이스는 객체를 어떻게 구성해야 하는지 정리한 기본 설계도
    • 인터페이스는 다형성의 개념을 위해 사용
      • 공통의 기능을 제공 후
      • 구현을 강제 하여, 일반화 관계를 쉽게 표현
    • 인터페이스 변수에 인터페이스가 구현된 서로 다른 구현 객체를 할당해서 사용이 가능합니다.
    • 객체가 인터페이스를 사용하면, 인터페이스 메서드를 반드시 구현해야 하는 제약을 가진다.
    • 극단적으로 동일한 목적 하에 동일한 기능을 수행하게 끔 강제하는 것
    • 인터페이스(interface)란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미
    • 구현 객체를 직접 몰라도 인터페이스 메서드만 알아도 객체 호출이 가능

2️⃣ 특징

  • 보기
    • 인터페이스는 100% 다형성 보장 ⇒ 모든 메서드가 선언만 되었고 구현은 되어있지 않다.
    • 인터페이스는 interface 키워드를 통해 선언할 수 있으며 implements 키워드를 통해 일반 클래스에서 인터페이스를 구현할 수 있다.
    • 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속은 지원하지 않지만 인터페이스라는 것을 통해 다중 상속이 가능
    • 인터페이스 멤버 변수는 public static final 로만 지정 가능하며 생략 가능하다다 .
    • 또한 멤버 함수는 public abstract final 로만 선언 가능하다다.
    • 디폴트 생성자, 인자 있는 생성자 모두 생성이 불가
    • 자식클래스는 부모 인터페이스의 함수를 모두 오버라이딩 해야 한다.

3️⃣ 목적

  • 보기
    • 자바의 다형성을 극대화하여 개발 코드 수정을 줄이고 프로그램 유지 보수 성을 높이기 위해 사용된다.
    • 다중 상속이 가능하다.
    • 서로 다른 개발자들 간의 협업에 도움이 된다.
    인터페이스는 100% 추상화이기 때문에 인터페이스를 통해서 공통적인 기능을 명시하고 강제적으로 구현하게 할 수 있다. 이를 통해 협업하는 과정에서, 상대방에게 기본적인 틀을 제공할 수 있다.

협업 예시 보기1. 개발자들이 서로 협업 하여 프로젝트를 만들 동안 서로 만들어낼 기능들은 유기적으로 상관 관계를 만들어낼 것이다.

2. 이럴 경우, A개발자는 B클래스의 더미(dummy) 클래스라는 가짜 혹은 임시 클래스 를 만들어서 B클래스가 필요한 자리에 채워 넣은 뒤, B개발자가 B클래스를 완성한 후에 바꿔뀜으로서 개발 일정에 차질이 생기지 않을 수 있을 것이다.

3. B클래스에는 여러 기능을 하는 메서드들이 존재할 텐데 이 메서드들의 형식(틀)을 담당하는 것이 인터페이스이다.

 

4. 인터페이스를 사용하지 않는 다면? 

5. 인터페이스로 메서드 1과 메서드 2의 형식을 잡아놓지 않는다면,  개발자들 간의 B클래스를 담당하는 B개발자가 메서드 1을 A개발자가 가지고 있는 B더미 클래스 안의메서드 1의 형식이 맞지 않아서 많은 양을 리팩터링 해야 하는 경우가 발생 할 수 있다.

 

4️⃣ 구현 예시

인터페이스(Interface) 구성요소는 다음과 같다.

상수 필드(Constant Field)

💡 인터페이스는 객체가 될 수 없기 때문에, 런타임에 필드 데이터를 저장할 수 없습니다. 그래서 인스턴스 필드/ 정적 필드는 선언이 불가능합니다. 상수 필드는 Compile Time에 선언되고 Run Time에 변경되지 않으므로 인터페이스에 선언이 가능합니다. [public static final]는 명시적으로 사용하지 않아도, Compile Time에 자동으로 선언되어 상수로 만듭니다. 네이밍은 모두 대문자로 구성되고 구분자는 "_"(언더바)로 표현됩니다. (Java Convention)

추상 메서드

 💡 인터페이스 변수로 호출된 메서드는 최종적으로 구현 객체에서 실행됩니다. 그래서 실체는 인터페이스에 없고, 구현 클래스에 있습니다. 추상 메서드는 리턴 타입 / 메서드 이름 / 매개변수 가 기술되는 클래스 설계 메서드입니다. [public abstract]은 명시적으로 선언하지 않아도, Compile Time에 자동으로 선언됩니다.

디폴트 메서드

💡 클래스의 인스턴스 메서드와 동일합니다. 즉, 인스턴스 메서드입니다. 다만 인터페이스에서 선언할 때, 리턴 타입 앞에 default 키워드가 붙습니다. [public]은 명시적으로 사용하지 않아도, Compile Time에 자동 선언됩니다. 디폴트 메서드는 나중에 인터페이스를 구현한 구현 클래스에 인스턴스 메서드로 추가됩니다. 재정의(Override)를 통해서 구현 클래스에서 재정의된 인스턴스 메서드로 사용할 수 있습니다.

정적 메서드

💡 Java 8에서 추가된 인터페이스의 멤버입니다. 선언 형식은 클래스 정적 메서드와 완전 동일합니다. [public]은 명시적으로 사용하지 않아도, Compile Time에 자동으로 선언됩니다. 인터페이스의 정적 메서드도 클래스의 정적 메서드와 똑같은 방식으로 사용이 가능합니다.

인터페이스 예제

💡 인터페이스를 상속받은 각 타입의 클래스가 자신만의 메소드를 호출

class Main {
	public static void main(String[] args) {
		진찰하다(new 사람());
		// 출력 : 사람이 진찰합니다.
		진찰하다(new 원숭이());
		// 출력 : 원숭이가 진찰합니다.
		진찰하다(new 치타());
		// 출력 : 치타가 진찰합니다.
		진찰하다(new 기린());
		// 출력 : 기린이 진찰합니다.
		진찰하다(new 로봇());
		// 출력 : 로봇이 진찰합니다.
		진찰하다(new 삼성());
		// 출력 : 삼성이 진찰합니다.
		진찰하다(new LG());
		// 출력 : 삼성이 진찰합니다.
	}
	
	// 여기에 static 추가
	public static void 진찰하다(의사 a의사) {
		a의사.진찰();
	}
}

interface 의사 {
	public void 진찰();
}

class 사람 implements 의사 {
	public void 진찰() {
		진료();
	}
	
	void 진료() {
		System.out.println("사람이 진찰합니다.");
	}
}

class 원숭이 implements 의사 {
	public void 진찰() {
		살펴보다();
	}
	
	void 살펴보다() {
		System.out.println("원숭이가 진찰합니다.");
	}
}

class 치타 implements 의사 {
	public void 진찰() {
		System.out.println("치타가 진찰합니다.");
	}
}

class 기린 implements 의사 {
	public void 진찰() {
		System.out.println("기린이 진찰합니다.");
	}
}

class 로봇 implements 의사 {
	public void 진찰() {
		오류감지();
	}
	
	void 오류감지() {
		System.out.println("로봇이 진찰합니다.");
	}
}

class 삼성 implements 의사 {
	public void 진찰() {
		삼성병원에_진찰하라고_지시();
	}
	
	void 삼성병원에_진찰하라고_지시() {
		System.out.println("삼성이 진찰합니다.");
	}
}

class LG implements 의사 {
	public void 진찰() {
		삼성병원에_진찰하라고_지시();
	}
	
	void 삼성병원에_진찰하라고_지시() {
		System.out.println("LG가 진찰합니다.");
	}
}

다중 인터페이스를 상속한 인터페이스

interface Walkable{
    void walk();
}

interface Runnable{
    void run();
}

interface Jumpable extends Walkable, Runnable{
    void jump();// walk(), run()도 포함되어 있을 것
}

class Jumper implements Jumpable{

    @Override
    public void walk() {
        System.out.println("walk");
    }

    @Override
    public void run() {
        System.out.println("run");
    }

    @Override
    public void jump() {
        System.out.println("jump");
    }
}

추상 클래스와 인터페이스

 💡 자바에서 추상 클래스는 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있다. 하지만 인터페이스(interface)는 오로지 추상 메소드와 상수만을 포함할 수 있다.

  • 추상 클래스 ➡️ IS - A
    • 추상 클래스는 멤버 변수가 있기 때문에 자녀 클래스들에 공통 속성을 설정할 수 있다. 또한 일반메서드와 추상메서드로 공통의 기능도 구현할 수 있다. 그래서 추상 클래스는 어떤 개념에 상위 개념으로 세분화 되는 하위 클래스들을 대표하는 클래스로 사용할 수 있다
      • 대표적인 예로 Animal 이란 큰 대 분류 안에 많은 종류의 Animal 을 상속하는 동물 클래스들을 작성할 수 있으며 그들의 공통 속성인 name, lifespan, color 것을 공통적으로 상속해 줄 수 있고 또한 공통 기능인 walk(),eat()등의 기능들도 상속해 줄 수 있다.
      • 추상 메서드 IS - A
  • 인터페이스 ➡️ HAS - A
    • 반면에 인터페이스는 추상메서드 밖에 없지만 다중 상속이 가능하기 때문에 세분화된 기능을 나타내는데 활용될 수 있다.
      • 예를 들면 Animal의 하위클래스들 중에 세분화 기능이라고 볼 수 있는 flyable, ridable 등을 표시해줄 수 있으며 동물별로 여러개의 세분화 된 기능들을 추가하는데 사용할 수 있다
      • 인터페이스 HAS - A
  • 정리해보면 공통적인 것을 모아 추상 클래스를 만들고 차이점을 모아 인터페이스를 만든다.

추상 클래스 ➡️ 수직적 구조 => 추상 클래스는 트리 형식으로 상위 클래스와 하위 클래스로 나뉘어지는 트리 구조이다.

하위 클래스는 상위 클래스에서 추상화시킨 생성자 혹은 메서드를 정의하면서 더 구체화되어간다.

인터페이스 ➡️ 수평적 구조 

종류 구상 클래스 추상 클래스 인터페이스

추상성 0% 0%~100% 100%
추상 메서드 개수 0개 0개 ~ n개 모든 메서드
구상성 100% 0%~100% 0%
구상 메서드 개수 모든 메서드 0개 ~ n개 0개
다른 클래스에 상속 extends extends implements
객체화 가능 여부 가능 불가능 불가능

추상 클래스는 구상을 해도 되고 안해도 상관없지만 인터페이스는 오로지 100% 추상

💡 인터페이스와 추상 클래스는 애초에 존재 목적이 다르다. 추상 클래스는 상속받아서 기능을 이용하고 확장하는 데 있지만 인터페이스는 함수의 껍데기만 있는데 그 이유는 그 함수의 구현을 강제하기 위해서 이다.

🧐 **‘추상 클래스와 인터페이스의 차이 및 존재 이유를 설명하라’**는 질문은 비단 외형적인 차이뿐만이 아니라 상속이라는 개념과 다형성이라는 개념에 중점을 두어야 한다.

 

추상 클래스는 여러 클래스의 공통적인 기능을 추출해 만든 추상적인 클래스로 미완성 설계도 라면 인터페이스는 객체를 어떻게 구성해야 하는지 정리된 완성된 기본 설계도이다.

둘 다 비슷하게 extends, implements 를 사용하여 서브 클래스를 정의하지만 추상 클래스의 상속은 슈퍼클래스 즉 부모의 기능을 이용하거나 확장하기 위해서 사용되는 반면 인터페이스는 해당 인터페이스를 구현한 객체들에 대해서 동일한 동작을 약속하기 위해 존재한다.

추상 클래스 ⇒ 상속받는 것 ⇒ 공통점들 인터페이스 ⇒ 차이점들

인터페이스를 구현하는 하위 클래스는 인터페이스에 명시된 메서드를 구현해야하는 코드의 명확성과 설계 방향을 잡아줍니다. 또한 인터페이스의 도입은 구체적인 클래스에 의존하지 않고 객체지향의 원리인 다형성을 추구할 수 있다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함