[JAVA] 인터페이스

dev_swanim·2023년 4월 6일

JAVA 문법

목록 보기
4/10
post-thumbnail

1. 인터페이스 역할

  • 두 객체를 연결
  • 사용 이유 : 객체 b가 객체 c로 교체되었을 때 객체 a가 객체 b를 직접 사용한다면, 객체 a의 소스 코드를 객체 b에서 c로 변경해주는 추가 작업이 필요하다. 이런 불필요한 작업을 하지 않도록 도와주는 게 인터페이스이다
  • 다형성 구현에서 주로 사용
    - 상속보다 인터페이스를 이용해 다형성을 많이 구현


2. 인터페이스 사용

인터페이스 선언

interface 인터페이스명 {...} //default 접근 제한
public interface 인터페이스명 {...} //public 접근 제한
public interface RemoteCtrl{
	//public 추상 메소드
	public void turnOn();
}

구현 클래스 선언

객체 B같은 구현 객체는 인터페이스를 구현하고 있다고 선언부에 명시해야 한다

public class B implements 인터페이스명 {...}
public class Television implements RemoteCtrl{
	@override
	public void turnOn(){
		System.out.println("TV ON");
	}
}

public class Audio implements RemoteCtrl{
	@override
	public void turnOn(){
		System.out.println("Audio ON");
	}
}
public class RemoteCtrlEx{
	public static void main(String[] args){
		RemoteCtrl rc;
		
		//rc 변수에 Television 객체를 대입
		rc = new Television();
		rc.turnOn();

		//rc 변수에 Audio 객체를 대입(교체)
		rc = new Audio();
		rc.turnOn();
	}
}

>> TV ON
>> Audio ON

3. 상수 필드

  • 인터페이스에 선언된 필드는 모두 public static final 특성을 갖고 있다
    • public static final을 생략해도 자동으로 컴파일 과정에서 붙게 된다
  • 상수는 구현 객체와 관련 없는 인터페이스 소속이므로 바로 인터페이스로 접근해도 된다
public interface RemoteCtrl{
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0;
}

public class RemoteCtrlEx{
	public static void main(String[] args){
		System.out.println(RemoteCtrl.MAX_VOLUME);
	}
}

4. 추상 메소드

  • 추상 메소드 : 실행부만 갖는 메소드(리턴 타입, 메소드명, 매개변수만 갖는다)
  • public abstract를 생략해도 컴파일 시, 자동으로 붙는다
public interface RemoteCtrl{
	//상수 필드
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0;

	//추상 메소드
	void turnOn();
	void turnOff();
	void setVolume(int volume);
}

⚠️ 구현 클래스에서 추상 메소드를 재정의 할 때, 인터페이스의 추상 메소드는 기본적으로 public 접근제한을 갖기 때문에 더 낮은 제한으로 재정의 불가능하다. 그래서 재정의한 메소드에 public이 추가되어 있다. (public void turnOn())

public class Television implements RemoteCtrl{
	//필드
	private int volume;

	//turnOn() 추상 메소드 오버라이딩
	@override
	public void turnOn(){
		System.out.println("TV ON");
	}
}

5. 디폴트 메소드(default method)

  • 사용 이유?
    • 인터페이스 구현객체에서 메소드를 모두 구현해야 하므로, 인터페이스에 메소드를 추가할 때 문제가 생긴다. 메소드 하나 추가를 위해 해당 인터페이스를 구현하는 모든 클래스에서 해당 메소드를 모두 구현해야 한다. 디폴트 메소드를 사용하면, 인터페이스의 구현을 상속하기에 인터페이스에다 새 메소드를 추가할 수 있다.
  • 클래스 메소드 선언법과 동일한데 default가 리턴타입 앞에 붙는다
  • 디폴트 메소드에서는 상수필드를 읽거나 추상 메소드를 호출하는 코드를 작성할 수 있다.
[public] default 리턴타입 메소드명(매개변수, ...){}

default void setMute(boolean mute){
	if(mute){
		//추상 메소드 호출하면서 상수 필드 사용
		setVolume(MAX_VOLUME);
	}
	else{
		System.out.println("MUTE");
	}
}

구현 클래스에서 디폴트 메소드를 재정의해서 쓸 수도 있다. 재정의 할 때, public 접근 제한자를 반드시 붙여야하고 default 키워드를 생략해야한다.

private int memoryVolume; //추가 필드 선언

@Override
public void setMute(boolean mute){
	if(mute){
		//추상 메소드 호출하면서 상수 필드 사용
		setVolume(MAX_VOLUME);
	}
	else{
		System.out.println("MUTE");
	}
}

6. 정적 메소드

  • 구현 객체가 없어도 인터페이스만으로도 호출 가능
  • public을 생략해도 자동으로 컴파일 과정에서 붙는다
[public | private] static 리턴타입 메소드명(매개변수, ...) {...}
static void changeBattery(){
	System.out.println("Change Battery");
}

//main
RemoteControl.changeBattery();

7. private 메소드

  • 인터페이스 외부에서 접근할 수 없는 private 메소드
  • 사용 용도 : 디폴트와 정적 메소드의 중복 코드를 줄이기 위함
구분설명호출 범위
private 메소드구현 객체가 필요한 메소드디폴트 메소드 안에서만 호출
private 정적 메소드구현 객체가 필요 없는 메소드디폴트 메소드 + 정적 메소드 안에서 호출
private interface Service{
	//default 메소드
	default void defaultMethod(){
		defaultCommon();
	}
	// private 메소드
	private void defaultCommon(){
		System.out.println("defaultMethod 중복 A");
	}

	//정적 메소드
	static void staticMethod(){
		staticCommon();
	}
	//private 정적 메소드
	private static void staticCommon(){
		System.out.println("staticMethod 중복C");
	}
}

8. 다중 인터페이스 구현

  • 구현 객체는 여러 개의 인터페이스를 implements 할 수 있다
public class 구현클래스명 implements 인터페이스A, 인터페이스B{
	//모든 추상 메소드 재정의
}
public interface RemoteControl{
	//추상 메소드
	void turnOn();
	void turnOff();
}

public interface Searchable{
	//추상 메소드
	void search(String url);
}

public class SmartTelevision implements RemoteControl, Searchable{
	//turnOn() 추상 메소드 오버라이딩
	@Override
	public void turnOn(){}
	
	//turnOff() 추상 메소드 오버라이딩
	public void turnOff(){}

	//search() 추상 메소드 오버라이딩
	@Override
	public void search(String url){}
}

//main
RemoteControl rc = new SmartTelevision();

rc.turnOn();
rc.turnOff();

Searchable searchable = new SmartTelevision();

searchabl.search("https://youtube.com");

→ 오버라이딩 한 내용으로 결과값 출력됨


9. 인터페이스 상속

  • 인터페이스도 다른 인터페이스를 상속할 수 있다. 클래스와는 다르게 다중 상속을 허용한다
  • 자식 인터페이스의 구현 클래스는 자식 인터페이스의 메소드, 부모 인터페이스의 모든 추상 메소드를 재정의 해야한다
  • 구현 객체가 자식 인터페이스 변수에 대입 → 자식 + 부모 인터페이스의 추상 메소드 모두 호출 가능
  • 부모 인터페이스 변수에 대입 → 부모 인터페이스에 선언된 추상 메소드만 호출 가능
public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2 {...}
public interface InterfaceA{
	void methodA();
}

public interface InterfaceB{
	void methodB();
}

public interface InterfaceC extends InterfaceA, InterfaceB{
	void methodC();
}

public class InterfaceCImpl implements InterfaceC{
	public void methodA(){}

	public void methodB(){}

	public void methodC(){}
}
//main
InterfaceCImpl impl = new InterfaceCImpl();

InterfaceA ia = impl;
ia.methodA(); //A만 호출 가능

InterfaceB ia = impl;
ia.methodB(); //B만 호출 가능

InterfaceC ia = impl;
ia.methodA();
ia.methodB();
ia.methodC();

10. 타입 변환

자동 타입 변환

  • 직간접적으로 A인터페이스를 구현하고 있으면 A를 구현하고 있는 구현 클래스B, 구현 클래스 B의 자식클래스C 모두 인터페이스 A로 자동 타입 변환이 가능하다
B b = new B();
C c = new C();

A a;
a = b;
a = c

강제 타입 변환

public interface Vehicle{
	void run();
}

public class Bus implements Vehicle{
	@Override
	public void run(){}

	//추가 메소드
	public void checkFare(){}
}
//main

//인터페이스 변수 선언과 구현 객체 대입
Vehicle vehicle = new Bus();

//인터페이스를 통해서 호출
vehicle.run();
//vehicle.checFare(); 사용불가

//강제 타입 변환 후 호출
Bus bus = (Bus) vehicle;
bus.run();
bus.checkFare();

11. 다형성

필드의 다형성

아래처럼 필드 타입으로 타이어 인터페이스를 선언하면, 자동 타입 변환에 의해 필드값으로 한국타이어 또는 금호타이어 객체를 대입할 수 있다.

public class Car{
	Tire tire1 = new HankookTire();
	Tire tire2 = new KumhoTire();
}

Car 객체를 생성한 후 다른 구현 객체를 대입할 수 있다. (타이어 교체)

Car myCar = new Car();
myCar.tire1 = new KumhoTire();

매개변수의 다형성

  • 매개변수 타입을 인터페이스로 선언하면 메소드 호출할 때 다양한 구현 객체를 대입할 수 있다
void drive(Vehicle vehicle){
	vehicle.run();
}

public class Driver{
	void drive(Vehicle vehicle){
		vehicle.run();
	}
}

12.객체 타입 확인

  • 객체 타입을 확인하기 위해 instanceof 연산자 사용
if(vehicle instanceof Bus bus){ //vehicle에 대입된 객체가 Bus일 경우 실행
	//bus 변수 사용
}

13. 봉인된 인터페이스

  • 무분별한 자식 인터페이스 생성을 막기 위해 봉인된 인터페이스 사용

sealed를 사용하면 permits 뒤에 상속 가능한 자식 인터페이스를 지정해야 한다.

public sealed interface InterfaceA permits InterfaceB {...}
//봉인 해제. 다른 자식 인터페이스 생성 가능
public non-sealed interface InterfaceB extends InterfaceA {...} 

// 봉인. 또 다른 봉인 인터페이스 선언
public sealed interface InterfaceB extends InterfaceA {...}

📚참고 문헌

이것이 자바다(신용권, 임경균 지음)

profile
데이터와 백엔드를 공부하고 있습니다😌

0개의 댓글