클래스가 아니므로 선언 시 class 키워드 사용 X
interface 키워드 사용하여 정의
구현코드가 없음
상수와 추상메서드만 가질 수 있음
-> 모든 멤버 변수는 public static final
(생략 가능)이 붙은 상수로 취급
-> 모든 메서드는 public abstract
(생략 가능)가 붙은 추상 메서드로 취급
추상메서드를 포함하므로 객체(인스턴스) 생성 X
-> 참조변수 타입으로는 사용 가능
extends
대신 implements
키워드 사용< 인터페이스 정의 기본 문법 >
[접근제한자] interface 인터페이스명 {
[상수...
추상메서드...]
}
< 인터페이스를 상속받은 서브클래스 정의 문법 >
[접근제한자] class 클래스명 implements 인터페이스명 {
}
public static void main(String[] args) {
// MyInterface mi = new MyInterface();
// MyInterface.NUM1 = 99;
// 상수는 값 변경할 수 없음
// 인터페이스는 인스턴스(객체) 생성 불가능
Sub s = new Sub();
s.method1();
s.method2();
s.NUM1 = 30;
// 상수는 값 변경할 수 없음
MyInterface mi = new Sub();
// 참조변수 타입으로 사용 가능
mi.method1();
mi.method2();
// 상속된 메서드만 호출 가능함
System.out.println(mi.NUM1);
// 상수를 인터페이스 안에서 선언했기 때문에
// 업캐스팅 했어도 접근 가능함
// 하지만!!!! 상수에 접근할 때는 변수로 접근하는 것보다
System.out.println(MyInteface.NUM1);
// 클래스 또는 인터페이스로 접근하는 게 더 좋음!!!!
}
interface MyInterface {
public static final int NUM1 = 10;
// 인터페이스에서 정의되는 멤버변수는 모두 상수!!
// 값 변경 불가능함
int NUM2 = 20;
// 다 상수이기 때문에
// 앞에 public static final을 생략해도
// 알아서 상수로 저장됨
// public MyInterface() {}
// 인터페이스는 생성자 생성 불가능
public abstract void method();
// 인터페이스에서 만들어지는 메서드는 모두 추상메서드!
// 일반메서드는 정의 불가능함
void method2();
// public abstract는 생략 가능함
}
class Sub implements MyInterface {
// 인터페이스를 상속받는 서브클래스 정의
@Override
public void method1() {
// 추상메서드 구현해야함
}
public void method2() {
// 아까 위에 인터페이스에서는 public을 생략했는데
// 인터페이스 내 모든 메서드의 접근제한자는 public이므로
// 오버라이딩을 통해 구현하는 메서드는
// public 타입만 가능하다!
// 그래서 public 꼭 적어줘야 함!!!!
// 안그러면 오류남...
}
}
다이아몬드 상속 = 다중 상속
인터페이스를 상속받는 서브클래스에서 다중 상속 가능
-> implements
키워드 뒤 복수 개의 인터페이스 지정 가능
인터페이스끼리 상속받을 경우 extends
로 상속
-> 추상메서드는 구현X
다중 상속을 허용했을 경우 다른 클래스 두 개를 동시에 상속 받는 서브클래스에서 공통적으로 갖는 메서드를 호출했을 때 어떤 클래스의 메서드를 호출했는지 분명하게 알 수 없는 문제가 발생하기 때문에 자바에서 다중 상속은 금지되어 있음
추상메서드를 이용한 다중상속(불가)
abstract class Animal {
public abstract void breeding();
}
class Whale extends Animal {
@Override
public void breeding() {
System.out.println("새끼를 낳아 번식!");
}
}
class Shark extends Animal {
@Override
public void breeding() {
System.out.println("알을 낳아 번식!");
}
}
class Whaleshark extends Whale, Shark {
Whaleshark.breeding();
//는 Whale의 메서드인지 Shark의 메서드인지 분명하지 않음
}
인터페이스를 이용한 다중 상속(가능)
interface Animal {
public abstract void breeding();
// 인터페이스 내의 모든 메서드는 추상메서드임
// 서브클래스에서 구현 필수
}
interface Swim {
}
interface Whale extends Animal, Swim {
// 인터페이스 간의 상속은 extends 사용함
}
interface Shark extends Animal, Swim {
}
class Whaleshark implements Whale, Shark {
// 인터페이스를 상속받은 서브클래스에서 추상메서드 구현
@Override
public void breeding() {
System.out.prinln("알을 낳아 번식");
}
}
인터페이스 내에서는 모든 메서드가 추상메서드이므로 중복되는 메서드에 대한 구별 없이 직접 구현하면 되기 때문에 중복된 메서드로 인한 혼란이 없음
-> 다이아몬드 상속에서의 문제점이 사라짐
구현의 강제로 코드의 통일성 향상 (= 표준화)
인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체 용이
-> 특정 클래스를 직접 다루는 대신 부모 인터페이스 타입으로 클래스를 다루게 되면 실제 인스턴스가 바뀌더라도 기존 코드를 수정할 필요가 없어짐
서로 상속 관계가 없는 클래스간의 인터페이스를 통한 상속 관계 부여
-> 다형성 확장
모듈간 독립적 프로그래밍으로 인한 개발 기간 단축
인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체 용이
-> 인터페이스 사용 시 손쉬운 모듈 교체 지원
public static void main(String[] args) {
PrintClient p = new PrintClient();
// 인스턴스 생성
p.setPrintClient(new LaserPrinter);
// LaserPrinter를 Printer로 업캐스팅
// 원래 파라미터는 Printer 타입이었기 때문에
// set메서드 호출해서 프린터기 인스턴스를
// 파라미터로 전달할 경우 업캐스팅이 일어남
p.print("Hello, World");
p.setPrintClient(new DotPrinter);
// LaserPrinter를 DotPrinter로 교체
p.print("Hello, Java");
}
}
class PrintClient {
private Printer printer;
// Printer 인터페이스 타입의 변수 선언
public void setPrintClient(Printer printer) {
this.printer = printer;
}
// Setter 메서드를 통해 Printer 타입 변수 초기화
public void print(String fileName) {
printer.print(fileName);
// Printer 타입 변수에 저장된 프린터의 인스턴스 사용해서
// print() 메서드 호출 가능
// PrinterClient의 멤버변수이기도 하지만
// interface Printer의 참조변수이기도 하니까!
}
}
interface Printer {
// 제일 먼저 정의해야 함
public abstract void print(String fileName);
}
class LaserPrinter implements Printer {
@Override
public void print(String fileName) {
System.out.println("LaserPrinter로 " + fileName + " 출력하기!");
}
}
class DotPrinter implements Printer {
@Override
public void print(String fileName) {
System.out.println("DotPrinter로 " + fileName + " 출력하기!");
}
}
서로 상속 관계가 없는 클래스간의 인터페이스를 통한 상속 관계 부여
public static void main(String[] args) {
Chargeable c = new NoteBookPc();
// 참조변수 형태로 접근
c.charge();
Chargeable c2 = new SmartPhone();
c2.charge();
// 바로 업캐스팅 진행해서 접근 가능
Chargeable[] chargeArr = {
new NoteBookPc(),
new SmartPhone()
};
// Chargeable 타입 배열로도 관리 가능
// 자동 업캐스팅
for(int i = 0; i < chargeArr.length; i++) {
chargeArr[i].charge();
}
}
interface Chargeable {
public abstract void charge();
}
class Pc {}
class HandPhone{}
class NoteBookPc extends Pc implements Chargeable {
@Override
public void charge() {
System.out.println("노트북 충전 중");
}
}
class SmartPhone extends HandPhone implements Chargeable {
@Override
public void charge() {
System.out.println("스마트폰 충전 중");
}
}