추상 메서드의 집합으로 그 외의 상수, static 메서드, default 메서드로 구성할 수 있다
구현된 것이 전혀 없는 설계도로 모든 멤버가 public이다
interface PlayingCard {
// 상수
public static final int SPADE = 4;
final int DIAMOND = 3;
static int HEART = 2;
int CLOVER = 1; // 항상 예외없이 public static final이 붙으며 생략 가능
// 추상 메서드
public abstract String getCardNumber();
String getCardKind(); // public abstract가 생략되어 있음
}
추상 클래스 : 추상 메서드를 가진 일반 클래스
인터페이스 : 추상 메서드의 집합으로 다중 상속이 가능하며 iv를 가질 수 없다
추상 클래스는 상속을 통해 추상 메서드를 강제적으로 구현하는 것이 목적이라면
인터페이스는 상속이 아닌 규칙을 정함으로써 동일한 동작을 구현하는 것이 차이이다
interface Movable { void move(int x, int y); }
interface Attackable { void attack(Unit u); }
interface Fightable extends Movable, Attackable {} // 다중 상속, 멤버 2개
인터페이스에 정의된 추상 메서드를 완성하는 것
class Fighter implements Fightable {
public void move(int x, int y) {}
public void attack(Unit u) {}
}
implements
키워드로 인터페이스를 구현한다abstract
키워드를 붙여 추상 클래스임을 명시해야 한다// 상속을 받으며 구현이 가능 (다중 상속)
class Fighter extends Unit implements Fightable {
public void move(int x, int y) {}
// 인터페이스 타입의 매개변수
public void attack(Fightable f) {}
// 인터페이스 리턴타입
Fightable getFightable() {
Fighter f = new Fighter();
return f;
}
// 인터페이스 타입의 참조변수
Unit u = new Fighter(); // Unit이 부모 객체라서 가능
Fightable f = (Fightable) new Fighter();
Fightable f2 = new Fighter();
Fightable f = (Fightable) new Fighter(); // 인터페이스 타입으로 형변환
Fightable f2 = new Fighter(); // Fightable를 구현한 Fighter를 참조
public void attack(Fightable f) {}
Fightable getFightable() {
Fighter f = new Fighter();
return f;
}
interface Parseable {
// 구문 분석작업을 수행한다
public abstract void parse(String fileName);
}
class ParserManager {
// 인터페이스 리턴타입
public static Parseable getParser(String type) {
if(type.equals("XML")) {
return new XMLParser();
} else {
Parseable p = new HTMLParser();
return p;
// return new HTMLParser();
}
}
}
// 인터페이스 구현
class XMLParser implements Parseable {
public void parse(String fileName) {
System.out.println(fileName + "- XML parsing completed.");
}
}
class HTMLParser implements Parseable {
public void parse(String fileName) {
System.out.println(fileName + "-HTML parsing completed.");
}
}
class ParserTest {
public static void main(String args[]) {
Parseable parser = ParserManager.getParser("XML");
parser.parse("document.xml");
parser = ParserManager.getParser("HTML");
parser.parse("document2.html");
}
}
class A {
public void method(B b) { // B 객체만 받을 수 있다
b.method();
}
}
class A2 {
public void method(I i) { // I를 구현한 객체를 받을 수 있다
i.method();
}
}
interface I {
abstract public void method();
}
class B implements I {
public void method() {
System.out.println("B method");
}
}
class C implements I {
public void method() {
System.out.println("C method");
}
}
A a = new A();
a.method(new B());
// a.method(new C()); // C 객체를 받으려면 A 클래스를 수정해야 함
A2 a2 = new A2();
a2.method(new B());
a2.method(new C()); // C 객체 또한 I를 구현했기 때문에 수정할 필요가 없음
JDK1.8부터는 default 메서드와 static 메서드를 인터페이스에 작성할 수 있게 되었다. static 메서드는 인스턴스를 생성하지 않아도 사용할 수 있는 독립적인 메서드지만 default 메서드의 경우는 다르다.
인터페이스에 메서드를 추가한다는 것은 추상 메서드를 추가한다는 것이고, 해당 인터페이스를 구현하는 모든 클래스가 새로운 메서드를 구현하는 현상이 생기게 된다. default 메서드는 이러한 문제를 해결하기 위해 추가된 방법으로 추상 메서드의 기본적인 구현을 제공하는 메서드라 추상 메서드가 아니기 때문에 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
class DefaultMethodTest {
public static void main(String[] args) {
Child c = new Child();
c.method1();
c.method2();
MyInterface.staticMethod();
MyInterface2.staticMethod();
}
}
class Child extends Parent implements MyInterface, MyInterface2 {
public void method1() {
System.out.println("method1() in Child");
}
}
class Parent {
public void method2() {
System.out.println("method2() in Parent");
}
}
interface MyInterface {
default void method1() {
System.out.println("method1() in MyInterface");
}
default void method2() {
System.out.println("method2() in MyInterface");
}
static void staticMethod() {
System.out.println("staticMethod() in MyInterface");
}
}
interface MyInterface2 {
default void method1() {
System.out.println("method1() in MyInterface2");
}
static void staticMethod() {
System.out.println("staticMethod() in MyInterface2");
}
}
method1() in Child
method2() in Parent
staticMethod() in MyInterface
staticMethod() in MyInterface2