자바_06 인터페이스와 추상클래스

aggapang·2025년 5월 2일

kb IT's Your Life

목록 보기
14/21

인터페이스

  • 두 객체를 연결하는 역할
  • 다형성 구현에 중요함
  • 다형성 : 인터페이스 하나의 이름으로 여러 형태의 객체를 사용할 수 있음

추상 메서드

  • 메서드 내용을 구현하지 않고 선언만 한 메서드
    • {} 중괄호 없는 리턴타입, 메소드명, 매개변수만 선언
  • public abstract 생략해도 컴파일 과정에서 자동으로 붙음
  • 추상 메서드를 하나라도 가진 클래스, 인터페이스는 객체 생성 불가능
  • 인터페이스를 implements한 클래스에서 추상 메소드 오버라이딩(재정의) 해야함!(필수)

인터페이스 선언

새로운 interface 파일 생성
public interface 인터페이스명{ // 필드, 메소드 등 }

  • 접근 제한자 : default(같은 패키지 내), public
  • new를 이용한 객체 생성 불가
    • 불완전한 메서드를 포함하고 있기 때문
    • 변수 타입으로는 쓸 수 있음
      • 인터페이스 변수명 = null;

구현 클래스 선언

public class 클래스명 implements 인터페이스명{}

  • 인터페이스에 정의된 추상 메소드에 대한 실행 내용
  • implements 키워드를 이용해서 해당 클래스가 인터페이스 사용할 수 있다고 선언
  • 인터페이스는 추상 메서드가 있어서 해당 클래스에서 재정의(@Override) 필수!!

변수 선언

인터페이스명 변수명 = new 클래스명();

  • 인터페이스는 참조 타입으로 구현 객체(클래스)를 대입해야함
    • 클래스 객체를 대입하면 자동형 변환이 됨

상수 필드

(public static final) 타입 상수명 = 값;

  • 인터페이스에 선언된 필드는 public static final 특성을 갖음 (불변의 상수 필드)
    • public static final는 생략 가능
    • 구현된 클래스에서 불변 변수로 공유하기 좋음
  • 이름 규칙 : 대문자, 다른 단어일때 _로 연결
  • ex) int MAX_COUNT = 100;

다중 인터페이스

public class 구현클래스명 implements 인터페이스1, 인터페이스2 { // 모든 추상 메소드 재정의 }
-> 인터페이스1 변수 = new 구현클래스명(); (O)
-> 인터페이스2 변수 = new 구현클래스명(); (O)

  • 구현 객체(클래스)는 여러개의 인터페이스를 구현할 수 있음

인터페이스 상속

public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2{...}
-> 자식인터페이스 변수 = new 구현클래스명(); (O)
-> 부모인터페이스1 변수 = new 구현클래스명(); (O)
-> 부모인터페이스2 변수 = new 구현클래스명(); (O)

  • 인터페이스도 다른 인터페이스 상속 가능
  • 다중 상속도 가능

타입변환

자동 타입 변환

  • 자동으로 타입 변환
  • 부모 클래스가 인터페이스를 구현하고 있다면 자식 클래스도 자동 타입 변환 가능

강제 타입 변환

구현클래스 변수 = (구현클래스) 인터페이스변수;

  • 인터페이스 이름으로 변수를 선언해 놓으면 인터페이스로 구현한 클래스, 객체를 모두 변수에 넣어서 사용 가능
  • 인터페이스명 변수명 = new 클래스명(); (자동 타입 변환)일때는 인터페이스에 선언된 메소드만 사용 가능
  • 구현클래스 변수 = (구현클래스) 인터페이스변수; (강제 타입 변환)일때는 클래스(객체)에 선언된 메소드도 사용 가능!

다형성

메소드 재정의 + 자동 타입 변환 => 다형성

매개변수의 다형성

  • 매개변수 타입을 인터페이스로 선언 가능
  • 메소드 호출때 다양한 구현 객체 대입 가능

instanceof 연산자

if(object instanceof InterfaceType){}

  • object(객체)가 InterfaceType(인터페이스)을 구현하고 있는가?
  • 객체가 특정 클래스나 인터페이스의 인스턴스인지 확인할 때 사용하는 연산자
  • Java12 부터 instanceof 연산 결과가 true이면 우측 타입 변수 사용할 수 있어서 강제 타입 변환 필요 없음
Vehicle vehicle = new Bus();  // 인터페이스 타입으로 선언했지만 실제 객체는 Bus

if (vehicle instanceof Bus bus) {
  //bus 변수 사용 가능
  bus.announce();  // 형변환 없이 Bus 메서드 사용 가능
}

추상 클래스

public abstract class 클래스명

  • 불완전한 클래스, 객체 생성 불가
  • 클래스 선언때,abstract 키워드 붙여줘야함
  • 단독으로 객체 생성 불가, 상속을 통해 자식 클래스만 만들 수 있음(실체 클래스의 부모역할)

+) 자동 타입 변환을 자주 사용하는 이유?

  1. 코드를 유연하게 만들기 위해 (다형성 활용)
Vehicle vehicle = new Bus();  // 자동 타입 변환
Vehicle vehicle = new Taxi(); // 이렇게도 가능
  • vehicle.run();처럼 동일한 코드로 다양한 객체(Bus, Taxi 등)를 처리할 수 있음
  • 코드를 유연하고 재사용성 높게 설계 가능!
    -> 실제 객체는 다양하지만, 같은 인터페이스 타입으로 제어할 수 있음!

  1. 인터페이스 기반 프로그래밍은 설계가 깔끔해짐
void startRide(Vehicle v) {
    v.run();  // Bus든, Taxi든 동작 가능
}
  • Vehicle 인터페이스만 알고 있어도 다양한 구현체(Bus, Taxi 등)를 받을 수 있음.
  • 확장성과 유지보수성이 엄청 좋아짐
    -> 개발자들은 이런 "약한 결합(loose coupling)" 구조를 선호함

  1. 구현체에 의존하지 않도록 하기 위해
  • ArrayList, LinkedList 등도 List 인터페이스를 구현하고 있음
    List list = new ArrayList();처럼 쓰는 거야.
void processList(List<String> list) {
    // 내부가 ArrayList든 LinkedList든 상관없음
}
  • 이 방식은 구현 클래스 변경이 자유로워서 유지보수가 편함!!
  • 하지만, 구현 클래스의 메서드는 못 쓰는 이유??

    "인터페이스나 부모 클래스에 선언된 기능만 사용할 거면, 그 타입만 알면 된다"라고 생각하기 때문

    • 이는 코드가 덜 복잡하고, 확장성이 좋짐
      -> 즉 "공통된 기능만 쓰는 게 더 좋은 설계 방식"이로 보기 때문
  • 강제 타입 변환은 언제 사용??

    • 진짜 필요한 경우만!
    • 예를 들어 Bus만의 고유 기능(checkFare())을 꼭 써야 하는 상황일 때만 캐스팅해서 사용
      if (vehicle instanceof Bus) {
          Bus bus = (Bus) vehicle;
          bus.checkFare();  // Bus만의 기능
      }

0개의 댓글