인터페이스

서현서현·2022년 3월 29일
0

JAVA

목록 보기
11/27
post-thumbnail

🪴 인터페이스

  • 객체와 객체 사이엔 Interface가 존재한다
  • 개발코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다. = 개발코드는 객체 내부구조 알 필요없고 인터페이스의 메소드만 알면 된다! ⇒ 개발코드를 수정하지 않아도 객체를 변경 할 수 있다는 장점

+) 인터페이스 안의 상수는 이미 상수이기때문에 static final 붙이지 않음. 또한 private도 사용하지 않는다. (아무것도 안쓴상태 = public)

+) 인터페이스엔 실제 body가 없고 이런 동작들을 할거다~ 선언만 하므로 추상메소드임.

🪴 인터페이스 선언

  • 인터페이스는 상수필드와 추상메소드만을 구성멤버로 가진다. (인터페이스는 객체가 아니므로 생성자 X)
[public] interface 인터페이스이름 {...}

상수필드 선언

  • 인터페이스는 개체의 사용방법을 정의한것이므로 데이터를 저장 할 수 있는 인스턴스나 정적필드는 선언 불가하다. 그러나 상수필드는 선언 가능!
[public static final] 타입 상수이름 =;
// 선언과 동시에 초기값 지정, 괄호는 디폴트라 생략가능

public interface RemoteControl {
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;

}

추상메소드 선언

  • 인터페이스로 호출된 메소드는 객체에서 실행된다. 때문에 인스턴스의 메소드는 실행블록이 필요없는 추상메소드로 선언
  • 추상메소드란? (리턴타입, 메소드이름, 매개변수만 기술되고 중괄호 X)
[public abstract] 리턴타입 메소드이름(매개변수, ...);
//괄호는 디폴트

public interface RemoteControl {
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
	
	//추상메소드
	public void turnOn();
	public void turnOff();
	public void setVolume(int volume);

}

🪴 인터페이스 구현

  • 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다
  • 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드이름, 매개타입, 리턴타입을 가진 실체메소드를 가지고 있어야 한다.
  • 이러한 객체를 인터페이스의 구현객체라고 하고, 그 구현객체를 생성하는 클래스를 구현클래스 라고 한다.

구현클래스

  • 보통클래스와 동일하나 인터페이스타입임을 알려주기 위해 선언부에 implements 붙인다.
public class 구현클래스이름 implements 인터페이스이름{
	// 인터페이스에 선언된 추상메소드의 실체메소드 선언
}

public class Television implements RemoteControl {
	private int volume;

	@Override
	public void turnOn() {
		System.out.println("티비를 켭니다");

	}

	@Override
	public void turnOff() {
		System.out.println("티비를 끕니다");

	}

	@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("현재 티비 볼륨: "+volume);
	}

}
💡 구현클래스에서 인터페이스의 추상메소드의 실체메소드를 입력시, 인터페이스의 모든 메소드는 기본으로 public이기 때문에 그보다 더 낮은 접근제한자를 사용 할 수 없다.
  • 구현클래스가 작성되면 new연산자로 객체를 생성할 수 있다
  • Television tv = new Television 으로 새 객체를 생성하면 인터페이스를 사용한것이 아니므로 다음과 같이 선언한다
public class RemoteControlExample{
	public static void main(String[] args){
		RemoteControl rc;
		rc = new Television();
		rc = new Audio();
// 인터페이스 변수 rc에 구현객체를 대입해서 사용
	}
}

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

  • 인터페이스 A와 B 두개가 같은 객체의 메소드를 호출할 수 있으려면
public class 구현클래스이름 implements 인터페이스A, 인터페이스B{
	// 인터페이스A에 선언된 추상메소드의 실체메소드 선언
	// 인터페이스B에 선언된 추상메소드의 실체메소드 선언
}
public interface Searchable {
	void search(String url);

}
public class SmartTelevision implements RemoteControl, Searchable {
	
	private int volume;

	@Override
	public void search(String url) {
		System.out.println(url+"을 검색합니다");

	}

	@Override
	public void turnOn() {
		System.out.println("티비를 켭니다");

	}

	@Override
	public void turnOff() {
		System.out.println("티비를 끕니다");

	}

	@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("현재 티비 볼륨: "+this.volume);
	}

}
public class SmartTelevisionExample {
	public static void main(String[] args) {
		SmartTelevision tv = new SmartTelevision();
		
		RemoteControl rc = tv;
		Searchable searchable = tv;
		// 인터페이스 변수에 구현객체 대입
	}

}

구현객체를 변수에 대입 한 이후엔 어떻게 해야할까?

🪴 인터페이스 사용

  • 클래스 선언시 인터페이스는 필드/생성자 또는 메소드의 매개변수/생성자 또는 메소드의 로컬변수로 선언 될 수 있다
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를 이용해 turnOn(), turnOff()메소드를 호출하면 구현객체의 turnOn()과 turnOff() 메소드가 실행된다.

(1) 필드로 선언된 rc
MyClass myClass = new MyClass();
myClass.rc.turnOn(); // Television의 turnOn() 실행
myClass.rc.setVolume(5); // Television의 setVolume(5) 실행

(2) 생성자의 매개변수타입으로 선언된 rc
MyClass( RemoteControl rc ) {
	this.rc = rc;
	rc.turnOn();
	rc.setVolume(5);
}
오디오의 메소드를 실행하고 싶을 경우 아래처럼 하면 된다
MyClass myClass = new MyClass(new Audio));

(3) 로컬변수로 선언된 rc
void methodA(){
	RemoteControl rc = new Audio();
	rc.turnOn();
	rc.setVolume(5);
}

(4) 메소드의 매개변수타입으로 선언된 rc
void methodB(RemoteControl rc){
	rc.turnOn();
	rc.setVolume(5);
}
다음과 같이 메소드가 호출되었을경우 Television의 메소드가 실행
MyClass myClass = new MyClass();
myClass.methodB(new Television());
public class MyClass {
	//필드
	RemoteControl rc = new Television();
	
	//생성자
	MyClass(){}
	
	MyClass(RemoteControl rc){
		//생성자의 매개값으로 구현객체 대입
		this.rc = rc;
		rc.turnOn();
		rc.setVolume(5);
	}
	
	//메소드
	void methodA() {
		RemoteControl rc = new Audio();
		rc.turnOn();
		rc.setVolume(5);
	}
	
	void methodB(RemoteControl rc) {
		rc.turnOn();
		rc.setVolume(5);
	}

}
public class MyClassExample {
	public static void main(String[] args) {
		System.out.println("1)----------------");
		
		MyClass myClass1 = new MyClass();
		myClass1.rc.turnOn();
		myClass1.rc.setVolume(5);
		// 실행결과 : 티비를켭니다 / 현재 티비볼륨:5
		
		System.out.println("2)----------------");
		MyClass myClass2 = new MyClass(new Audio());
	  // 실행결과 : 오디오를켭니다 / 현재 오디오볼륨:5		
		
		System.out.println("3)----------------");
		MyClass myClass3 = new MyClass();
		myClass3.methodA();
		// 실행결과 : 오디오를켭니다 / 현재 오디오볼륨:5

		System.out.println("4)----------------");
		MyClass myClass4 = new MyClass();
		myClass4.methodB(new Television());
		// 실행결과 : 티비를켭니다 / 현재 티비볼륨:5
	}

}

🪴 타입변환과 다형성

  • 앞서배운 상속의 다형성과 비슷하다
  • 인터페이스 변수로 자동 타입변환이 가능하다.

🪴 필드의 다형성

// 인터페이스
public interface Tire {
	public 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 frontLeftTire = new HankookTire();
	Tire frontRightTire = new HankookTire();
	
	void run() {
		frontLeftTire.roll();
		frontRightTire.roll();
	}

}

// 테스트
public class CarExample {
	public static void main(String[] args) {
		Car mycar = new Car();
		
		mycar.run();
		
		mycar.frontLeftTire = new KumhoTire();
		mycar.frontRightTire = new KumhoTire();
		mycar.run();
		
	}

}

// 결과
한국타이어가 굴러갑니다
한국타이어가 굴러갑니다
금호타이어가 굴러갑니다
금호타이어가 굴러갑니다

🪴 매개변수의 다형성

  • 상속에서 매개변수를 부모타입으로 선언하고 자식객체를 대입해 호출했듯, 이번에는 매개변수를 인터페이스 타입으로 선언하고 구현객체로 호출한다.
// 매개변수의 인터페이스화

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

}

// 인터페이스
public interface Vehicle {
	public void run();
}

// 구현 클래스
public class Bus implements Vehicle{
	@Override
	public void run() {
		System.out.println("버스가 달립니다");
	}

}

// 구현 클래스
public class Taxi implements Vehicle{
	@Override
	public void run() {
		System.out.println("택시가 달립니다");
	}

}

// 매개변수의 다형성 테스트
public class DriverExample {
	public static void main(String[] args) {
		Driver driver = new Driver();
		
		Bus bus = new Bus();
		Taxi taxi = new Taxi();
		
		driver.drive(bus);
		driver.drive(taxi);
		
	}

}

🪴 강제타입변환

  • 구현객체가 인터페이스타입으로 변환하면 인터페이스에 선언된 메소드만 사용 가능(당연함)
// 인터페이스
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 VehicleExample {
	public static void main(String[] args) {
		Vehicle vehicle = new Bus();
		
		vehicle.run();
		//vehicle.checkFare(); <- 인터페이스에 없으므로 실행X
		
		Bus bus = (Bus) vehicle;
		bus.run();
		bus.checkFare();
	}

}

//결과
버스가 달립니다
버스가 달립니다
승차요금을 체크합니다

🪴 객체타입확인

  • 강제타입변환은 구현객체가 인터페이스타입으로 변환되어있는 상태에서 가능, 그러나 구현객체가 변환되어있는지 알 수 없는 상태에서 무작정 강제타입변환하면 ClassCastException
  • 인스턴스 타입으로 변환 됐는지 확인 하는 방법? → instanceof를 여기서도 쓴다!
if (vehicle instanceof Bus) {
	Bus bus = (Bus) vehicle
}

🪴 인터페이스 상속

  • 인터페이스는 클래스와는 달리 다중상속을 허용한다.

0개의 댓글