인터페이스란 기능을 사용하는 방법을 명시하는 것으로,
자바의 다형성을 극대화하여 개발코드 수정을 줄이고 프로그램 유지 보수성을 높이기 위해 인터페이스를 사용한다.
🔎 인터페이스 정의하기
interface 키워드를 통해 선언 할 수 있다.interface Printable {
public void print(String doc); // 추상 메소드
}
🔎 인터페이스를 구현하는 클래스
implements 키워드를 통해 일반 클래스에서 인터페이스를 구현할 수 있다.class Printer implements Printalble {
public void print(String doc) { // Printable 인터페이스의 print 메소드 구현
system.out.println(doc);
}
}
🔎 인터페이스 특징
인터페이스 대상으로는 인스턴스 생성이 불가능하다.
인터페이스형 참조변수 선언이 가능하다.
➡ 인터페이스를 직접 혹은 간접적으로 구현하는 모든 클래스의 인스턴스를 참조할 수 있다.
구현하는 메소드와 추상 메소드 사이에도 메소드 오버라이딩 관계가 성립한다.
➡ 어노테이션 @Override 선언이 가능
interface Printable {
public void print(String doc); // 추상 메소드
}
class Printer implements Printable { // Printable을 구현하는 Printer 클래스
@Override
public void print(String doc) { // 오버라이딩 관계 성립
System.out.println(doc);
}
}
class InterfaceExam {
public static void main(String[] args) {
Printable p = new Printer(); // 인터페이스(Printable)형 참조변수 선언 가능
p.print("Hello Java"); // 오버라이딩 한 메소드가 호출됨
}
}
// 출력 결과
Hello Java
🔎 관련 예제 (프린터드라이버)
마이크로소프트의 윈도우에서 삼성과 LG의 프린터를 대상으로 출력이 가능할 때, 마이크로소프트에서 인터페이스를 만들어 모든 프린터 업체에게 제공을 한다.
프린터 업체들은 제공된 인터페이스를 가지고 자사의 프린터를 조작하는 메소드를 구현하여 클래스를 정의하고 제공한다.
그럼 마이크로소프트는 클래스 이름만 알면 내부적으로 구현이 어떻게 이뤄지는지 몰라도 해당 업체의 프린터로 출력할 수 있다.
이것이 바로 인터페이스를 두는 이유이다 !
interface Printable2 { // MS가 정의하고 제공한 인터페이스
public void print(String doc); // 추상 메소드
}
class LPrinterDriver implements Printable { // L사의 프린터 사용에 필요한 클래스 정의
@Override
public void print(String doc) {
System.out.println("Form LG printer");
System.out.println(doc);
}
}
class SPrinterDriver implements Printable { // S사의 프린터 사용에 필요한 클래스 정의
@Override
public void print(String doc) {
System.out.println("From Samsung printer");
System.out.println(doc);
}
}
class PrinterDriver {
public static void main(String[] args) {
String myDoc = "This is a report about...";
// 삼성 프린터로 출력
Printable p = new SPrinterDriver();
p.print(myDoc);
System.out.println(); // 단순 개행
// LG 프린터로 출력
p = new LPrinterDriver();
p.print(myDoc);
}
}
// 출력 결과
From Samsung printer
This is a report about...
Form LG printer
This is a report about...
instanceof 연산을 할 수 있다.🔎 인터페이스의 메소드
public이 선언된 것으로 간주한다. ( 생략 가능 )🔎 인터페이스의 변수
public, static, final이 선언된 것으로 간주한다. ( 생략 가능 )🔎 인터페이스를 구현하는 클래스
인터페이스에 새로운 기능을 위한 추상메소드가 추가될 경우(예를 들어 위 예제에서 컬러 프린터가 생기는 경우),
인터페이스의 모든 추상 메소드는 이를 구현하는 클래스에서 모두 구현해야 하므로 기존에 개발된 드라이버를 모두 수정해야할 것이다.
이러한 상황을 고려해 자바에서는 인터페이스의 상속을 지원한다.
🔎 인터페이스 상속 방법
extends로 명시한다.implements로 명시한다.interface Printable { // MS사가 제공한 인터페이스
void print(String doc); // 흑백 출력을 위한 추상 메소드
}
/* Printable를 상속하는 인터페이스 */
interface ColorPrintable extends Printable {
void printColor(String doc); // 컬러 출력을 위한 추상 메소드
}
// L사의 컬러 프린터 드라이버
class LPrinterColorDriver implements ColorPrintable {
@Override
public void print(String doc) { // 흑백 출력 메소드
System.out.println("From LG black & white print");
System.out.println(doc);
}
@Override
public void printColor(String doc) { // 컬러 출력 메소드
System.out.println("From LG color print");
System.out.println(doc);
}
}
public class PrinterDriver {
public static void main(String[] args) {
String myDoc = "This is a report about...";
ColorPrintable p = new LPrinterColorDriver();
p.print(myDoc); // 흑백 출력
System.out.println(); // 단순 개행
p.printColor(myDoc); // 컬러 출력
}
}
// 출력 결과
From black & white print
This is a report about...
From color print
This is a report about...
디폴트 메소드는 인터페이스에 추상 메소드를 추가해야 하는 상황에서 이전에 개발해 놓은 코드에 영향을 미치지 않기 위해 등장한 문법이다.
기능 보강을 위해 모든 인터페이스에 최소 한개 이상의 추상 메소드를 추가해야 하는 경우, 인터페이스의 상속으로 해결한다면 인터페이스의 수는 두배로 늘어나게 될 것이다.
이렇게 인터페이스의 수가 늘어가는 것은 프로그램 개발에 불편을 초래하는 일이다.
이러한 상황의 해결을 위해 인터페이스의 디폴트 메소드를 지원한다.
🔎 디폴드 메소드의 선언과 특징
default 키워드를 통해 선언 할 수 있다.interface Printable {
void print(String doc);
default void printColor(String doc) {} // 인터페이스의 디폴트 메소드
}
class LPrinterDrv implements Printable { // L사 프린터 드라이브 (흑백 출력만 가
@Override
public void print(String doc) {
System.out.println("From LG black & white printer");
System.out.println(doc);
}
}
class SPrinterDrv implements Printable { // S사 프린터 드라이브 (흑백, 컬러 출력 모두 가능)
@Override
public void print(String doc) {
System.out.println("From Samsung black & white printer");
System.out.println(doc);
}
@Override
public void printColor(String doc) {
System.out.println("From Samsung color printer");
System.out.println(doc);
}
}
class PrinterDriver {
public static void main(String[] args) {
String myDoc = "This is a report about...";
Printable p1 = new LPrinterDrv();
p1.print(myDoc);
System.out.println();
Printable p2 = new SPrinterDrv();
p2.print(myDoc);
System.out.println();
p2.printColor(myDoc);
}
}
// 출력 결과
From LG black & white printer
This is a report about...
From Samsung black & white printer
This is a report about...
From Samsung color printer
This is a report about...
static 메소드 (클래스 메소드)static 메소드를 사용할 수 있다.interface Printable {
static void printLine(String str) { // static 메소드
System.out.println(str);
}
default void print(String doc) {
printLine(doc); // 인터페이스의 static 메소드 호출
}
}
// 인터페이스 Printale4에는 구현해야 할 메소드가 존재 하지 않음
class Printer implements Printable { }
class InterfaceStaticExam {
public static void main(String[] args) {
String myDoc = "This is a report about...";
Printable p = new Printer();
p.print(myDoc);
Printable4.printLine("end fo line"); // 인터페이스의 static 메소드 직접 호출
}
}
// 결과 출력
This is a report about...
end fo line
instanceof 연산true를 반환한다.interface Printable {
void printLine(String str);
}
class SimplePrinter implements Printable { // Printable를 직접 구현
@Override
public void printLine(String str) {
System.out.println(str);
}
}
class MultiPrinter extends SimplePrinter { // SimplePrinter를 상속함으로써 Printable4를 간접 구현함
@Override
public void printLine(String str) {
super.printLine("start of multi...");
super.printLine(str);
super.printLine("end of multi...");
}
}
public class InterfaceInstanceofExam {
public static void main(String[] args) {
Printable p1 = new SimplePrinter();
Printable p2 = new MultiPrinter();
if (p1 instanceof Printable)
p1.printLine("This is a simple printer");
System.out.println();
if (p2 instanceof Printable)
p2.printLine("This is a muliful printer");
}
}
// 출력 결과
This is a simple printer
start of multi...
This is a muliful printer
end of multi...
Marker 인터페이스interface Upper{} // 마커 인터페이스
interface Lower{} // 마커 인터페이스
interface Printable {
String getContents();
}
class Report implements Printable, Upper {
String cons;
Report(String cons) { // 생성자
this.cons = cons;
}
@Override
public String getContents() {
return cons;
}
}
class Printer {
public void printContents(Printable doc) {
if(doc instanceof Upper) // doc 참조인스턴스가 Upper를 구현한다면
System.out.println((doc.getContents().toUpperCase())); // 문자열의 모든 문자를 대문자로 바꿈
else if(doc instanceof Lower) // doc 참조인스턴스가 Lower를 구현한다면
System.out.println((doc.getContents().toLowerCase())); // 문자열의 모든 문자를 소문자로 바꿈
else
System.out.println(doc.getContents());
}
}
class MarkerInterfaceExam {
public static void main(String[] args) {
Printer p = new Printer();
Report doc = new Report("Simple Funny News~");
p.printContents(doc);
}
}
// 출력 결과
SIMPLE FUNNY NEWS~ // doc 참조 인스턴스가 Upper를 구현하므로 대문자로 출력됨
(Abstract class)abstract 선언을 추가해 추상클래스를 정의한다.public abstract class House {
public void methodOne() {
System.out.println("method one");
}
public abstract void methodTwo(); // 추상 메소드
}
🔎 추상 클래스의 특징