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 |