추상 클래스(규격 강제)의 업그레이드판
인터페이스의 구성요소는 모두 추상 메서드이다.(일반 멤버변수, 일반 멤버메서드 없음, 정적 멤버는 사용함)
public interface 인터페이스명 {...}
RemoteControl.interface
public interface RemoteControl {
// 인터페이스의 멤버는 상수, 정적 멤버 메서드, 추상 메서드, 디폴트 메서드만 사용이 가능함
// 인터페이스에서 사용할 수 있는 멤버는 일반 멤버 변수와 메서드는 불가능하기 때문에 static 및 abstract 를 생략할 수 있음
// 인터페이스는 객체 사용 설명서이기 때문에 상수, 정적 메서드, 추상 메서드, 디폴트 메서드만 사용이 가능함
// 정적 메서드 및 디폴트 메서드는 자바 버전 8부터 사용이 가능해짐
// static final 붙어야하는데 인터페이스 안에 있는건 무조건 상수로 인식하기때문에 안붙여도 됨
// 상수
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
// 추상 메서드(abstract 생량)
void turnOn();
void turnOff();
void setVolume(int volume);
// 디폴트 메서드
default void setMute(boolean mute) {
if (mute) {
System.out.println("무음 처리합니다.");
}
else {
System.out.println("무음 해제합니다.");
}
}
// 정적 메서드
static void changeBattery() {
System.out.println("건전지를 교환합니다.");
}
}
_
로 연결[public] default 리턴타입 메소드명(매개변수, ...) {...}
public interface RemoteControl {
// 인터페이스의 멤버는 상수, 정적 멤버 메서드, 추상 메서드, 디폴트 메서드만 사용이 가능함
// 인터페이스에서 사용할 수 있는 멤버는 일반 멤버 변수와 메서드는 불가능하기 때문에 static 및 abstract 를 생략할 수 있음
// 인터페이스는 객체 사용 설명서이기 때문에 상수, 정적 메서드, 추상 메서드, 디폴트 메서드만 사용이 가능함
// 정적 메서드 및 디폴트 메서드는 자바 버전 8부터 사용이 가능해짐
// static final 붙어야하는데 인터페이스 안에 있는건 무조건 상수로 인식하기때문에 안붙여도 됨
// 상수
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
// 추상 메서드(abstract 생량)
void turnOn();
void turnOff();
void setVolume(int volume);
// 디폴트 메서드
default void setMute(boolean mute) {
if (mute) {
System.out.println("무음 처리합니다.");
}
else {
System.out.println("무음 해제합니다.");
}
}
// 정적 메서드
static void changeBattery() {
System.out.println("건전지를 교환합니다.");
}
}
Television.class
public class Television implements RemoteControl{ // 커서 올리고 Alt + Enter : 메소드 자동 구현
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 볼륨 : " + this.volume);
}
}
Audio.class
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 볼륨 : " + this.volume);
}
}
RemoteControlEx.class
public class RemoteControlEx {
public static void main(String[] args) {
// 인터페이스 타입의 객체를 생성이 불가능
// 인터페이스 타입의 변수는 생성이 가능
// 다형성에 의해서 인터페이스 타입의 변수에 자식 클래스 타입의 객체를 대입하는것이 가능함
RemoteControl rc;
rc = new Television();
rc.turnOn();
rc.setVolume(3);
rc.setVolume(15);
rc.turnOff();
rc = new Audio();
rc.turnOn();
rc.setVolume(3);
rc.setMute(true);
rc.setMute(false);
rc.setVolume(-5);
rc.turnOn();
}
}
RemoteControlEx.class - 추가
rc = new RemoteControl() {
private int volume;
@Override
public void turnOn() {
System.out.println("radio 전원을 켭니다.");
}
@Override
public void turnOff() {
System.out.println("radio 전원을 끕니다.");
}
@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("현대 rddio 볼륨 : " + this.volume);
}
}; // 세미콜론 붙여줘야함!
rc.turnOn();
rc.setVolume(50);
rc.setMute(true);
rc.turnOff();
// changeBattery()는 정적 메서드 이므로 인터페이스명.changeBattery()로 호출해야 함
// 정적멤버는 객체 만들어서 호출하는것 아님.
RemoteControl.changeBattery();
Searchable.interface
public interface Searchable {
void search(String url);
}
SmartTelevision.class
public class SmartTelevision implements RemoteControl, Searchable {
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 = MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 스마트TV 볼륨 : " + this.volume);
}
@Override
public void search(String url) {
System.out.println(url + "을 검색합니다.");
}
}
RemoteControlEx.class
public class RemoteControlEx {
public static void main(String[] args) {
// 인터페이스 타입의 객체를 생성이 불가능
// 인터페이스 타입의 변수는 생성이 가능
// 다형성에 의해서 인터페이스 타입의 변수에 자식 클래스 타입의 객체를 대입하는것이 가능함
RemoteControl rc;
rc = new Television();
rc.turnOn();
rc.setVolume(3);
rc.setVolume(15);
rc.turnOff();
rc = new Audio();
rc.turnOn();
rc.setVolume(3);
rc.setMute(true);
rc.setMute(false);
rc.setVolume(-5);
rc.turnOn();
rc = new RemoteControl() {
private int volume;
@Override
public void turnOn() {
System.out.println("radio 전원을 켭니다.");
}
@Override
public void turnOff() {
System.out.println("radio 전원을 끕니다.");
}
@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("현대 rddio 볼륨 : " + this.volume);
}
}; // 세미콜론 붙여줘야함!
rc.turnOn();
rc.setVolume(50);
rc.setMute(true);
rc.turnOff();
// changeBattery()는 정적 메서드 이므로 인터페이스명.changeBattery()로 호출해야 함
// 정적멤버는 객체 만들어서 호출하는것 아님.
RemoteControl.changeBattery();
ㅡㅡㅡㅡㅡ다중 인터페이스 구현 클래스 ㅡㅡㅡㅡㅡ
SmartTelevision stv = new SmartTelevision();
stv.turnOn();
stv.setVolume(30);
stv.setMute(true);
stv.setVolume(-50);
stv.search("kakaotalk");
stv.turnOff();
}
}
인터페이스명.정적메소드명();
Tirei.interface
public interface Tirei {
public void roll();
}
HankookTire2.class
public class HankookTire2 implements Tirei {
@Override
public void roll() {
System.out.println("한국 타이어가 굴러갑니다.");
}
}
KumhoTire2.class
public class KumhoTire2 implements Tirei {
@Override
public void roll() {
System.out.println("금호 타이어가 굴러갑니다.");
}
}
Car4.class
public class Car4 {
Tirei frontLeftTire = new HankookTire2();
Tirei frontRightTire = new HankookTire2();
Tirei backLeftTire = new HankookTire2();
Tirei backRightTire = new HankookTire2();
void run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
}
Car4Ex.class
public class Car4Ex {
public static void main(String[] args) {
Car4 myCar = new Car4();
myCar.run();
myCar.frontLeftTire = new KumhoTire2();
myCar.frontRightTire = new KumhoTire2();
myCar.run();
}
}
Car5.class
public class Car5 {
Tirei[] tires = new Tirei[4];
// 방법 1 : 배열 선언과 동시에..
// Tirei[] tires = {
// new HankookTire2(),
// new HankookTire2(),
// new HankookTire2(),
// new HankookTire2(),
// }
// 방법 2 : 생성자에서 for문 사용
public Car5() {
for (int i=0; i < tires.length; i++) {
tires[i] = new HankookTire2();
}
}
void run() {
for (Tirei item : tires) {
item.roll();
}
}
}
Car4Ex.class (추가)
import package2.C;
public class Car4Ex {
public static void main(String[] args) {
Car4 myCar = new Car4();
myCar.run();
myCar.frontLeftTire = new KumhoTire2();
myCar.frontRightTire = new KumhoTire2();
myCar.run();
/////////// 추가 ///////////
Car5 car5 = new Car5();
car5.run();
car5.tires[2] = new KumhoTire2();
car5.tires[3] = new KumhoTire2();
car5.run();
}
}
Driver2.class
public class Driver2 {
public void drive(Vehicle2 vehicle2) {
vehicle2.run();
}
}
Vehicle2.interface
public interface Vehicle2 {
void run(); // 인터페이스에서는 public 생략 가능
}
Driver2Ex.class
import package1.B;
import package2.D;
public class Driver2Ex {
public static void main(String[] args) {
System.out.println("각각 객체를 생성하여 실행");
Bus2 bus2 = new Bus2();
Taxi2 taxi2 = new Taxi2();
bus2.run();
taxi2.run();
System.out.println("\n익명 객체를 통한 구현");
// 오류 : 인터페이스타입은 객체생성 불가능
// Vehicle2 vehicle2 = new Vehicle2();
// 익명객체 생성해서 사용하면 됨
Vehicle2 vehicle2 = new Vehicle2() {
@Override
public void run() {
System.out.println("탈것이 달립니다.");
}
}; // 익명객체 생성시 중괄호 끝에 세미콜론!
vehicle2.run();
System.out.println("\n부모 인터페이스 타입의 변수에 자식 객체를 대입");
vehicle2 = bus2;
vehicle2.run();
vehicle2 = taxi2;
vehicle2.run();
System.out.println("\n인터페이스 변수를 매개변수로 사용 시");
Driver2 driver2 = new Driver2();
driver2.drive(bus2);
driver2.drive(taxi2);
System.out.println("\n자동 타입변환, 강제 타입변환, instanceof\n");
/////// 자동 타입 변환, 부모인 Vehicle2 인터페이스 타입의 변수에 자식인 Bus2클래스 타입의 객체를 대입 ///////
Vehicle2 vehicle21 = new Bus2();
vehicle21.run();
// 오류 발생 : 데이터 타입이 다르므로 강제 타입변환을 시도해야 함
// Bus2 bus21 = vehicle21;
Bus2 bus21 = (Bus2) vehicle21; // 강제 타입 변환
bus21.run();
// 강제 타입 변환의 조건 : 원본이 자식 클래스 타입의 객체일때 해당 클래스 타입으로 변환 시 가능 -> instanceof 로 확인가능
// 원본 클래스 객체 타입이 맞는지 확인을 하기 위해서는 instanceof 연산자를 사용해야 함
Vehicle2 vehicle22 = new Bus2();
vehicle22.run();
// 런타임 에러 발생
// Taxi2 taxi22 = (Taxi2) vehicle22;
// taxi22.run();
if (vehicle22 instanceof Taxi2) {
Taxi2 taxi22 = (Taxi2) vehicle22;
taxi22.run();
System.out.println("정상적으로 Taxi2 클래스 타입으로 변경 했습니다.");
}
else {
System.out.println("Taxi2 클래스 타입으로 변경할 수 없습니다.");
}
}
}
하위 인터페이스 구현 클래스는 아래 추상 메소드를 모두 재정의해야
인터페이스 자동 타입 변환
인터페이스는 인터페이스끼리 다중 상속이 가능하다.
InterfaceA, InterfaceB, InterfaceC
public interface InterfaceA {
public void methodA();
}
public interface InterfaceB {
public void methodB();
}
public interface InterfaceC extends InterfaceA, InterfaceB{
public void methodC();
}
ImplC.class
public class ImplC implements InterfaceC {
@Override
public void methodA() {
System.out.println("구현 클래스 C - 메서드A 실행");
}
@Override
public void methodB() {
System.out.println("구현 클래스 C - 메서드B 실행");
}
@Override
public void methodC() {
System.out.println("구현 클래스 C - 메서드C 실행");
}
}
ImplEx.class
public class ImplEx {
public static void main(String[] args) {
System.out.println("구현체 implC의 객체 impl은 모든 메서드 사용 가능");
ImplC impl = new ImplC();
impl.methodA();
impl.methodB();
impl.methodC();
System.out.println("\nInterfaceA의 변수에 대입 시 ");
InterfaceA ifA = impl;
ifA.methodA(); // 메서드A 만 사용 가능
System.out.println("\nInterfaceB의 변수에 대입 시 ");
InterfaceB ifB = impl;
ifB.methodB(); // 메서드B 만 사용 가능
System.out.println("\nInterfaceC의 변수에 대입 시 ");
InterfaceC ifC = impl;
impl.methodA();
impl.methodB();
impl.methodC(); // 메서드 A, B, C 모두 사용 가능
}
}
MyInterface.interface
public interface MyInterface {
public void method1();
// 해당 interface 를 구현하는 구현체가 여러 개가 존재할 경우에 구현체의 수정 없이 기능을 추가하고자 하는 경우에 사용하는 것이 디폴트 메서드임
default void method2() {
System.out.println("dMyInterface의 method2() 실행");
}
}
MyClassA.class
public class MyClassA implements MyInterface{
@Override
public void method1() {
System.out.println("MyClassA의 method1() 실행");
}
// 메서드2번 오버라이드 안해도 됨(default메서드라서), 해도 상관X
/*@Override
public void method2() {
System.out.println("MyClassA의 method2() 실행");
}*/
}
MyclassB.class
public class MyClassB implements MyInterface{
@Override
public void method1() {
System.out.println("MyClassB의 method1() 실행");
}
@Override
public void method2() {
System.out.println("MyClassB의 method2() 실행");
}
}
DefaultMethodEx.class
public class DefaultMethodEx {
public static void main(String[] args) {
MyInterface m1 = new MyClassA();
m1.method1(); // 추상 메서드 : 오버라이딩
m1.method2(); // 디폴트 메서드
MyInterface m2 = new MyClassB();
m2.method1(); // 추상 메서드 : 오버라이딩
m2.method2(); // 추상 메서드 : 오버라이딩
}
}
(MyInterface, MyClassA, MyClassB)
MyInterface2.interface
public interface MyInterface2 extends MyInterface{// 인터페이스끼리 상속은 extends 사용
// 부모 인터페이스인 MyInterface에서 상속받은 멤버 메서드 중 디폴트 메서드인 method2() 를 오버라이딩하여 추상 메서드로 변환
@Override
void method2();
void method3(); // MyInterface2 전용 메서드3
}
MyClassC.class
public class MyClassC implements MyInterface2{
// MyInterface 에서 상속해준 추상 메서드
@Override
public void method1() {
System.out.println("MyClassC의 method1() 실행");
}
// MyInterface 에서 디폴트 메서드로 상속해줬으나 MyInterface2에서 추상 메서드로 오버라이딩한 메서드
// MyInterface2를 구현하는 구현체는 반드시 해당 메서드를 구현해야 함.
@Override
public void method2() {
System.out.println("MyClassC의 method2() 실행");
}
// MyInterface 에서 전용으로 생성한 추상메서드
@Override
public void method3() {
System.out.println("MyClassC의 method2() 실행");
}
}