인터페이스를 이용한 다형성

- 인터페이스도 구현 클래스의 부모? 엄밀히는 아니지만 부모의 역할을 함.

 
interface Fightable {
	void move(int x, int y);
	void attack(Fightable f);		// Fightable 인터페이스를 구현한 클래스의 인터페이스만 가능!!
}

class Fighter extends Unit implements Fightable {
	public void move (int x , int y) { /* 내용 생략 */ }
	public void attack (Fightable f) { /* 내용 생략 */ }
}
Unit u = new Fighter();
Fightable f = new Fighter();

f.move(100,200);
f.attack(new Fighter());

 

 

- 인터페이스를 메소드의 리턴타입으로 지정할 수 있다.

class Fighter extends Unit implements Fightable {
	public void move (int x , int y) { /* 내용 생략 */ }
	public void attack (Fightable f) { /* 내용 생략 */ }
}
Fightable method() {	// Fightable 인터페이스를 구현한 클래스의 인스턴스를 반환
	Fighter f = new Fighter();
	return f;
}

-> Fightable f를 반환하게 된다.

 

ex)

abstract class Unit2 {
	int x, y;
	abstract void move(int x, int y);
	void stop() {
		System.out.println("멈춥니다.");
	}
}

interface Fightable {	// 인터페이스의 모든 메소드는 public abstract.
	void move(int x, int y);	// public abstract가 생략됨
	void attack(Fightable f);	// public abstract가 생략됨
}

class Fighter extends Unit2 implements Fightable {
	public void move(int x, int y) {	
		// 오버라이딩 규칙 : 조상보다 접근제어자가 좁으면 안된다.
		System.out.println("[" + x + ", " + y + "]으로 이동");
	}
	
	public void attack(Fightable f) {
		System.out.println(f+"를 공격");
	}
	
	// 싸울 수 있는 상대를 불러온다.
	Fightable getFightable() {
		Fighter f = new Fighter();	// Fighter를 생성해서 반환
		return f;	// 실제로는 (Fightable)f 이다.
	}
}

public class FighterTest {

	public static void main(String[] args) {
		Fightable f = new Fighter();
		f.move(100, 200);
		f.attack(new Fighter());
//		f.stop();	// Fightable에는 stop()이 없어서 호출 불가
		
		
		Unit2 u = new Fighter();
		u.move(100, 200);
//		u.attack(new Fighter());	// Unit2에는 attack()이 없어서 호출불가
		u.stop();
		
		Fighter f2 = new Fighter();
		Fightable f3 = f2.getFightable();
		
		
	}

}
 

인터페이스의 장점

- 두 대상(객체) 간의 연결, 대화, 소통을 돕는 중간 역할을 한다.

- 선언(설계)와 구현을 분리시킬 수 있게 한다.

 

- 인터페이스 덕분에 B가 변경되어도 A는 안 바꿀 수 있게 된다.(느슨한 결합)

A <-(Interface)-> B

 

직접적인 관계의 두 클래스(A-B)

class A {
	public void methodA (B b) {
		b.methodB();
	}
}
class B {
	public void methodB() {
		System.out.println(“methodB()”);
	}
}

class InterfaceTest {
	public static void main (String args[]) {
		A a = new A();
		a.methodA(new B());
	}
}

 

간접적인 관계의 두 클래스 (A-I-B)

interface I {
	void methodB();
}
class B implements I {
	public void methodB() {
		System.out.println(“MethodB()”);
	}
}

class A {
	public void methodA(I i) {	// I를 사용한다.
		i.methodB();
	}
}

class C implements I {		// B를 C로 변경한다 해도 A에 영향이 없음.
	public void methodB() {
		System.out.println(“MethodB() in C”);
	}
}

 

 

인터페이스의 장점

- 개발 시간을 단축할 수 있다.

- 변경에 유리한 유연한 설계가 가능하다.

- 표준화가 가능하다.

- 서로 관계없는 클래스들을, 관계를 맺어줄 수 있다.

-> 새로운 인터페이스를 만들어서 구현하게 할 수 있음.

 

 

디폴트 메소드와 static 메소드

- 인터페이스에 디폴트 메소드, static 메소드 추가 가능

- 인터페이스에 새로운 메소드(추상 메소드)를 추가하기 어려움.

해결책 => 디폴트 메소드(default method)

 

- 디폴트 메소드는 인스턴스 메소드(인터페이스 원칙 위반)

 

- 디폴트 메소드가 기존의 메소드와 충돌할 때의 해결책

1. 여러 인터페이스의 디폴트 메소드 간의 충돌

- 인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩 해야 한다.

 

2. 디폴트 메소드와 조상 클래스의 메소드 간의 충돌

 

- 조상 클래스의 메소드가 상속되고, 디폴트 메소드는 무시된다.

 

-> 만약 충돌이 생기면 그냥 직접 오버라이딩 하는 편이 좋다.

 

 

 

내부 클래스(inner class)

- 클래스 안의 클래스

class A {	// 외부 클래스
	...
	class B{	 // 내부 클래스
	...
	}
	...
}

 

내부 클래스의 장점

내부 클래스에서 외부 클래스의 멤버들에 쉽게 접근할 수 있다.

코드의 복잡성을 줄일 수 있다.(캡슐화)

 

- 내부 클래스의 종류와 유효범위(scope)는 변수와 동일

- 내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일

(기본적인 클래스는 public(default) class만 가능)

public / protected / (default) / private

 

 

ex)

class Ex7_13 {
	class InstanceInner {}
	static class StaticInner {}
	
	InstanceInner iv = new InstanceInner();		// 인스턴스멤버끼리는 직접 접근 가능
	static StaticInner cv = new StaticInner();	// static 멤버끼리는 직접 접근 가능
	
	static void staticMethod() {	// static멤버는 인스턴스멤버에 직접 접근 불가
//		InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();
		
		Ex7_13 outer = new Ex7_13();	// 인스턴스클래스는 외부 클래스를 먼저 생성해야 생성가능
		InstanceInner obj1 = outer.new InstanceInner();
	}
	
	void instanceMethod() { // 인스턴스메소드에서는 인스턴스멤버와 static멤버 모두 접근 가능
		InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();
//		LocalInner lv = new LocalInner(); // 지역 내부 클래스는 외부에서 접근할 수 없다.
	}
	
	void myMethod() {
		class LocalInner {}
		LocalInner lv = new LocalInner();
	}

}

 

ex)

public class Outer {
	private int outerIv = 0;
	static int outerCv = 0;
	
	class InstanceInner {
		int iiv = outerIv;	// 외부 클래스의 private멤버도 접근 가능하다.★
		int iiv2 = outerCv;
	}
	
	static class StaticInner {
//		int siv = outerIv; // static클래스는 외부 클래스의 인스턴스멤버에 접근할 수 없다.
		static int scv = outerCv;
	}
	
	void myMethod() {
		final int lv = 0;	// lv가 변수지만 값을 변경하는 코드가 없으므로 상수로 간주된다.
							// 상수는 메소드가 종료되어도 constant pool에 존재함. 그래서 상수를 씀 
		final int LV = 0;	// JDK 1.8부터 final 생략 가능
		
		class LocalInner {	// 지역 내부 클래스를 감싸고 있는 메소드의 상수만 사용 가능
			int liv = outerIv;
			int Liv2 = outerCv;
//			외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능하다.★
			int liv3 = lv;	// 에러!!! (JDK1.8부터 에러 아님)
			int liv4 = LV;	// OK
		}
	}
	
}

내부클래스에서 외부 클래스의 private멤버에 접근이 가능하다.

내부클래스를 감싸고 있는 메소드의 상수만 내부클래스에 사용 가능하다.

 

ex)

 

class Outer2 {
	class InstanceInner {
		int iv = 100;
	}
	
	static class StaticInner {
		int iv = 200;
		static int cv = 300;
	}
	
	void myMethod() {
		class LocalInner {
			int iv = 400;
		} 
	}
	
}

public class Ex7_15 {
	public static void main(String args[]) {
		Outer2 oc = new Outer2();	// 외부 클래스의 인스턴스를 먼저 생성해야 
		Outer2.InstanceInner ii = oc.new InstanceInner(); // 인스턴스 클래스의 인스턴스를 생성 가능
		
		System.out.println("ii.iv : "+ii.iv);
		System.out.println("Outer2.StaticInner.cv : " + Outer2.StaticInner.cv);
		
		// 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
		Outer2.StaticInner si = new Outer2.StaticInner();
		System.out.println("si.iv : "+ si.iv);

	}
}

 

ex)

class Outer3 {
	int value = 10;		// Outer3.this.value
	
	class Inner {
		int value = 20;	// this.value
		
		void method1() {
			int value = 30;
			System.out.println("value :" + value);
			System.out.println("this.value :" + this.value);
			System.out.println("Outer3.this.value :" + Outer3.this.value);
			
		}
	} // Inner클래스의 끝
} // Outer3클래스의 끝
public class Ex7_16 {

	public static void main(String[] args) {
		Outer3 outer = new Outer3();
		Outer3.Inner inner = outer.new Inner();
		inner.method1();

	}

}

 

 

익명 클래스(anonymous class)

- 이름이 없는 일회용 클래스. 정의와 생성을 동시에 한다.

new 조상클래스이름() {

// 멤버 선언

}

또는

 

new 구현인터페이스이름() {

// 멤버 선언

}

 

 

ex)

public class Ex7_17 {
	Object iv = new Object() {
		void method() {}
		
	};	// 익명 클래스
	
	static Object cv = new Object() {
		void method() {} 
	};	// 익명 클래스
	
	void myMethod() {
		Object lv = new Object() {
			void method() {}
		};	// 익명 클래스
	}
	
}

 

ex2)

import java.awt.*;
import java.awt.event.*;

//class EventHandler implements ActionListener {
//	public void actionPerformed(ActionEvent e) {
//		System.out.println("ActionEvent occurred!!!");
//	}
//}

public class Ex7_18 {
	public static void main(String[] args) {
		Button b = new Button("Start");
		b.addActionListener(new ActionListener() {	// 클래스의 정의와 객체 생성을 동시에
			public void actionPerformed(ActionEvent e) {
				System.out.println("ActionEvent occurred!!!");
			}
			
		});
	}
}

 

 


출처 - 유튜브 남궁성의 정석코딩 [자바의 정석 - 기초편] 

 

 

Chapter 6과 7 내용이 매우 중요해서, 앞으로 진도 나가면서도 계속 복습해야 한다고 하셨다

 

Chapter 8 나가기 전에 내일은 Chapter 6, 7 실습을 많이 해봐야겠음.

+ Recent posts