⦁ 인터페이스를 이용한 다형성
- 인터페이스도 구현 클래스의 부모? 엄밀히는 아니지만 부모의 역할을 함.
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 실습을 많이 해봐야겠음.
'Java > Java의 정석' 카테고리의 다른 글
220314 Java - Chapter 8. 예외 처리(이어서 마무리) (0) | 2022.03.14 |
---|---|
220312 Java - Chapter 8. 예외 처리 (0) | 2022.03.13 |
220309 Java - Chapter 7. 객체지향개념 II Part.2 (0) | 2022.03.10 |
220308 Java - Chapter 7. 객체지향개념 II Part 1 (0) | 2022.03.09 |
220307 Java - Chapter 6. 객체지향 개념I Part. 2 (0) | 2022.03.07 |