JAVA 7주차

ndy·2022년 11월 9일

Java

목록 보기
7/10

8장 인터페이스


인터페이스란?

  • 개발 코드와 객체가 서로 통신하는 접점
    • 개발 코드는 객체의 내부 구조를 알 필요가 없음
    • 인터페이스의 메소드만 알고 있으면 됨

8.1 인터페이스의 역할

  • 개발 코드 변경 없이 리턴값 또는 실행 내용이 다양해 질 수 있음 (다형성)

8.2 인터페이스 선언

  • 인터페이스 이름
    • 첫자는 대문자로 시작
    • 숫자로 시작x, $_를 제외한 특수 문자 사용x
    • 다른 단어와 결합되면 첫 알파벳은 대문자로 하는 것이 관례

  • 인터페이스 선언
    public interface 인터페이스명 {...}

  • 인터페이스 구성 멤버
    • 상수
    • 추상 메소드
    • 디폴트 메소드
    • 정적 메소드
interface 인터페이스명 {
	//상수
	타입 상수명 =;
    //추상 메소드
    타입 메소드명(매개변수, ...);
    //디폴트 메소드
    default 타입 메소드명(매개변수, ...){...}
    //정적 메소드
    static 타입 메소드명(매개변수, ...){...}
}

8.3 인터페이스 구현

상수 필드 선언

  • 인터페이스는 상수 필드만 선언 가능
  • 인터페이스에 선언된 필드는 모드 public static final의 특징을 가짐
  • 상수명은 대문자로 작성
  • 선언과 동시에 초기값을 지정해야 함
public interface RemoteControl{
	public int MAX_VOLUME = 10; // 컴파일시 public static final로 자동 변환
    public int MIN_VOLUME = 0; // 컴파일시 public static final로 자동 변환
}

추상 메소드 선언

  • 인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행됨
  • 인터페이스의 메소드는 기본적으로 실행 블록이 없는 추상 메소드로 선언 함
public interface RemoteControl{
	public void turnOn(); 
    public void turnOff();
    public void setVolume(int volume);
    // 컴파일시 모든 메소드에 자동적으로 public abstract가 붙음
}

디폴트 메소드 선언

  • 자바8에서 추가된 인터페이스의 새로운 맴버
[public] default 리턴타입 메소드명(매개변수, ...){...}
  • 실행 블록을 가지고 있는 메소드
  • default 키워드를 반드시 붙여야 함
  • 기본적으로 public 접근 제한을 가지므로 생략하더라도 컴파일 과정에서 붙음
public interface RemoteControl{
	default void setMute(boolean mute){
    	if(mute){
        	System.out.println("무음 처리합니다.");
        }else{
        	System.out.println("무음 해제합니다.");
        }
    }
}

정적 메소드 선언

  • 자바8에서 추가된 인터페이스의 새로운 맴버
[public] static 리턴타입 메소드명(매개변수, ...) {...}
public interface RemoteControl{
	static void changeBattery(){
    	System.out.println("건전지를 교환합니다.");
    }
}

오픈 소스코드를 만들었는데 그 오픈소스가 엄청 유명해져서 전 세계 사람들이 다 사용하고 있는데, 인터페이스에 새로운 메소드를 만들어야 하는 상황이 발생했다.
자칫 잘못하면 내가 만든 오픈소스를 사용한 사람들은 전부 오류가 발생하고 수정을 해야 하는 일이 발생할 수도 있다.
이런 경우를 위해 만들어짐


구현 객체구현 클래스

  • 인터페이스의 추상 메소드에 대한 실체 메소드를 가진 객체 = 구현 객체
  • 구현 객체를 생성하는 클래스 = 구현 클래스

구현 클래스 선언

public class 구현클래스명 implements 인터페이스명 {
	//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
  • 추상 메소드실체 메소드를 작성하는 방법
    • 메소드의 선언부가 정확히 일치해야 함
    • 인터페이스의 모든 추상 메소드를 재정의하는 실체 메소드를 작성해야 함
      • 일부만 재정의할 경우 추상 클래스(abstract)로 선언해야 함
// 추상 클래스
public abstract class Television implements RemoteControl{
	public void turnOn() {...}
    public void turnOff() {...}
    // setVolume() 메소드를 오버라이딩 하지 않음 (추상 클래스로 선언)
}
  • 인터페이스의 모든 메소드는 public 접근 제한을 갖기 때문에 public 보다 더 낮은 접근 제한으로 작성 불가
  • @Override 어노테이션을 이용해서 정확히 재정의되었는지 컴파일러가 체크하도록 함

인터페이스 변수와 구현 객체

  • 인터페이스를 사용하지 않은 경우
Television tv = new Television();
  • 인터페이스를 사용한 경우
인터페이스 변수 = 구현객체;
RemoteControl rc = new Television();
// RemoteControl 인터페이스 변수 rc에 
// RemoteControl 인터페이스를 구현해놓은 Television 클래스를 대입

익명 구현 객체

명시적인 구현 클래스 작성을 생략하고 바로 구현 객체를 얻는 방법

인터페이스 변수 = new 인터페이스() {
	// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
  • 인터페이스의 추상 메소드들을 모두 재정의하는 실체 메소드가 있어야 함
  • 추가적으로 필드와 메소드를 선언할 수 있지만 익명 객체 안에서만 사용할 수 있고, 인터페이스 변수로 접근할 수 없음

주로 사용되는 곳

  • UI 프로그래밍(Swing, Android)에서 이벤트를 처리하기 위해 사용
  • 임시 작업 스레드를 만들기 위해 사용
  • 자바8부터 지원하는 람다식은 내부적으로 익명 구현 객체를 사용

익명 구현 객체도 클래스(바이트코드)파일을 가지고 있음

  • 클래스$번호.class 파일명으로 생성됨

다중 인터페이스 구현 클래스

public class 구현 클래스명 implements 인터페이스1, 인터페이스2{
	// 인터페이스1에 선언된 추상 메소드의 실체 메소드 선언
    // 인터페이스2에 선언된 추상 메소드의 실체 메소드 선언
}

8.4 인터페이스 사용

인터페이스에 구현 객체를 대입하는 방법

public class MyClass{
	//필드
    RemoteControl rc = new Television(); // 인터페이스 변수에 구현 객체 대입
    
    //생성자
    // 생성자에 매개변수로 인터페이스 변수가 옴 (구현 객체 대입 가능)
    MyClass(RemoteControl rc){ 
    	this.rc = rc;
    }
    //메소드
    void methodA(){
    	//로컬 변수
        RemoteControl rc = new Audio(); // 인터페이스 변수에 구현 객체 대입
    }
    
    void methodB(RemoteControl rc) {...} 
    // 메소드에 매개변수로 인터페이스 변수가 옴 (구현 객체 대입 가능)
}

추상 메소드 사용

RemoteControl rc = new Television();
rc.turnOn(); 	// Television 클래스의 실체 메소드 turnOn() 실행
rc.turnOff(); 	// Television 클래스의 실체 메소드 turnOff() 실행

디폴트 메소드 사용

  • 인터페이스로 바로 호출 불가능
    • 구현 객체가 인터페이스 대입되어야 호출 가능

  • 모든 구현 객체가 가지고 있는 기본 메소드로 사용함
    • 필요에 따라 구현 클래스가 디폴트 메소드를 재정의 가능
public interface RemoteControl {
	default void setMute(boolean mute){...} // 디폴트 메소드
}
RemoteControl.setMute(true); // 불가능

RemoteControl rc = new Television(); // 구현 객체가 인터페이스 변수에 대입됨
rc.setMute(true); // 가능

정적 메소드 사용

  • 인터페이스로 바로 호출 가능
public interface RemoteControl {
	static void setMute(boolean mute){...} // 정적 메소드
}
RemoteControl.setMute(true); // 가능

8.5 타입변환과 다형성

다형성

  • 하나의 타입에 여러가지 객체를 대입해서 다양한 실행 결과를 얻는 것
  • 다형성을 구현하는 기술
    • 상속 or 인터페이스의 자동 타입 변환
    • Overriding
  • 다형성의 효과
    • 다양한 실행 결과를 얻음
    • 객체를 부품화시킬 수 있어 유지보수 용이

인터페이스를 이용한 다형성 개념

interface A {
	void method1();
    void method2();
}
A a = new ClassEx1();
A a = new ClassEx2();

a.method1();
a.method2();
// 수정 필요 없음

자동 타입 변환(Promotion)

인터페이스 변수 = 구현객체;
// 클래스가 인터페이스로 자동 타입 변환됨


필드의 다형성

// Tire 인터페이스 생성
public interface Tire {
	public void roll();
}
// Tire 인터페이스를 구현한 HankookTire 구현 클래스
public class HankookTire implements Tire {
	@Override
	public void roll(){
    	System.out.println("한국 타이어가 굴러갑니다.")
    }
}
public class Car {
	Tire frontLeftTire = new HankookTire(); 
    Tire frontRightTire = new HankookTire();
    Tire backLeftTire = new HankookTire();
    Tire backRightTire = new HankookTire();
    // 인터페이스 변수에 구현 클래스를 대입
    // (클래스->인터페이스 자동 타입 변환)
    
    void run(){
		frontLeftTire.roll();
        frontRightTire.roll();
        backLeftTire.roll();
        backRightTire.roll();
        // System.out.println("한국 타이어가 굴러갑니다.")가 실행 됨
    }
    
}
Car myCar = new Car();
myCar.frontLeftTire = new KumhoTire(); 
myCar.frontRightTire = new KumhoTire();
// 인터페이스 변수에 구현 클래스를 대입

// run() 메소드 실행시 
// System.out.println("금호 타이어가 굴러갑니다.")가 실행 됨

인터페이스 배열로 구현 객체 관리

Tire[] tires = {
	new HankookTire(),
    new HankookTire(),
    new HankookTire(),
    new HankookTire()
};
tires[1] = new KumhoTire();

매개변수의 다형성

// Vehicle 인터페이스 생성
public interface Vehicle {
	public void run();
}
public class Driver {
	// Vehicle 인터페이스를 매개변수로 받는 drive() 메소드
	public void drive(Vehicle vehicle){
    	vehicle.run();
    }
}
Driver driver = new Driver(); // Driver 클래스 객체 생성

Bus bus = new Bus(); // Vehicle 인터페이스를 구현한 bus 구현 객체 생성

driver.drive(bus); // = Vehicle vehicle = bus;
// bus 구현 객체의 run() 메소드 실행

강제 타입 변환(Casting)

  • 인터페이스 타입으로 자동 타입 변환 후, 다시 구현 클래스 타입으로 변환
    • 구현 클래스 타입에 선언된 다른 맴버를 사용하기 위함
구현 클래스 변수 = (구현 클래스) 인터페이스 변수;
interface Vehicle {
	void run();
}
class Bus implements Vehicle {
	void run() {...} // Vehicle 인터페이스의 추상 메소드 구현
    void checkFare() {...} // 메소드 선언
}
Vehicle vehicle = new Bus(); // 구현 객체를 인터페이스 변수에 대입

vehicle.run(); 			// 가능
vehicle.checkFare(); 	// 불가능

Bus bus = (Bus)vehicle; // Bus 클래스로 강제 타입 변환

vehicle.run(); 			// 가능
vehicle.checkFare(); 	// 가능

객체 타입 확인(instanceof 연산자)

  • 강제 타입 변환전에 구현 클래스 타입을 조사
public void method(Vehicle vehicle){
	if(vehicle instanceof Bus){ // vehicle가 Bus객체를 참조하고 있는지 확인
    	Bus bus = (Bus)vehicle;
    }
}

8.6 인터페이스 상속

  • 인터페이스간에도 상속이 가능
public interface 하위 인터페이스 extends 상위 인터페이스1, 상위 인터페이스2 {...}
하위 인터페이스 변수 = new 구현 클래스();
상위 인터페이스1 변수 = new 구현 클래스();
상위 인터페이스2 변수 = new 구현 클래스();
  • 해당 타입의 인터페이스에 선언된 메소드만 호출 가능

8.7 티폴트 메소드와 인터페이스 확장

디폴트 메소드의 어색한 동거

  • 인터페이스 개념에 맞지않는 디폴트 메소드

디폴트 메소드의 필요성

  • 기존 인터페이스에 추상 메소드를 추가할 수 없음
    • 추상 메소드를 추가하면 기존 클래스들이 오류 발생

  • 디폴트 메소드는 추상 메소드가 아니다
    • 디폴트 메소드를 추가하더라도 기존 구현 클래스들은 오류 발생x

디폴트 메소드가 있는 인터페이스 상속

  • 부모 인터페이스의 디폴트 메소드를 자식 인터페이스에서 활용하는 방법
    1. 디폴트 메소드를 단순히 상속 받음
    2. 디폴트 메소드를 재정의(Override)
    3. 디폴트 메소드를 추상 메소드로 선언
public interface ParentInterface {
	public void method1();
    public default void method2(){
    	System.out.println("default method");
    }
}
public interface ChildInterface3 extends ParentInterface {
    public void method2();
    // default를 없애고 추상 메소드로 선언
}

0개의 댓글