interface 인터페이스이름{
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수목록);
}
interface PlayingCard {
public static final int SPADE = 4;
final int DIAMOND = 3; //public static 생략
static int HEART = 2; //public생략
int CLOVER = 1; //public static final 생략
public abstract String getCardNumber();
String getCardKind(); //public abstract 생략
}
추상클래스
abstract class Player{ //추상클래스
boolean pause;
int currentPos;
Player(){
pause = false;
currentPos = 0;
}
abstract void play(int pos); //추상메서드
abstract void stop();
void play(){ //인스턴스 메서드
play(currentPos);
}
}
인터페이스
interface Fightable{
void move(int x, int y);
void attack(Unit u);
} //추상메서드밖에 없음..
인터페이스 구현 예제
interface PhoneInterface{
final int TIMEOUT = 10000; //상수필드
void sendCall(); //추상메서드
void receiveCall(); //추상메서드
default void printLogo(){ //default메서드
System.out.println("**phone**");
}
}
class SamsungPhone implements PhoneInterface {
//PhoneInterface의 모든 추상메서드 구현
public void sendCall() {
System.out.println("따르릉");
}
public void receiveCall() {
System.out.println("전화왔습니다");
}
//메서드 추가 착성
public void flash() {
System.out.println("전화기에 불이 켜졌습니다");
}
}
public class InterfaceEx {
public static void main(String[] args) {
SamsungPhone sp = new SamsungPhone();
sp.printLogo();
sp.sendCall();
sp.receiveCall();
sp.flash();
}
}
interface Fightable extends Movable,Attackable{}
interface Movable{
void move(int x, int y);
}
interface Attackable{
void attack(Unit u);
}
class 클래스이름 implements 인터페이스{
//추상메서드 구현
}
class Fighter implements Fightable{ //fighter 클래스는 fightable 인터페이스를 구현했다
//인터페이스에 정의된 추상메서드를 모두 구현해야 한다.
public void move(int x, int y) {
//내용 생략
}
public void attack(Unit u){
//내용 생략
}
}
일부만 구현하는 경우 클래스 앞에 abstract를 붙인다.
abstract class Fighter implements Fightable{
public void move(int x, int y) {
//내용 생략
}
}
또 다음과 같이 상속과 구현을 동시에 할 수도 있다.
class Fighter extends Unit implements Fightable{
public void move(int x, int y) {
public void attack(Unit u){
}
}
interface PhoneInterface{
final int TIMEOUT = 10000; //상수필드
void sendCall(); //추상메서드
void receiveCall(); //추상메서드
default void printLogo(){ //default메서드
System.out.println("**phone**");
}
}
interface MobilePhoneInterface extends PhoneInterface{
//인터페이스 상속
void sendSMS();
void receiveSMS();
}
interface MP3Interface{
public void play();
public void stop();
}
class PDA{
public int calculate(int x, int y){
return x + y;
}
}
//smartPhone은 PDA를 상속받고 두 추상메서드를 구현한다
class SmartPhone extends PDA implements MobilePhoneInterface, MP3Interface {
public void sendCall() {
System.out.println("따르르릉");
}
public void receiveCall() {
System.out.println("전화왔엉");
}
public void sendSMS() {
System.out.println("문자 간다");
}
public void receiveSMS() {
System.out.println("문자 왔다");
}
public void play() {
System.out.println("음악 나온다");
}
public void stop() {
System.out.println("음악 끈다");
}
//추가로 작성한 메서드
public void schedule() {
System.out.println("일정 관리");
}
}
public class InterfaceEx{
public static void main(String[] args) {
SmartPhone sp = new SmartPhone();
sp.printLogo();
sp.sendCall();
sp.play();
System.out.println("3 + 5 = " + sp.calculate(3,5));
sp.schedule();
}
}
인터페이스(조상) 타입의 참조변수로 구현한 클래스(자식)의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환이 가능하다.
이전 글의 매개변수의 다형성과 비슷하다. 매개변수의 다형성
인터페이스 Fightable을 클래스 Fighter가 구현했을 때
다음과 같이 Fighter 인스턴스를 Fighterable타입의 참조변수로 참조하는 것이 가능하다.
(그러나 FIghtable 인터페이스에 선언된 추상 메서드만 쓸 수 있다.)
Fightable f = (Fightable)new Fighter();
또는
Fightable f = new Fighter();
//Fightable타입의 참조변수로 Fighter클래스 참조
따라서 인터페이스는 다음과 같이 메서드의 매개변수 타입으로도 사용될 수 있다.
void attack(Fightable f){
}
인터페이스 타입의 매개변수가 갖는 의미는 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야 한다는 것이다.
class Fighter extends Unit implements Fightable{
//Fighter클래스가 Unit을 상속받고 Fightable을 구현한다
public void move(int x, int y){ //내용 생략
}
public void attack(Fightable f){ //내용 생략
}
}
이와 같이 Fighterble 인터페이스를 구현한 Fighter 클래스가 있을 때, attack의 매개변수로 Fighter 인스턴스를 넘겨줄 수 있다. 즉 attack(new Fighter())와 같이 할 수 있다.
또 메서드의 리턴타입으로 인터페이스를 지정할 수 있다.
리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 뜻이다.
Fightable method(){
Fighter f = new Fighter();
return f;
//한 문장으로 하면 return new Fighter();
즉 이 경우, 메서드의 리턴문에서 Fighteralbe인터페이스를 구현한 Fighter 클래스의 인스턴스 주소를 반환한다.
예를 들어 껍데기와 알맹이 모두 가지고 있는 class B를 인터페이스를 이용해 분리시켜보자.
class B{
public void method(){
System.out.println("methodInB");
}
}
우선 method()를 메서드로 갖는 인터페이스를 선언한다. 이는 껍데기다.
interface I{
public void method(); //추상 메서드, 선언부만 가진 껍데기
}
그리고 class B가 interface I를 구현시킨다.
class B implements I{
public void method(){
System.out.println("methodInB");
}
}
껍데기와 알맹이를 모두 가지고 있는 기존의 class B는 유연성이 떨어지고 변경에 불리하다. (높은 결합도)
그러나 인터페이스를 사용하면 낮은 결합도를 가진 유연한 노드가 된다.
코드를 통해 더 자세히 보도록 하자.
직접적인 관계를 가진 A,B 두 클래스가 있다.
이 코드는 B를 C로 변경하면 A도 함께 변경해야한다.
그러면 간접적인 관계는 어떤 걸까?
B클래스의 껍데기와 알맹이를 분리했다.
먼저 B클래스의 method()를 추상 메서드로 갖는 인터페이스를 작성했다.
A클래스 역시 인터페이스 I를 사용하도록 변경했다. 이 경우 B와는 관계가 없어지므로 B가 바뀌어도 A클래스에는 변경이 없다.
즉
class A{
public void method(B b){
//public void method(C b) A가 B 대신 C를 사용하려면 여기도 수정해야함
b.method(); //b의 메서드를 호출하는 메서드
}
}
class B{
public void method(){
System.out.println("class B's method");
}
}
//class C{
// public void method(){
// System.out.println("class C's method");
// }
//}
public class Interface {
public static void main(String[] args) {
A a = new A();
a.method(new B()); //A가 B를 사용,의존
//a.method(new C()) //A가 C를 사용하려면 여기도 수정해야함
}
}
이 코드에서는 클래스를 추가하려면 따로 추가해야한다. 그렇다면 인터페이스를 이용해보겠다.
class A{
public void method(I i){ //인터페이스 I를 구현한 것만 들어갈 수 있음
i.method();
}
}
//class b의 선언과 구현 분리
interface I{
public void method(); //method의 선언
}
class B implements I{
public void method(){ //method의 구현
System.out.println("class B's method");
}
}
class C{
public void method(){
System.out.println("class C's method");
}
}
public class Interface {
public static void main(String[] args) {
A a = new A();
a.method(new B()); //A가 B를 사용,의존
}
}
이 때 C를 추가하면? 인터페이스를 구현하면 된다. 이 경우 클래스 A에는 변화가 없어도 된다.
class A{
public void method(I i){ //인터페이스 I를 구현한 것만 들어갈 수 있음
//public void method(C c)
i.method();
}
}
//class b의 선언과 구현 분리
interface I{
public void method(); //method의 선언
}
class B implements I{
public void method(){ //method의 구현
System.out.println("class B's method");
}
}
class C implements I{
public void method(){
System.out.println("class C's method");
}
}
public class Interface {
public static void main(String[] args) {
A a = new A();
a.method(new C());
}
}
Unit을 상속받는 클래스들이 있다고 하자.
이 때 Tank와 Dropship을 수리하는 메서드를 만들 때에는
1. 오버로딩을 통해 메서드를 여러개 만들거나
2. 다형성을 이용할 수 있다. 그런데 이 경우에는 해당 클래스와 그 자손만 가능하다.
이 경우 3개의 클래스가 인터페이스를 만들어서 구현한다면
repair 메서드 하나만 추가하면 된다. Repairable을 구현한 클래스의 객체(SCV, Tank, Dropship)들만 이용할 수 있다.
interface MyInterface{
void method();
void newMethod(); //추상 메서드
}
위와 같이 추상 메서드 newMethod()를 추가하는 대신
interface MyInterface{
void method();
default void newMethod(){}
}
이처럼 디폴트 메서드를 추가하면 기존의 MyInterface를 구현한 클래스를 변경하지 않아도 된다.(조상 클래스에 새로운 메서드를 추가한 것과 동일)
class Parent3{
public void method2(){
System.out.println("method2() in Parent3");
}
}
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");
}
}
class Child3 extends Parent3 implements MyInterface, MyInterface2{
public void method1(){
System.out.println("method1() in Child3"); //오버라이딩
}
}
public class InterEx {
public static void main(String[] args) {
Child3 c = new Child3();
c.method1();
c.method2();
MyInterface.staticMethod();
MyInterface2.staticMethod();
}
}
output
method1() in Child3
method2() in Parent3
staticMethod() in MyInterface
staticMethod() in MyInterface2