기본 콘텐츠로 건너뛰기

방송대 Java프로그래밍 5강: 인터페이스·다형성과 클래스 확장 구조

Java프로그래밍 5강 - 인터페이스와 다형성

방송대 Java프로그래밍 5강: 인터페이스·다형성과 클래스 확장 구조

Java에서 변수의 자료형과 실제 객체의 클래스는 언제나 같아야 할까? 그렇지 않다. 상위 자료형으로 공통 기능을 약속하고, 그 변수에 서로 다른 하위 객체를 연결하면 같은 호출이 객체에 따라 다른 동작을 낸다. 이번 강의는 이 구조를 만드는 추상 클래스와 인터페이스, 실행 시점의 다형성, 값의 범위를 고정하는 열거 자료형, 일회성 구현인 익명 클래스, 바깥 클래스와 협력하는 중첩 클래스를 하나의 관계망으로 묶는다.

먼저 관계망을 그리면 다섯 개념이 한 흐름으로 연결된다

이번 강의의 중심 질문은 “무엇을 약속하고, 누가 실제 행동을 정하는가?”이다. 추상 클래스와 인터페이스는 하위 클래스가 따라야 할 계약을 만든다. 다형성은 그 계약 자료형의 변수에 여러 구현 객체를 연결한다. 열거 자료형은 허용할 값을 정해진 객체 집합으로 묶고, 익명 클래스는 계약의 일회성 구현을 즉석에서 만든다. 중첩 클래스는 바깥 클래스와 밀접한 보조 동작을 내부에 배치한다.

구조해결하려는 문제핵심 판단 질문
추상 클래스공통 상태·구현을 나누면서 일부 동작은 하위 클래스에 맡김관련 클래스가 공유할 필드나 구현이 있는가?
인터페이스서로 다른 클래스에 공통 동작 계약을 부여함상속 계층보다 “할 수 있는 일”을 약속하려는가?
다형성하나의 상위 자료형으로 여러 구현을 교체해 사용함변수의 선언 자료형과 실제 객체가 각각 무엇인가?
열거 자료형허용 값을 이름 있는 상수 객체로 제한함가능한 값의 집합이 미리 정해져 있는가?
익명·중첩 클래스일회성 구현 또는 바깥 클래스 전용 보조 기능을 가까이 둠재사용 이름이 필요한가, 바깥 객체에 종속되는가?

한 문장 지도: 계약을 정의하고 → 구현 객체를 연결하고 → 상위 자료형으로 호출하면 → 실제 객체가 재정의한 메서드가 실행된다.

추상 클래스는 공통 구현과 미완성 계약을 함께 담는다

추상 메서드는 머리부만 선언하고 몸체를 두지 않은 메서드다. abstract로 표시하며, 이를 물려받은 일반 하위 클래스는 반드시 구현해야 한다. 구현하지 않으려면 그 하위 클래스도 추상 클래스로 남겨야 한다. 추상 메서드는 하위 클래스가 재정의해야 하므로 private, static, final과 결합할 수 없다.

abstract class Shape {
    private final String name;

    Shape(String name) {
        this.name = name;
    }

    String getName() {              // 공통 구현
        return name;
    }

    abstract double getArea();      // 하위 클래스가 완성할 계약
}

class Rectangle extends Shape {
    private final double width;
    private final double height;

    Rectangle(double width, double height) {
        super("rectangle");
        this.width = width;
        this.height = height;
    }

    @Override
    double getArea() {
        return width * height;
    }
}

Shape에는 완성된 getName()과 미완성 getArea()가 함께 있다. 따라서 new Shape(...)로 추상 클래스 객체를 직접 만들 수는 없지만, Shape를 상위 자료형이나 부모 클래스로 사용할 수 있다. 관련 도형이 이름 관리 방식을 공유하면서 넓이 계산만 달리해야 할 때 알맞은 구조다.

오개념 바로잡기: 추상 클래스의 모든 메서드가 추상 메서드인 것은 아니다. 추상 메서드가 하나도 없는 추상 클래스도 가능하며, 핵심 제한은 해당 클래스를 직접 인스턴스화할 수 없다는 점이다.

인터페이스는 구현보다 가능한 동작을 먼저 약속한다

인터페이스는 클래스가 제공해야 할 동작을 선언하는 자료형이다. 일반 메서드 선언은 자동으로 public abstract이고, 필드는 자동으로 public static final인 상수다. 생성자가 없으므로 인터페이스 자체의 객체도 만들 수 없다. 클래스는 implements로 하나 이상의 인터페이스를 구현하고, 인터페이스는 extends로 여러 인터페이스를 상속할 수 있다.

interface Movable {
    void move(int dx, int dy);
}

interface Scalable {
    void scale(double ratio);
}

class Icon implements Movable, Scalable {
    private int x;
    private int y;
    private double size = 1.0;

    public void move(int dx, int dy) {
        x += dx;
        y += dy;
    }

    public void scale(double ratio) {
        size *= ratio;
    }
}

인터페이스의 메서드는 공개 계약이므로 구현 메서드도 public이어야 한다. 한 클래스가 MovableScalable을 동시에 구현하면 “이동 가능”과 “크기 조절 가능”이라는 두 능력을 함께 얻는다. 여기서 다중 구현은 여러 부모 클래스의 상태를 물려받는 다중 클래스 상속과 다르다.

일반 선언과 달리 default 메서드static 메서드에는 몸체를 둘 수 있다. 특히 default 메서드는 기존 구현 클래스의 코드를 모두 즉시 고치지 않고도 인터페이스에 기본 동작을 추가할 수 있게 한다.

interface Loggable {
    void log(String message);

    default void warn(String message) {
        log("WARN: " + message);
    }
}
비교 기준추상 클래스인터페이스
주된 목적가까운 클래스의 공통 기반구현 클래스의 동작 계약
인스턴스 필드가질 수 있음가질 수 없음; 선언 필드는 상수
생성자가질 수 있으나 직접 객체 생성은 불가가질 수 없음
구현 상속·구현 수클래스 하나만 extends클래스가 여러 개를 implements 가능
몸체 있는 메서드일반 메서드 가능default·static 메서드 가능

다형성은 선언 자료형과 실제 객체를 나누어 추적한다

다형성은 같은 이름의 동작이 대상이나 문맥에 따라 여러 형태로 나타나는 성질이다. 강의에서는 인자 목록이 다른 같은 이름의 메서드를 두는 오버로딩, 하위 클래스가 같은 시그니처의 메서드를 다시 정의하는 오버라이딩, 서로 다른 클래스가 같은 인터페이스 메서드를 각자 구현하는 경우를 함께 다룬다.

실행 결과를 판단할 때는 두 축을 분리한다. 컴파일러는 변수의 선언 자료형에 해당 메서드가 있는지 확인한다. 호출이 허용되면, 실행 시에는 실제 객체가 가진 오버라이딩 메서드를 선택한다.

interface Notifier {
    void send();
}

class EmailNotifier implements Notifier {
    public void send() {
        System.out.println("email");
    }
}

class SmsNotifier implements Notifier {
    public void send() {
        System.out.println("sms");
    }
}

Notifier notice = new EmailNotifier();
notice.send();                       // email

notice = new SmsNotifier();
notice.send();                       // sms
추적 단계첫 번째 호출두 번째 호출
선언 자료형NotifierNotifier
컴파일 시 확인Notifiersend()가 있으므로 호출 가능
실제 객체EmailNotifierSmsNotifier
실행 메서드·출력EmailNotifier.send() → emailSmsNotifier.send() → sms

한편 선언 자료형에 없는 구현 클래스 전용 메서드는 실제 객체에 존재하더라도 그 상위 자료형 변수를 통해 바로 호출할 수 없다. 즉, 선언 자료형은 “보이는 멤버의 경계”를 정하고 실제 객체는 “오버라이딩된 행동의 선택”을 정한다.

업캐스팅은 자동이지만 다운캐스팅은 실제 객체를 확인해야 한다

상속이나 구현 관계가 있으면 하위 자료형 객체를 상위 자료형 변수에 대입할 수 있다. 이를 업캐스팅이라고 하며 별도의 형 변환 표기 없이 허용된다. 여러 하위 객체를 하나의 상위 자료형으로 처리할 수 있어 코드의 유연성과 재사용성이 높아진다.

반대로 상위 자료형 참조를 하위 자료형으로 바꾸는 다운캐스팅에는 명시적 형 변환이 필요하다. 문법상 캐스트를 썼다는 사실만으로 안전해지지는 않는다. 참조가 실제로 그 하위 클래스 객체를 가리켜야 한다.

Notifier notice = new EmailNotifier(); // 업캐스팅: 자동
EmailNotifier email = (EmailNotifier) notice; // 안전

Notifier other = new SmsNotifier();
EmailNotifier wrong = (EmailNotifier) other;  // 실행 시 형 변환 오류

다운캐스팅 점검 순서: ① 두 자료형 사이에 상속·구현 관계가 있는지 본다. ② 현재 참조가 가리키는 실제 객체를 찾는다. ③ 그 객체가 목표 하위 자료형인지 확인한 뒤 캐스트한다.

예를 들어 부모 자료형 변수에 Sales 객체와 Development 객체를 번갈아 대입하고 doJob()을 호출하면, 각 객체가 재정의한 영업 업무와 개발 업무가 실행된다. 배열이나 메서드 매개변수를 부모 자료형으로 선언해도 같은 원리가 적용된다.

열거 자료형은 값 목록을 상수 객체의 집합으로 만든다

열거 자료형(enum)은 사용할 수 있는 값을 미리 정한 이름 있는 상수 집합이다. Day.MONDAY처럼 잘못된 임의 값의 유입을 막고 의미를 분명하게 표현한다. values()는 선언된 열거 상수들을 순서대로 돌려준다.

열거 상수는 단순 정수가 아니라 해당 enum의 객체처럼 동작한다. 따라서 필드와 메서드를 가질 수 있고, 각 상수 선언에서 생성자 인자를 전달할 수 있다. 상수 목록 뒤에 필드나 메서드가 이어지면 목록 끝에 세미콜론을 쓴다. enum 생성자는 같은 이름을 가지며 외부에서 호출할 수 없도록 private이다.

enum TeamRecord {
    BLUE(18, 12), GOLD(14, 16);

    private final int win;
    private final int lose;

    private TeamRecord(int win, int lose) {
        this.win = win;
        this.lose = lose;
    }

    double winRate() {
        return win * 100.0 / (win + lose);
    }
}

System.out.println(TeamRecord.BLUE.winRate()); // 60.0

위 학습 예제에서 BLUE는 18승 12패이므로 전체 경기 수는 30이고, 승률은 18 × 100.0 ÷ 30 = 60.0이다. 100.0을 사용해 실수 나눗셈이 이루어지게 한 점도 함께 확인해야 한다. 열거 상수가 초기화될 때 생성자가 자동 호출되어 각 상수의 승·패 필드가 정해진다.

익명 클래스는 한 번 쓸 구현을 객체 생성 자리에 완성한다

익명 클래스는 이름을 따로 선언하지 않고 객체를 만들면서 부모 클래스를 상속하거나 인터페이스를 구현하는 클래스다. 같은 익명 클래스를 이름으로 다시 생성할 수 없으므로 한 지점에서만 필요한 짧은 구현에 적합하다.

Notifier oneTime = new Notifier() {
    @Override
    public void send() {
        System.out.println("one-time notice");
    }
};

oneTime.send();

new Notifier() { ... }는 인터페이스 객체를 직접 만든 것이 아니다. 중괄호 안에서 Notifier를 구현하는 이름 없는 클래스를 정의하고 그 객체를 만든 것이다. 부모 클래스를 대상으로 할 때도 new SuperClass() { ... } 형태로 메서드를 재정의할 수 있다.

접근 경계: 익명 클래스 몸체에 새 필드나 메서드를 추가할 수는 있지만, 변수가 부모 클래스나 인터페이스 자료형이면 그 자료형에 선언되지 않은 새 멤버를 변수로 직접 호출할 수 없다. 다형성에서 배운 “선언 자료형이 보이는 멤버를 정한다”는 규칙이 그대로 적용된다.

중첩 클래스는 바깥 객체와의 결합 정도로 구분한다

클래스 안에 선언한 클래스를 중첩 클래스라고 한다. 비정적 내부 클래스의 객체는 바깥 클래스 객체에 연결되어 바깥 객체의 멤버에 접근할 수 있다. 반면 static 중첩 클래스는 바깥 객체를 만들지 않고도 사용할 수 있으며, 바깥 클래스의 static 멤버에 접근한다.

구분바깥 객체 필요바깥 멤버 접근적합한 역할
비정적 내부 클래스필요인스턴스·static 멤버특정 바깥 객체의 상태를 다루는 보조 객체
static 중첩 클래스불필요static 멤버바깥 객체 상태와 독립적인 보조 자료형

이름이 같은 변수가 겹칠 때는 가까운 범위가 우선한다. 매개변수는 이름만 쓰고, 내부 클래스 필드는 this, 바깥 클래스 필드는 Outer.this로 구별한다.

class Outer {
    int x = 100;

    class Inner {
        int x = 20;

        void printX(int x) {
            System.out.println(x);            // 매개변수: 3
            System.out.println(this.x);       // Inner 필드: 20
            System.out.println(Outer.this.x); // Outer 필드: 100
        }
    }
}

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.printX(3);

강의의 반복자 예제처럼, 내부 클래스는 바깥 클래스의 배열을 순회하는 전용 Iterator 구현에도 어울린다. 인덱스를 0에서 시작해 2씩 증가시키면 크기 15인 배열에서 0, 2, 4, 6, 8, 10, 12, 14 위치를 차례로 방문한다. 반복 규칙과 내부 상태를 바깥 자료구조 가까이에 숨길 수 있다는 장점이 있다.

코드를 읽을 때는 계약·참조·객체·범위를 차례로 확인한다

  1. 계약 찾기: 추상 메서드나 인터페이스 메서드가 무엇을 요구하는지 표시한다.
  2. 구현 확인: 일반 하위 클래스가 요구된 메서드를 모두 구현했는지, 접근 범위가 충분한지 본다.
  3. 참조와 객체 분리: 대입문의 왼쪽 선언 자료형과 오른쪽 실제 객체 클래스를 따로 적는다.
  4. 호출 판정: 선언 자료형으로 호출 가능 여부를 판단한 뒤 실제 객체의 오버라이딩 메서드를 선택한다.
  5. 범위 추적: enum의 허용 상수, 익명 클래스의 일회성 멤버, 내부 클래스의 this가 가리키는 범위를 확인한다.

시험용 압축 공식: “호출 가능?”은 선언 자료형, “어느 구현?”은 실제 객체, “어느 변수?”는 가장 가까운 범위와 this의 주인을 본다.

핵심 개념 정리

  • 추상 클래스는 공통 필드·구현과 추상 메서드를 함께 둘 수 있지만 직접 객체를 만들 수 없다.
  • 인터페이스는 동작 계약을 제공하며, 클래스는 여러 인터페이스를 구현할 수 있다.
  • 상위 자료형 변수는 하위 객체를 참조할 수 있고, 오버라이딩 메서드는 실제 객체에 따라 동적으로 선택된다.
  • 업캐스팅은 자동이지만 다운캐스팅은 명시적이며 실제 객체가 목표 자료형과 맞아야 한다.
  • enum은 제한된 상수 객체 집합이고 필드·생성자·메서드를 가질 수 있다.
  • 익명 클래스는 일회성 구현, 비정적 내부 클래스는 특정 바깥 객체와 결합된 보조 동작에 적합하다.

이번 강의의 코드를 만나면 계약 자료형을 찾고 → 구현 관계를 확인하고 → 선언 자료형으로 호출 가능성을 판정한 뒤 → 실제 객체로 실행 메서드를 선택하고 → 형 변환과 이름 범위의 경계를 점검한다. 이 순서를 지키면 추상화, 다형성, 익명·중첩 클래스가 따로 떨어진 문법이 아니라 객체를 유연하게 조직하는 하나의 설계 흐름으로 보인다.

이해도 확인 퀴즈

1. 추상 클래스와 추상 메서드에 대한 설명으로 옳은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②

  • ① 오답: 추상 클래스에도 몸체가 있는 일반 메서드를 둘 수 있다.
  • ② 정답: 공통 구현과 하위 클래스가 완성할 추상 계약을 한 클래스에 담을 수 있다.
  • ③ 오답: final은 재정의를 막으므로 구현을 요구하는 추상 메서드와 모순된다.
  • ④ 오답: 추상 클래스는 직접 인스턴스화할 수 없지만 부모 클래스로 사용할 수 있다.

2. 인터페이스에 대한 설명으로 옳은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④

  • ① 오답: 인터페이스에는 생성자가 없고 자체 객체를 만들 수 없다.
  • ② 오답: Java 클래스는 쉼표로 구분해 여러 인터페이스를 구현할 수 있다.
  • ③ 오답: 인터페이스에서 선언한 필드는 public static final인 상수다.
  • ④ 정답: 서로 다른 능력 계약을 한 클래스에 함께 부여하는 문법이다.

3. Notifier n = new SmsNotifier(); n.send();에서 두 클래스가 본문처럼 정의되었다면 출력은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③

  • ① 오답: 실제 객체가 구현한 send()가 정상적으로 호출된다.
  • ② 오답: email은 실제 객체가 EmailNotifier일 때의 출력이다.
  • ③ 정답: 선언 자료형은 Notifier지만 실제 객체가 SmsNotifier이므로 그 구현이 실행된다.
  • ④ 오답: Notifiersend()가 선언되어 있어 호출은 컴파일 단계에서 허용된다.

4. 다음 중 실행 시 형 변환 오류가 발생하는 문장은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①

  • ① 정답: 참조가 가리키는 실제 객체는 SmsNotifier이므로 EmailNotifier로 바꿀 수 없다.
  • ② 오답: 구현 클래스 객체를 인터페이스 자료형에 대입하는 안전한 업캐스팅이다.
  • ③ 오답: 하위 객체의 참조를 상위 인터페이스 자료형에 대입하는 자동 변환이다.
  • ④ 오답: 실제 객체가 목표 하위 자료형인 EmailNotifier이므로 다운캐스팅이 유효하다.

5. 본문의 TeamRecord.BLUE(18, 12)에 대해 win * 100.0 / (win + lose)의 결과는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②

  • ① 오답: 패배 12를 승리 18로 나눈 값에 가까워 전체 경기 대비 승리 비율이 아니다.
  • ② 정답: 전체 30경기 중 18승이므로 18 × 100.0 ÷ 30 = 60.0이다.
  • ③ 오답: 승리만 분모로 두거나 임의 반올림한 결과로, 식의 분모 30을 반영하지 않았다.
  • ④ 오답: 전체 경기 수를 승리 수로 나누는 등 분자와 분모를 뒤바꾼 값이다.

6. 관련 클래스들이 이름 필드와 출력 메서드는 공유하되, 넓이 계산만 각자 구현해야 한다. 가장 알맞은 중심 구조는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ③

  • ① 오답: 문자열 상수는 공통 상태와 하위 클래스별 계산 동작을 조직하지 못한다.
  • ② 오답: 재사용할 계층에 일회성 익명 구현을 반복하면 공통 기반을 만들기 어렵다.
  • ③ 정답: 공유 상태·구현과 하위별 필수 구현을 동시에 표현하는 추상 클래스의 용도에 맞다.
  • ④ 오답: 복사는 중복을 늘리고 공통 규칙을 한곳에서 관리하지 못하게 한다.

7. 본문의 inner.printX(3)이 출력하는 순서로 옳은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ①

  • ① 정답: x는 매개변수 3, this.x는 내부 필드 20, Outer.this.x는 바깥 필드 100이다.
  • ② 오답: 바깥 필드부터 읽은 순서이며 실제 출력문의 순서와 반대다.
  • ③ 오답: 첫 표현 x는 내부 필드가 아니라 가장 가까운 매개변수다.
  • ④ 오답: 두 번째의 this는 바깥 객체가 아니라 Inner 객체를 가리킨다.

8. 비정적 내부 클래스와 static 중첩 클래스의 차이를 바르게 설명한 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④

  • ① 오답: 비정적 내부 클래스 객체는 연결될 바깥 객체가 필요하다.
  • ② 오답: 특정 바깥 객체에 연결되는 쪽은 비정적 내부 클래스다.
  • ③ 오답: 비정적 내부 클래스는 연결된 바깥 객체의 인스턴스 멤버에도 접근할 수 있다.
  • ④ 정답: 두 구조를 바깥 객체에 대한 종속 여부로 정확히 구분했다.

9. Notifier oneTime = new Notifier() { ... };에 대한 설명으로 옳지 않은 것은?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ②

  • ① 오답: 익명 클래스 표현의 실제 의미를 올바르게 설명했다.
  • ② 정답: 인터페이스 자체는 객체화할 수 없으며, 이 문법은 익명 구현 클래스의 객체를 만든다.
  • ③ 오답: 인터페이스 계약의 메서드를 익명 클래스 몸체에서 완성한다.
  • ④ 오답: 재사용 이름이 필요 없는 일회성 구현이 대표적인 사용 상황이다.

10. 여러 알림 구현을 하나의 메서드 매개변수로 받고, 전달된 객체에 맞는 send()를 실행하려 한다. 가장 적절한 설계는?

정답입니다.

오답입니다. 답안을 다시 선택해 보세요.

정답 및 해설 보기

정답: ④

  • ① 오답: 상위 자료형에 없는 구현 전용 메서드는 그 매개변수로 바로 보이지 않는다.
  • ② 오답: 공통 계약을 포기하므로 구현이 늘 때 호출 코드를 함께 늘려야 한다.
  • ③ 오답: 제한된 값 집합인 enum만으로 서로 다른 알림 동작 계약을 대신할 필요가 없다.
  • ④ 정답: 인터페이스 자료형으로 공통 호출을 보장하고 실제 객체별 오버라이딩을 동적으로 선택한다.

참고 자료와 작성 기준

이 글은 해당 차시 강의자료를 바탕으로 학습 목적에 맞게 재구성한 비공식 학습자료입니다. 계약 비교표, 선언 자료형·실제 객체 추적표, 형 변환 점검 절차, enum 승률 계산과 중첩 범위 예제는 개념 사이의 관계를 스스로 판정할 수 있도록 구성하고 검토했습니다.

  • 작성·편집: 올에이클래스 학습연구팀
  • 주요 근거: 한국방송통신대학교 Java프로그래밍 5강 「인터페이스와 다형성」 강의자료(교재 4장)
  • 보충 자료: 별도의 외부 자료를 본문 근거로 사용하지 않음
  • 편집 원칙: 올에이클래스 편집 정책
  • 최종 내용 검토: 2026-09-01

댓글