: 객체의 사용 방법을 정의한 타입
: 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할
-왜 직접 객체의 메소드를 호출하지 않고, 중간에 인터페이스를 두는 걸까?
: 개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있도록 하기 위해서
: 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다
-> 개발 코드 측면에서는 코드 변경 없이 실행 내용과 리턴값을 다양화할 수 있는 장점
static final double PI = 3.141592;
static final int NUMBER2021 = 210;
static final String SCHOOL_NAME = "AB";
// static final 표기 생략
int A = 10;
doubld B = 3.0;
abstract void method();
abstract int method1(double a, int b);
abstract boolean method2();
abstract void abMethod();
// abstract 표기 생략
String method3();
default void soundMethod() {
System.out.println("인터페이스의 default 메소드임");
}
default void bMethod() {
}
public static void sMethod() {
}
static void cMethod() {
}
가능하다
public interface InterEx01 { }
public interface InterEx02 extends InterEx01 { }
다중 상속도 가능
public interface InterEx01 { }
public interface InterEx02 { }
public interface InterEx03 extends InterEx01, InterEx02 { }
일반 클래스와는 상속(extends)가 아니라 연결(implements)이라 한다
다만 이건 단일 연결만 가능
public interface InterEx01 { }
public interface InterEx02 { }
// 가능
public class InterfaceEx implements InterEx01 { }
// 불가능
public class InterfaceEx implements InterEx01, InterEx02 { }
public class InterfaceEx implements InterEx01 implements InterEx02 { }
상속과 연결을 같이 쓰는것도 가능하다
public class InterfaceEx extends Unit implements InterEx01, InterEx02 { }
System.out.println(InterEx.PI);
System.out.println(InterEx.NUMBER2021);
System.out.println(InterEx.SCHOOLNAME);
System.out.println(InterEx02.A);
InterEx.sMethod();
InterEx02.cMethod();
ClassInterEx ci = new ClassInterEx();
ci.abMethod();
ci.method();
ci.soundMethod();
-상수필드, 추상메소드, 디폴드 메소드, 정정메소드 모두 사용
-Object클래스의 toString() 메소드도 사용
public interface RemoteControl {
// 상수 필드
// 상수명은 대문자로 작성하되
// 서로 다른 단어로 구성되어 있을 경우 언더바로 연결하는 것이 관례
// public, static, final을 생략하더라도 자동적으로 컴파틸 과정에서 붙음
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
// 추상 메소드
// public abstract를 작성해주지 않아도 컴파일 과정에서 자동으로 붙는다
public void turnOn();
public void turnOff();
public void setVolume(int volume);
// 디폴드 메소드
default void setMute(boolean mute) {
// true면 if 실행, flase면 else 실행
if(mute) {
System.out.println(toString() + " 무음 처리합니다");
} else {
System.out.println(toString() + " 무음 해제합니다");
}
}
@Override
String toString();
// 정적 메소드
static void changeBattery() {
System.out.println("건전지를 교환합니다");
}
}
interface 클래스를 받아와 구현하는 클래스이기도 하기 때문에 구현 클래스라 하기도 한다.
public class Tv implements RemoteControl {
// 필드
private int volume;
@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);
if (volume > 8) {
System.out.println("청력을 위해 볼륨을 줄이세요");
}
}
@Override
public void turnOff() {
System.out.println(toString() + "를 끕니다");
}
@Override
public void turnOn() {
System.out.println(toString() + "을 켭니다");
}
@Override
public String toString() {
return "TV";
}
}
public class Lamp implements RemoteControl{
@Override
public void setVolume(int volume) {
}
@Override
public void turnOn() {
System.out.println(toString() + "를 끕니다");
}
@Override
public void turnOff() {
System.out.println(toString() + "을 켭니다");
}
@Override
public String toString() {
return "형광등";
}
// default 메소드도 받아와 재정의 후 사용하는 것 가능하다
// 이 경우 main에서 호출시
// Interface의 것이 아닌 재정의된 default 메소드가 출력된다
@Override
public void setMute(boolean mute) {
if(mute) {
System.out.println(toString() + "을 새로 갈았습니다");
} else {
System.out.println(toString() + "이 오래 되었습니다");
}
}
}
public class Audio {
public static void main(String[] args) {
// 상수 사용하기 -> 인터페이스명.상수명
// 맥스볼륨 출력하기
System.out.println(RemoteControl.MAX_VOLUME);
// 민 볼륨에 5 더해서 출력하기
int num = RemoteControl.MIN_VOLUME + 5;
System.out.println(num);
System.out.println("----------------------------");
// 추상메소드 사용하기
// 일반 클래스에 연결 후 오버라이딩해 출력 가능
Tv tv = new Tv();
tv.turnOn();
tv.setVolume(50);
tv.turnOff();
Lamp lamp = new Lamp();
lamp.turnOn();
lamp.setMute(true);
lamp.turnOff();
System.out.println("----------------------------");
// 디폴드 메소드 사용하기
tv.setMute(true);
lamp.setMute(true);
// 정적 메소드 사용하기
// static이라 다른 곳에서 오버라이딩 불가
// 객체 생성된 것으로 불러오기 안됨
// 클래스명.메소드명 으로 호출가능
RemoteControl.changeBattery();
// tv.changeBattery(); <- 안됨
}
}
실행모습
아래 그림과 같은 관계를 구현해보자
// 부모 클래스
public class Unit {
}
public interface Movable {
// 추상 메소드 하나 선언해줌
void move();
}
public interface Attackable {
// 추상 메소드 하나 선언해줌
void attack();
}
// 인터페이스끼리는 extends 를 사용해 연결 -> 다중연결도 가능
// 일반클래스와 인터페이스는 implements 사용해 연결 -> 단일 연결만 가능
public interface Fightable extends Movable, Attackable {
void fight();
}
// 클래스의 상속도 받고
// 클래스에 인터페이스도 연결하겠다면
// 섞어 사용하는 거 가능
public class Fighter extends Unit implements Fightable, Inter2 {
// 추상메소드를 가지고 있는 인터페이스
//클래스와 연결할 시 에러를 수정하려면
// 추상메소드를 오버라이딩 해야한다
@Override
public void move() { }
@Override
public void fight() { }
@Override
public void attack() { }
@Override
public void in1() { }
@Override
public void in2() { }
}
public class FighterMain {
public static void main(String[] args) {
Fighter f = new Fighter();
// instanceof
// : 앞에 있는 객체로 뒤에있는 객체를 가리킬 수 있습니까
// : 앞에있는 객체는 뒤에있는 객체의 자손입니까
if (f instanceof Unit) {
System.out.println("f는 Unit 클래스의 하위입니다");
}
if (f instanceof Fightable) {
System.out.println("f는 fightable 클래스의 하위입니다");
}
}
}
실행결과
-다형성
: Animal a = new Cat();
: 하위 클래스의 인스턴스를 상위 타입의 참조변수로 참조하는 것이 가능한 것
-인터페이스 역시 이를 구현한 상위 타입의 클래스라 할 수 있기에
해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며 인터페이스 타입으로의 형변환도 가능
: 인터페이스명 참조변수 = new 구현클래스();
-인터페이스의 장점
: 개발시간을 단축시킬 수 있다.
: 표준화가 가능하다
: 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다
: 독립적인 프로그래밍이 가능하다
public interface Fightable {
}
public class Fighter extends Unit implements Fightable {
}
class Unit{
}
public class MainClass {
public static void main(String[] args) {
// 다형성
// 부모가 더 큰 타입이라 자동 형변환 된 상황
// 부모타입 변수 = new 자식타입();
// 인터페이스 변수 = new 자식클래스();
Fightable f = new Fighter();
// 자식타입 변수 = 부모타입변수
// 작은타입에 큰 타입을 넣어야하니 다운캐스팅(강제 형변환) 필요
Fighter fi = (Fighter) f;
}
}
-Parserable인터페이스
: 구문분석을 수행하는 기능을 구현할 목적으로 추상메서드'parse(String fileName)을 정의
-XMLParser 구현 클래스
-HTMLParser구현 클래스
ParserManager클래스의 getParser메서드
: 매개변수로 넘겨 받는 type의 값에 따라 XMLParser인스턴스 또는 HTMLParser인스턴스를 반환
public interface Parseable {
// 구문 분석 작업을 수행
// 추상 메소드 선언
public abstract void parse(String fileName);
}
public class XMLParser implements Parseable {
@Override
public void parse(String fileName) {
// 구문 분석작업을 수행하는 코드
System.out.println(fileName + "- XML parsing completed");
}
}
class HTMLParser implements Parseable {
@Override
public void parse(String fileName) {
// 구문 분석작업을 수행하는 코드
System.out.println(fileName + "- HTML parsing completed");
}
}
public class ParserManager {
// 리턴타입이 Parseable인 인터페이스
// getParser의 리턴타입은 Parseable이니 메인에서 Parseable로 받음
// Parseable parser = ParserManager.getParser("XML");
public static Parseable getParser(String type) {
if (type.equals("XML")) {
// 새롭게 XML파서를 생성해라
// 힙메모리에 올려라
return new XMLParser();
} else {
Parseable p = new HTMLParser();
return p;
}
}
public static void main(String[] args) {
// Parseable 의 parser에
// 파서매니저
Parseable parser = ParserManager.getParser("XML");
parser.parse("document.xml");
parser = ParserManager.getParser("HTML");
parser.parse("document2.html");
}
}
실행결과