JAVA/Java 기초

인터페이스(Interface).java

john_ 2023. 1. 16. 16:37
728x90

2023.01.16 - [JAVA/Java 기초] - 추상클래스.java

 

 

인터페이스(interface) =  여러 장치를 연결하는 접속기

 

여러 객체를 연결하는 역할을 합니다.

 

  • 추상클래스처럼 틀을 구현 하지만 클래스가 아닙니다.
  • 클래스가 아니므로 부모클래스가 될 수 없습니다.

추상클래스  : 상속을 받은 자식 클래스는 부모클래스를 하나만 받습니다.

 

인터페이스 : 클래스가 아니므로 구현하는 클래스가 부모클래스가 되고,

여러 인터페이스를 상속 받을수 있습니다.

 


인터페이스 선언

  • 인터페이스 선언은 class 키워드 대신 interface 키워드를 사용합니다.
  • 접근 제한자로는 클래스와 마찬가지로 같은 패키지 내에서만 사용 가능한 default, 패키지와 상관없이 사용하는 public 을 붙일수 있습니다.
interface 인터페이스명{...}			//default 접근 제한
public interface 인터페이스명{...}	//public 접근 제한
public interface 인터페이스명{
//public 상수 필드
//public 추상 메소드
//public 디폴트 메소드
//public 정적 메소드
//private 메소드
//private 정적 메소드
}

 


구현클래스 선언

  • 인터페이스에 정의된 추상 메소드에 대한 실행내용이 구현 됩니다.

  • implements 키워드는 해당 클래스가 인너페이스를 통해 사용할수 있다는 표시이며, 인터페이스의 추상 메소드를 재정의한 메소드가 있다는 뜻입니다.

 

public class B implements 인터페이스명{...}

 


변수 선언과 구현 객체 대입

  • 인터페이스는 참조 타입에 속하므로 인터페이스 변수에는 객체를 참조하고 있지 않다는 뜻으로 null을 대입할수 있습니다.
RemoteControl rc;
RemoteControl rc = null;
  • 인터페이스를 통해 구현 객체를 사용 하려면, 인터페이스 변수에 구현 객체의 번지를 대입해야 합니다.

 


예시입니다.

package java230116;
//인터페이스 RemoteControl. 최상단에 위치합니다.
public interface RemoteControl {
	// public 추상 메서드
	public void turnOn1();
}
rc = new Television();
package java230116;
//자식클래스 (구현클래스) Television
public class Television implements RemoteControl{

	@Override
	public void turnOn1() {
		System.out.println("TV를 켭니다.");
	}
}
package java230116;
// 자식클래스 (구현클래스) Audio
public class Audio implements RemoteControl{

	public void turnOn1() {
		System.out.println("Audio를 켭니다.");
	}
}
package java230116;

public class RemoteControlExam {

	public static void main(String[] args) {

		RemoteControl rc;
		//RemoteControl를 참조하는 객체 rc 를 선언합니다.
		//// 통합리모컨
		rc = new Television();
		//Television 클래스를 속성으로 설정합니다.
		//// TV 버튼 - 리모컨에 Television 클래스 대입
		rc.turnOn1();
		//Television의 turnOn()을 실행합니다. (오버라이딩)
		
		rc = new Audio();
		// Audio 클래스를 객체 rc의 속성으로 설정,
		// 기존의 rc의 주소를 덮어씌웁니다.
		//// Audio 버튼 - 리모컨에 Audio 클래스 대입
		rc.turnOn1();
		// Audio클래스의 turnOn을 실행합니다.
		
		rc = new Television();
		//기존의 Television을 참조하던 주소가  audio로 덮어씌워졌기때문에
		//new Television으로 새로운 메모리를 할당받아야 사용 가능합니다.
		
		Television tv = new Television();	//TV 리모컨
		tv.turnOn1();
		Audio au = new Audio();				// audio 리모컨
		au.turnOn1();
	}
}

 


상수필드

  • 인터페이스에는 public static fianl 특성을 갖는 불변의 상수필드를 멤버로 가질수도 있습니다.
[public static final]/** 생략가능 **/ 타입 상수명 = 값;
  • 인터페이스에 선언된 필드는 모두 public static final 속성으로,  public static final을 생략해도 자동으로 컴파일 과정에 추가됩니다.
  • 상수명은 대문자, 서로 다른단어일때 언더바(_)로 연결합니다.
public interface RemoteControl {
	//interface에는 상수와 추상메서드만 입력됩니다.
	
	//상수 선언
	int MAX_VOLUME = 10;	//public static final 생략해도 상수로 선언됩니다.
	int MIN_VOLUME = 0;	//public static final 생략해도 상수로 선언됩니다.
	
	// public 추상 메서드
	public void turnOn1();
}

 


 

추상 메소드 

  • 리턴타입, 메소드명, 매개변수만 기술되고 중괄호{}를 붙이지 않는 메소드 입니다.
  • public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙습니다.
  • 추상 메소드는 객체 A가 인터페이스를 통해 어떻게 메소드를 호출할수 있는지 방법을 알려주는 역할입니다.

 

예제입니다.

//인터페이스 RemoteControl. 최상단에 위치합니다.
public interface RemoteControl {
	//interface에는 상수와 추상메서드만 입력됩니다.
	
	//상수 선언
	int MAX_VOLUME = 10;	//static final 생략해도 상수로 선언됩니다.
	int MIN_VOLUME = 0;		//static final 생략됨도 상수로 선언됩니다.
	
	// public 추상 메서드
	public void turnOn1();
	public void turnOff();
	void setVolume(int volume);
	
}
//자식클래스 (구현클래스) Television
public class Television implements RemoteControl{
	
	private int volume;
	@Override
	public void turnOn1() {
		System.out.println("TV를 켭니다.");
	}
	
	@Override
	public void turnOff() {
		System.out.println("TV를 끕니다.");
	}
	
	@Override
	public void setVolume(int volume) {
		if(volume>RemoteControl.MAX_VOLUME) {	//최대 볼륨값을 초과 하지 않게.
			this.volume = RemoteControl.MAX_VOLUME;
		}else if(volume < RemoteControl.MIN_VOLUME) {	//최소볼륨값 이하가 되지 않게.
			this.volume = RemoteControl.MIN_VOLUME;
		}else {
			this.volume = volume;
		}
		System.out.println("현재 TV 볼륨 : " + this.volume);
	}
}
// 자식클래스 (구현클래스) Audio
public class Audio implements RemoteControl{

	private int volume;
	
	public void turnOn1() {
		System.out.println("Audio를 켭니다.");
	}
	
	@Override
	public void turnOff() {
		System.out.println("Audio를 끕니다.");
	}
	
	@Override
	public void setVolume(int volume) {
		if(volume>RemoteControl.MAX_VOLUME) {	//최대 볼륨값을 초과 하지 않게.
			this.volume = RemoteControl.MAX_VOLUME;
		}else if(volume < RemoteControl.MIN_VOLUME) {	//최소볼륨값 이하가 되지 않게.
			this.volume = RemoteControl.MIN_VOLUME;
		}else {
			this.volume = volume;
		}
		System.out.println("현재 Audio 볼륨 : " + this.volume);
	}
}
public class RemoteControlExam {

	public static void main(String[] args) {

		RemoteControl rc;

		rc = new Television();
		rc.turnOn1();
		rc.setVolume(5);
		rc.turnOff();
		System.out.println();
		
		rc = new Audio();
		rc.turnOn1();
		rc.setVolume(6);
		rc.turnOff();
		System.out.println();
		
		System.out.println("리모컨 최대 볼륨 : " + RemoteControl.MAX_VOLUME);
		System.out.println("리모컨 최소 볼륨 : " + RemoteControl.MIN_VOLUME);
	}
}
결과 : 
TV를 켭니다.
현재 TV 볼륨 : 5
TV를 끕니다.

Audio를 켭니다.
현재 Audio 볼륨 : 6
Audio를 끕니다.

리모컨 최대 볼륨 : 10
리모컨 최소 볼륨 : 0

 


디폴트 메소드

  • 인터페이스에는 완전한 실행 코드를 가진 디폴트 메소드를 선언 할수 있습니다.
  • 추상 메소드는 실행부(중괄호 {})가 없지만 디폴트 메소드는 실행부가 있습니다. default 키워드가 리턴 타입 앞에 붙습니다.
[public] default 리턴타입 메소드명(매개변수, ...){...}
  • 디폴트 메소드의 실행부에는 상수 필드를 읽거나 추상 메소드를 호출하는 코드를 작성할수 있습니다.
//인터페이스 RemoteControl. 최상단에 위치합니다.
public interface RemoteControl {
	//interface에는 상수와 추상메서드만 입력됩니다.
	
	//상수 선언
	int MAX_VOLUME = 10;	//static final 생략해도 상수로 선언됩니다.
	int MIN_VOLUME = 0;		//static final 생략됨도 상수로 선언됩니다.
	
	// public 추상 메서드
	public void turnOn1();
	public void turnOff();
	void setVolume(int volume);
	
	// default 인트선스 메서드
	default void setMute(boolean mute) {
		if(mute) {
			System.out.println("무음 처리합니다.");
			setVolume(MIN_VOLUME);
		}else {
			System.out.println("무음 해제합니다.");
		}
	}
}
public class RemoteControlExam {

	public static void main(String[] args) {

		RemoteControl rc;

		rc = new Television();
		rc.turnOn1();
		rc.setVolume(5);
		rc.turnOff();
		rc.setMute(true);	//setMute부 
		rc.setMute(false);	//setMute부
		System.out.println();
		
		rc = new Audio();
		rc.turnOn1();
		rc.setVolume(6);
		rc.turnOff();
		System.out.println();
		
		System.out.println("리모컨 최대 볼륨 : " + RemoteControl.MAX_VOLUME);
		System.out.println("리모컨 최소 볼륨 : " + RemoteControl.MIN_VOLUME);
	}
}

 


정적 메소드

  • 구현 객체가 없어도 인터페이스만으로 호출할수 있습니다.
  • 선언시 public을 생략 하더라도 자동으로 컴파일과정에서 붙습니다.
[public | private] static 리턴타입 메소드명(매개변수, ...){...}
  • 정적 실행부를 작성 할때 상수 필드를 제외한 추상 메소드, 디폴트 메소드, private 메소드 등을 호출할 수 없습니다.

 

//인터페이스 RemoteControl. 최상단에 위치합니다.
public interface RemoteControl {
	//interface에는 상수와 추상메서드만 입력됩니다.
	
	//상수 선언
	int MAX_VOLUME = 10;	//static final 생략해도 상수로 선언됩니다.
	int MIN_VOLUME = 0;		//static final 생략됨도 상수로 선언됩니다.
	
	// public 추상 메서드
	public void turnOn1();
	public void turnOff();
	void setVolume(int volume);
	
	// default 인트선스 메서드
	default void setMute(boolean mute) {
		if(mute) {
			System.out.println("무음 처리합니다.");
			setVolume(MIN_VOLUME);
		}else {
			System.out.println("무음 해제합니다.");
		}
	}
	
	static void chageBattery() {
		System.out.println("리모컨 건전지를 교환합니다.");
	}
}
public class RemoteControlExam {

	public static void main(String[] args) {

		RemoteControl rc;

		rc = new Television();
		rc.turnOn1();
		rc.setVolume(5);
		rc.turnOff();
		rc.setMute(true);	//setMute부 
		rc.setMute(false);	//setMute부
		System.out.println();
		
		rc = new Audio();
		rc.turnOn1();
		rc.setVolume(6);
		rc.turnOff();
		rc.setMute(true);	//setMute부 
		rc.setMute(false);	//setMute부
		System.out.println();
		
		System.out.println("리모컨 최대 볼륨 : " + RemoteControl.MAX_VOLUME);
		System.out.println("리모컨 최소 볼륨 : " + RemoteControl.MIN_VOLUME);
		
		System.out.println();
		RemoteControl.chageBattery();
        //기존 정적메소드를 사용하듯이
        //인터페이스에서도 정적메소드를 사용하면
        //객체의 생성 필요없이 메소드 호출이 가능합니다.
	}
}

 


private 메소드

  • 인터페이스의 상수필드, 추상메소드, 디폴트메소드, 정적 메소드는 모두 public 접근 제한을 가집니다.
  • public을 생략하더라도 항상 외부에서 접근이 가능합니다.
  • 이에 반해 인터페이스에 외부에서 접근할수 없는 private 메소드 선언도 가능합니다.

  • private 메소드는 디폴트 메소드 안에서만 호출이 가능합니다.
  • private 정적 메소드는 정적 메소드 안에서도 호출이 가능합니다.

 

다음은 자바 15이상의 버전에서 구동되는 코드입니다.

 

package java230116;

public interface Service {
	
	//디폴트 메소드
	default void defaultMethod1() {
		System.out.println("defalutMethod1 종속코드");
		defaultCommon();
	}
	
	default void defaultMethod2() {
		System.out.println("defalutMethod2 종속코드");
		defaultCommon();
	}
	
	//private 메소드
	private void defaultCommon() {
		System.out.println("defaultMethod 중복코드 A");
		System.out.println("defaultMethod 중복코드 B");
	}
	static void staticMethod1() {
		System.out.println("staticMethod1 종속코드");
		staticCommon();
	}
	static void staticMethod2() {
		System.out.println("staticMethod2 종속코드");
		staticCommon();
	}


	//private 정적 메소드
	private static void staticCommon() {
		System.out.println("defaultMethod 중복코드 A");
		System.out.println("defaultMethod 중복코드 B");
	}
}
public class ServiceImp implements Service{
}
public class ServiceExam {

	public static void main(String[] args) {
		
		//인터페이스 변수 선언과 구현 객체 대입
		Service service = new ServiceImp();
		
		// 디폴트 메소드 호출
		service.defaultMethod1();
		System.out.println();
		service.defaultMethod2();
		System.out.println();
		
		//정적 메소드 호출
		Service.staticMethod1();
		System.out.println();
		Service.staticMethod2();
		System.out.println();
		
	}
}

 


다중 인터페이스

  • 구현 객체는 여러 개의 인터페이스를 통해 구현 객체를 사용 할 수 있습니다.

  • 구현 클래스는 인터페이스 A와 인터페이스 B를 implements 뒤에 쉼표로 구분해서 작성해, 모든 인터페이스가 가진 추상 메소드를 재정의 합니다.
public class 구현클래스명 implments 인터페이스A, 인터페이스B {
	//모든 추상메소드 재정의
}

 

 

Interface가 가진 추상메서드를 구현클래스에서 재정의 해줄때 implement를 사용합니다.
구현클래스에서 속성을 재정의(오버라이딩) 해준 후 메인메서드에서 호출하여 사용합니다.

예시입니다.

//인터페이스A
public interface InterfaceA {
	//추상메소드
	void methodA();
}
//인터페이스 B
public interface InterfaceB {
	//추상 메서드
	void methodB();
}
//인터페이스C
public interface InterfaceC extends InterfaceA, InterfaceB{
	// 추상클래스
	void methodC();
}
//실행부 소스코드
public class ExtendsExample {

	public static void main(String[] args) {
		//구현클래스로 객체생성 = A, B, C
		InterfaceCImp impl = new InterfaceCImp();
		impl.methodA();
		impl.methodB();
		impl.methodC();
		
		InterfaceA ia = impl;
		//InterfaceA : methodA()만 실행가능
		ia.methodA();
//		ia.methodB();
//		ia.methodC();
		System.out.println();
		
		InterfaceB ib = impl;
		//InterfaceB : methodB()만 실행가능
//		ib.methodA();
		ib.methodB();
//		ib.methodC();
		System.out.println();
		
		InterfaceC ic = impl;
		ic.methodA();
		ic.methodB();
		ic.methodC();
	}
}
결과 :
InterfaceCImp-methodA() 실행

InterfaceCImp-methodB() 실행

InterfaceCImp-methodA() 실행
InterfaceCImp-methodB() 실행
InterfaceCImp-methodC() 실행

 


자동 타입변환

자동으로 타입변환이 일어나는 것을 뜻합니다.

public interface A {
}
//A를 상속받은 구현클래스 B
public class B implements A{
}
//A를 구현하는 구현클래스 C
public class C implements A{
}
//A->B를 상속받은 구현클래스 D
public class D extends B{
}
//A->C를 상속받은 구현클래스 E
public class E extends C{
}
public class PromotionExam {

	public static void main(String[] args) {
		
		//구현 객체 생성
		B b = new B();
		C c = new C();
		D d = new D();
		E e = new E();
		
		// 인터페이스A 변수 선언
		A a;
		
		//변수에 구현 객체 대입
		a = b;	// 구현 클래스 -> 부모 인터페이스 : 자동 타입 변환
		a = c;	// 구현 클래스 -> 부모 인터페이스 : 자동 타입 변환
		a = d;	// 자식 클래스 -> 부모 인터페이스 : 자동 타입 변환
		a = e;	// 자식 클래스 -> 부모 인터페이스 : 자동 타입 변환
		
		//구현 클래스 <- (구현클래스)부모 인터페이스 : 강제 타입 변환
	}
}

 


강제 타입변환

  • 캐스팅 기호를 사용해서 인터페이스 타입을 구현 클래스 타입으로 변환시키는 것

  • 구현 객체가 인터페이스 타입으로 자동 변환되면, 인터페이스에 선언된 메소드만 사용 가능합니다.

예시입니다.

 


public interface Vehicle {
	
	public void run();
}
public class Bus implements Vehicle{

	@Override
	public void run() {
		System.out.println("버스가 달립니다.");
	}
	
	//추가 메서드
	public void checkFare() {
		System.out.println("승차요금을 체크합니다.");
	}
}
public class CastingExam {

	public static void main(String[] args) {

		Vehicle vehicle = new Bus();
		
		vehicle.run();
		
//		Bus bus = vehicle; 
		//vehicle에는 run 밖에없어서 Bus에서 구현된 추가된 checkFare부분이 없어서 
		//따라서 매칭오류가 발생
		
		Bus bus = (Bus)vehicle; // 강제형변환
		//Vehicle 형이 Bus 형이 되며 Bus의 메소드인 CheckFare를 포함한 상태로 변해
		//vehicle에서도 checkFare()를 사용할수 있습니다.
		
		bus.run();
		bus.checkFare();
	}
}

 


다형성

  • 사용 방법은 동일하지만 다양한 결과가 나오는 성질입니다.

 

 

  • 인터페이스 역시 다형성을 구현하기 위해 재정의와 자동 타입 변환 기능을 이용합니다.

 

예시입니다.

 

public interface Tire {

	void roll();
}
public class HankookTire implements Tire{

	@Override
	public void roll() {
		System.out.println("한국타이어가 굴러갑니다.");
	}
}
public class KumhoTire implements Tire{

	@Override
	public void roll() {
		System.out.println("금호타이어가 굴러갑니다.");
	}
}
public class Car {

	Tire tire1 = new HankookTire();
	Tire tire2 = new KumhoTire();
	
	void run() {
		tire1.roll();
		tire2.roll();
	}
}
public class CarExam {

	public static void main(String[] args) {

		Car myCar = new Car();
		
		myCar.run();
		System.out.println();
		myCar.tire1 = new KumhoTire();
		myCar.tire2 = new HankookTire();
		
		myCar.run();
	}
}
결과 : 
한국타이어가 굴러갑니다.
금호타이어가 굴러갑니다.

금호타이어가 굴러갑니다.
한국타이어가 굴러갑니다.

 


필드의 다형성

 

 


매개변수의 다형성

  • 매개 변수 타입을 인터페이스로 선언하면 메소드 호출시 다양한 구현 객체를 대입할수 있습니다.

 

 

버스와 택시로 예시를 들어보겠습니다.

public interface Vehicle {
	//추상메소드
	void run() {
	}
}
public class Driver {

	void drive(Vehicle vehicle) {	//매개변수 타입을 인터페이스 타입으로 선언
		vehicle.run();
	}
}
public class Bus implements Vehicle{

	@Override
	public void run() {
		System.out.println("버스가 달립니다.");
	}
	public void checkFare(){
		System.out.println("요금을 계산합니다.");
	}
}
public class Taxi implements Vehicle{
	
	//추상 메서드 재정의
	@Override
	public void run() {
		System.out.println("택시가 달립니다.");
	}
}
public class DriverExam {

	public static void main(String[] args) {
		
		//Driver 객체생성
		Driver driver = new Driver();
		
		//Vehicle 구현 객체 생성
		Bus bus = new Bus();
		Taxi taxi = new Taxi();
		
		//매개변수에 구현 객체 대입(다형성 : 실행 결과 다름)
		driver.drive(bus);	// 자동 타입변환 -> 오버라이딩 메소드 호출 -> 결과 버스
		driver.drive(taxi);	// 자동 타입변환 -> 오버라이딩 메소드 호출 -> 결과 택시
	}
}
결과 :
택시가 달립니다.

요금을 계산합니다.
버스가 달립니다.

 


instanceof 연산자

 

  • 인터페이스에서도 객체 타입을 확인 하기 위해 instanceof 연산자를 사용가능합니다.

  • Java12 부터는 instanceof 연산의 결과가 true일경우, 우측 타입변수를 사용할수 있기 때문에 강제타입변환이 필요없습니다.

public class InstanceofExam {

	public static void main(String[] args) {

		// 구현 객체 생성
		Taxi taxi = new Taxi();
		Bus bus = new Bus();
		
		// ride() 메소드 호출 시 구현 객체를 매개값으로 전달합니다.
		ride(taxi);
		System.out.println();
		ride(bus);
		
	}
	// 내부 클래스
	public static void ride(Vehicle vehicle) {
		// 방법 1 : JAVA12 이전
		if(vehicle instanceof Bus) {
			Bus bus = (Bus)vehicle;
			bus.checkFare();
		}
		
		// 방법 2 : JAVA 12 이후
//		if(vehicle instanceof Bus bus) {
//			bus.checkFare();
//		}
		vehicle.run();
	}
}

 


봉인된 인터페이스(sealed)

  • Java 15 부터 무분별한 자식 인터페이스 생성을 방지하기위해 봉인된 인터페이스를 사용합니다.

 

 

  • sealed 키워드를 사용하면 permits 키워드 뒤에 상속 가능한 자식 인터페이스를 지정할수 있습니다.
  • non-sealed는 봉인을 해제 한다는 뜻입니다.

 

 

728x90

'JAVA > Java 기초' 카테고리의 다른 글

컬렉션 자료구조3.java  (0) 2023.01.26
컬렉션자료구조2.java  (0) 2023.01.26
추상클래스.java  (0) 2023.01.16
메소드 재정의.jav  (0) 2023.01.13
오버라이딩(overriding).java  (0) 2023.01.12