8. 인터페이스
8.1 인터페이스 역할
인터페이스 웹 개발할때 많이 사용됨.
다형성 부여에 필요한 기술임 인터페이스는. 즉 인터페이스 = 다형성이다.
사전적의미로 두 장치를 연결하는 접속기
인터페이스는 두 객체를 연결하는 역할을 한다.
직접 호출하면 간단할텐데 인터페이스를 쓰는이유?
객체B에는 매우 많은 필드 메소드가 있기때문에 호출한는 객체A가 다 알 필요가 없다.
자동차 운전할때 자동차 부품을 다 알지 않아도 되듯이 계기판이 인터페이스 역할을 하는 것임.
객체 A가 인터페이스의 메소드를 호출하면 실제 로 진행되는 것은 인터페이스 뒤편의 객체B 이다.
장점은 인터페이스 뒤의객체 B가 C로 교체되어도 문제가 생기지 않는다.
실행결과가 다르게 설계되었으면 객체A는 객체 교체로 인해 다른 결과를 얻게되는 것이다.
따라서 다형성을 구현하는데 주된 기술로 이용되고 상속보다 인터페이스를 이용해서 다형성을 구현하는 경우가 더 많다.
8.2 인터페이스와 구현 클래스 선언
인터페이스는 .java소스파일로 작성되고 ~.class 형태로 컴파일되기 때문에 물리적형태는 클래스와 동일
그러나 소스를 작성할때 선언하는 방법과 구성 멤버가 클래스와 다르다.
8.2.1 인터페이스 선언
인터페이스 선언은 class대신 interface키워드를 사요한다.
[public] interface 인터페이스명{}
사용할수 있는 종류는 다음과 같다.
public 상수필드 public 추상메소드 public 디폴트메소드 public 정적메소드 private 메소드 private 정적메소드
리모컨을 이용해서 상호작용을 한다고 생각해서 RemoteControl을 인터페이스로 만드는거임.
public interface RemoteControl {
//추상메소드선언
public void turnOn();인터페이스로 turnOn을 사용할수잇다.라고 이해가능 인터페이스를 통해서 실제 사용되는 객체의 turnOn이 실행됨
인터페이스는public을 안붙여도 기본이 public임. abstract 생략해도 추상메소드임.
RemoteControl인터페이스로 trunOn을 사용할수잇다.라고 생각
리모콘을 누르지만 실제 켜지는건 티비라고 생각하면됨.
8.2.2 구현클래스 선언
객체A가 인터페이스의 추상 메소드를 호출하면 객체B의 메소드를 실행한다.
객체B는 인터페이스에 선언된 추상메소드와 동일한 선언부를 가진(재정의된)메소드를 가지고있어야함.
원래있는데 다시선언하는게 재정의임 (상속관련x여도)
객체 B에 인터페이스를 구현한(implement)한 객체라고 한다.
public class B implements 인터페이스명{}
8.2.3 변수 선언과 구현객체 대입
인터페이스도 하나의 타입의므로 변수의 타입으로 사용할 수 있다.
인터페이스는 참조타입에 속하므로 인터페이스 변수에는 객체를 참조하고 있지않다는 뜻으로 null을 대입할수있다.
RemoteControl rc;
RemoteControl rc = null;
rc = new Television();
rc.trunOn();
public interface RemoteControl {
//추상메소드
void turnOn();
}public class Audio implements RemoteControl{
@Override
public void turnOn() {
System.out.println("Audio를 켭니다.");
}}public class Televison implements RemoteControl{
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}}public class RemoteControlExample {
public static void main(String[] args) {
//rc변수에 Televison 객체를 대입
RemoteControl rc = new Televison();
rc.turnOn();
//rc변수에는 RemoteControl을 구현한 어떠한 객체든 대입이 가능하다.
// 만약 Audio객체가 구현객체라면 다음과 같이 Audio객체로 교체해서 대입가능하다.
//rc변수에 Audio 객체를 대입(교체시킴)
rc = new Audio();
rc.turnOn();
}}8.3 상수필드
인터페이스는 상수필드를 멤버로 가질 수 있다.
인터페이스에 선언된 필드는 모두 public static final 특성을 갖기 때문에 생략하더라도 자동적으로 컴파일과정에서 붙는다.
상수명은 대문자로 작성하되 서로 다른 단어로 구성되어있을경우 언더바로 연결하는게 관례이다.
public interface RemoteControl {
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
//컴파일 과정중에 자동으로 이렇게 됨. public static final int MAX_VOLUME = 10;
}public class RemoteControlExample {
public static void main(String[] args) {
System.out.println("리모콘 최대 볼륨: " + RemoteControl.MAX_VOLUME); //10
System.out.println("리모콘 최소 볼륨: " + RemoteControl.MIN_VOLUME); //0
}8.4 추상메소드
인터페이스는 구현 클래스가 재정의해야햐는 public 추상메소드를 멤버로 가질수 있다.
public interface RemoteControl {
//상수필드
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
//추상메소드
void turnOn();
void turnOff();
void setVolume(int volume);
}public class Televison implements RemoteControl{
//필드
private int volume;
//추상메소드 오버라이딩
@Override
public void turnOn() {
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 볼륨: " + volume);
}}public class Audio implements RemoteControl{
//필드
private int volume;
//추상메소드 오버라이딩
@Override
public void turnOn() {
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 볼륨: " + volume);
}}
**********
public class RemoteControlExample {
public static void main(String[] args) {
//인터페이스 변수선언
RemoteControl rc;
//텔레비젼 객체 생성후 인터페이스 변수에 대입
rc = new Televison();
rc.turnOn();
rc.setVolume(5);
rc.turnOff();
rc = new Audio();
rc.turnOn();
rc.setVolume(5);
rc.turnOff();
}}8.5 디폴트 메소드
인터페이스에는 완전한 실행코드를 가진 디폴트 메소드를 선언할수잇다. 추상 메소드는 실행부 {}가 없지만 디폴트 메소드는 실행부가 있다.
선언방법은 클래스 메소드와 동일한데 차이점은 default키워드가 리턴타입앞에 붙는다.
메소드 앞에 아무것도 안붙이면 추상메소드가 되니까 붙여줘야함.
[public] default 리턴타입 메소드명(매개변수,...){}
디폴트 메소드의 실행부에는 상수필드를 읽거나 추상메소드를 호출하는 코드를 작성할수있다.
RemoteControl 안에 무음 처리 기능을 제공하는 setMute()디폴트 메소드를 선언해보자.
디폴트 메소드도 구현객체가 필요한 메소드이다.
따라서 호출하려면 Television객체를 다음과 같이 인터페이스 변수에 대입하고나서 setMute()를 호출해야한다.
public interface RemoteControl {
//상수필드
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
//추상메소드
void turnOn();
void turnOff();
void setVolume(int volume);
//디폴트 인스턴스 메소드
default void setMute(boolean mute)
{
if(mute)
{
System.out.println("무음 처리합니다.");
//추상메소드 호출하면서 상수필드 사용
setVolume(MIN_VOLUME);
}
else
{
System.out.println("무음 해제합니다.");
}}}8.6 정적메소드
인터페이스에는 정적 메소드 선언도 가능하다.
추상메소드와 디폴트 메소드는 구현객체가 필요하디만 정적메소드는 구현객체가 없어도 인터페이스만으로 호출가능
선언방법은 클래스 메소드와 완전 동일
[public | private] static 리턴타입 메소드명(매개변수, ...){}
//정적메소드
static void changeBattery()
{
System.out.println("리모콘 건전지를 교환합니다.");
}public static void main(String[] args) {
//정적메소드 호출
RemoteControl.changeBattery(); //리모콘 건전지를 교환합니다.
//주의할점은 실행부를 작성할때 추상메소드 디폴트 메소드 private메소드를 호출할 수없다.
// 위의 메소드는 구현객체가 필요한 인스턴스 메소드 이기때문
}}8.7 private 메소드
인터페이스의 상수필드 추상메소드 디폴트 메소드 정적메소드는 모두 public접근 제한을 갖는다.
이 멤버들을 선언할때는 생성하더라도 컴파일과정에서 자동으로 붙는다.
또한 외부에서 접근할 수 없는 private 메소드선언도 가능하다.
private메소드는 디폴트 메소드 안에서만 호출이 가능한 반면
private 정적 메소드는 디폴트메소드 뿐만 아니라 정적 메소드 안에서도 호출이 가능하다.
private 메소드의 용도는 디폴트와 정적 메소드들의 중복코드를 줄이기 위함이다.
다음은 service인터페이스에서 2개의 디폴트메소드와 2개의 정적메소드 중
중복코드 부분을 각각 private메소드와 private정적메소드로 선언하고 호출하는 방법이다.
public class ServiceImpl implements Service{
}public interface Service {
//디폴트 메소드
default void defaultMethod1()
{
System.out.println("defaultMethod1 종속 코드");
defaultCommon();
}
default void defaultMethod2()
{
System.out.println("defaultMethod2 종속 코드");
defaultCommon();
}
//private 메소드
private void defaultCommon()
{
System.out.println("defaultMethod1 중복 코드A");
System.out.println("defaultMethod2 중복 코드B");
}
//정적 메소드
static void staticMethod1()
{
System.out.println("staticMethod1 종속 코드");
staticCommon();
}
static void staticMethod2()
{
System.out.println("staticMethod2 종속 코드");
staticCommon();
}
private static void staticCommon()
{
System.out.println("staticMethod1 중복 코드C");
System.out.println("staticMethod2 중복 코드D");
}}public class ServiceExample {
public static void main(String[] args) {
//인터페이스 변수 선언과 구현 객체 대입
Service service = new ServiceImpl();
//디폴트 메소드 호출
service.defaultMethod1();
System.out.println();
service.defaultMethod2();
System.out.println();
//정적메소드 호출
Service.staticMethod1();
System.out.println();
Service.staticMethod2();
System.out.println();
}}8.8 다중인터페이스 구현
구현 객체는 여러개의 인터페이스를 implements할수있다. 구현객체가 인터페이스A와 인터페이스B를 구현하고있다면
각각의 인터페이스를 통해 구현객체를 사용할수잇다.
왜? 인터페이스는 사용설명서이기때문에 사용방법을 다르게해서 객체를 이용하고 싶을때 이렇게 사용한다.
public class 구현 클래스명 implements 인터페이스A, 인터페이스B {//모든 추상메소드 재정의}
인터페이스A변수 = new 구현클래스명 인터페이스B변수 = new 구현클래스명
public class SmartTelevison implements RemoteControl, Searchable{
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
@Override
public void search(String url) {
System.out.println(url + "을 검색합니다.");
}} public class MultiInterfaceImplExample {
public static void main(String[] args) {
RemoteControl rc = new SmartTelevison();
rc.turnOn();
rc.turnOff();
Searchable serchable = new SmartTelevison();
serchable.search("www.youtube.com");
}}8.9 인터페이스 상속
인터페이스도 다른 인터페이스를 상속할수잇으며 클래스와는 달리 다중상속을 허용한다.
자식인터페이스의 구현클래스는 자식인터페이스의 메소드 뿐만 아니라 부모 인터페이스의 모든 추상 메소드를 재정의해야한다.
구현객체는 자식 및 부모 인터페이스 변수에 대입가능하다.
public class InterfaceClmpl implements InterfaceC {
@Override
public void methodA() {
System.out.println("InterfaceClmpl-methodA() 실행");
}
@Override
public void methodB() {
System.out.println("InterfaceClmpl-methodB() 실행");
}
@Override
public void methodC() {
System.out.println("InterfaceClmpl-methodC() 실행");
}}public class ExtendsExample {
public static void main(String[] args) {
InterfaceClmpl impl = new InterfaceClmpl();
InterfaceA ia = impl;
ia.methodA();
System.out.println();
InterfaceB ib = impl;
ib.methodB();
System.out.println();
InterfaceC ic = impl;
ic.methodA();
ic.methodB();
ic.methodC();
}}8.10 타입변환
인터페이스의 타입변환은 인터페이스와 구현클래스간에 발생한다.
인터페이스 변수에 구현 객체를 대입하면 구현객체는 인터페이스 타입으로 자동변환된다.
반대로 인터페이스 타입을 구현객체 클래스 타입으로 변환시킬수있는데 이는 강제 타입변환이 필요하다.
부모 클래스가 인터페이스를 구현하고 있다면 자식클래스도 인터페이스 타입으로 자동타입 변환될 수있다.
인터페이스 변수 = 구현객체;
강제타입변환
구현클래스 변수 = (구현클래스) 인터페이스변수;
상속관계에서 강제타입변환해서 자식의 메소드나 필드도 사용하기 위해서 사용하는 것 과 같은 이유이다. 다형성을 위해서 하는거임.
public interface Vehicle {
//추상메소드
void run();
}public class Bus implements Vehicle{
//추상메소드 재정의
@Override
public void run() {
System.out.println("버스가 달립니다.");
}
//추가메소드
public void checkFare()
{
System.out.println("승차요금을 체크합니다.");
}}public class PromotionExample {
public static void main(String[] args) {
//인터페이스 변수 선언과 구현객체 선언
Vehicle vehicle = new Bus();
//인터페이스 통해서 호출
vehicle.run();
//강제 타입 변환 후 호출
Bus bus = (Bus) vehicle;
bus.run();
bus.checkFare();
}}8.11 다형성
7장 상속에서 다형성에 대해 살펴보았다. 인터페이스 또한 다형성을 구현하는 주된 기술로 사용된다.
현업에서는 상속보다 인터페이스를 통해서 다향성을 구현하는 경우가 더많다.
구현객체 B와 구현객체 C중 어느 객체가 인터페이스에 대입되엇느냐에 따라서 객체A의 메소드 호출 결과는 달라질 수잇다.
상속의 다형성과 마찬가지로 인터페이스 역시 다형성을구현하기 위해 재정의와 자동타입변환기능을 사용한다.
8.11.1 필드의 다형성
public class Car {
//필드
Tire tire1 = new HankookTire(); //인터페이스 타입으로 타입만듬.
//자동타입변환이라 tire 인터페이스 = 구현객체한거
Tire tire2 = new HankookTire();
//메소드
void run()
{
tire1.roll();
tire2.roll();
}}public class CarExample {
public static void main(String[] args) {
//자동차 객체생성
Car myCar = new Car();
//rum()메소드 실행;
myCar.run();
//타이어 객체 교체
//Tire tire1 = new KumhoTire();
//인터페이스 = 구현객체();한거
myCar.tire1 = new KumhoTire();
myCar.tire2 = new KumhoTire();
//run()메소드 실행(다형성 : 실행결과가 다름)
myCar.run();
}}8.11.2 매개변수의 다형성
메소드 호출시 매개값을 다양화하기 위해 상속에서는 매개변수타입을 부모타입으로 선언하고 호출할때는 다양한 자식객체를 대입햇다.
이것은 자동타입변환때문인데 비슷한 원리로 매개변수타입을 인터페이스로 선언하면 메소드 호출시 다양한 구현객체를 대입할수있다.
public class Driver {
void drive (Vehicle vehicle)
//Vehicle vehicle = new Bus(); 인터페이스 = 구현객체
//Vehicle vehicle = new Taxi();
{
vehicle.run();
}}public class DriverExample {
public static void main(String[] args) {
//Driver 객체 생성
Driver driver = new Driver();
//Vehicle 구현객체 생성
Bus bus = new Bus();
Taxi taxi = new Taxi();
//매개값으로 구현 객체 대입(다형성 실행결과가 다름)
//void drive (Vehicle vehicle) 이건데
//Vehicle vehicle = new Bus(); 인터페이스 = 구현객체 자동타입변환이 되니
//void drive (new Bus()) 되서 된거임
driver.drive(bus);
driver.drive(taxi);
}}8.12 객체타입확인
상속에서 객체 타입을 확인하기 위해 instanceof연산자를 사용했는데 인터페이스에서도 사용할 수 잇다.
인터페이스 변수 instanceof 대입된객체 하면 비교가 가능하다.
public class InstanceofExample {
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)
{
/*
if(vehicle instanceof bus)
{
Bus bus = (Bus) vehicle;
bus.checkFare();
}
*/
//자바 12부터 사용가능
if(vehicle instanceof Bus bus)
{
bus.checkFare();
}
vehicle.run();
}}'기초단계 > JAVA' 카테고리의 다른 글
| 2022.12.15-4 JAVA 복습 예외처리 (1) | 2022.12.15 |
|---|---|
| 2022.12.15-3 JAVA 복습 중첩클래스와 익명객체 (1) | 2022.12.15 |
| 2022.12.15 JAVA 복습 상속 (0) | 2022.12.15 |
| 2022.12.14-6 JAVA 복습 클래스 (0) | 2022.12.14 |
| 2022.12.14-5 JAVA 복습 클래스 (0) | 2022.12.14 |