클래스 내부에는 필드, 메서드, 생성자와 더불어 이너 클래스, 이너 인터페이스까지 총 4가지 종류의 요소
만 존재할 수 있다고 했다. 13장에서는 마지막 내부 요소인 이너 클래스, 이너 인터페이스를 알아보자. 문
법적으로 이해하기가 조금 까다롭지만, 결국 필드나 메서드처럼 클래스의 내부 멤버 중 하나일 뿐이므로
다른 멤버들과 비슷한 관점으로 접근하면 이해하는 데 많은 도움이 될 것이다.
- 13.1 이너 클래스
- 13.2 익명 이너 클래스
- 13.3 이너 인터페이스
13.1 이너 클래스
클래스 내부에 포함되는 이너 클래스(inner class)는 인스턴스 멤버 이너 클래스, 정적 멤버 이너 클래스 그리고 지역 이너 클래스로 나뉜다.
인스턴스 멤버 클래스와 정적 멤버 클래스는 필드와 메서드처럼 클래스의 멤버인 반면, 지역 이너 클래스는 메서드 내에서 정의하며, 지역 변수처럼 해당 메서드 내부에서만 한정적으로 사용되는 클래스다. 그럼 이너 클래스의 종류를 하나씩 알아보자.
13.1.1 인스턴스 멤버 이너 클래스
인스턴스 멤버 이너 클래스는 이름에서도 알 수 있는 것처럼 인스턴스, 즉 객체 내부에 멤버의 형태로 존재한다. 이때 자신을 감싸고 있는 아우터 클래스(outer class)의 모든 접근 지정자의 멤버에 접근할 수 있다. 인스턴스 멤버 이너 클래스 자체도 아우터 클래스의 멤버이므로 당연한 이야기일 것이다. 소스 파일(.java)이 1개라 하더라도 컴파일을 수행하면 각 클래스별로 바이트 코드(.class) 파일이 생성된다고 했다. 이너 클래스도 이와 마찬가지다. 다음과 같이 아우터 클래스 내부에 1개의 이너 클래스가 존재할 때를 생각해보자.
접근 지정자와 상관없이 아우터 클래스의 멤버를 사용할 수 있다는 점을 활용하기 위해 일반적으로 이너 클래스를 사용한다. 따라서 디스플레이 클래스 내부에 색 조정 클래스를 포함하는 것처럼 이너 클래스가 아우터 클래스와 밀접하게 관련돼 있을 때 주로 사용한다.
class 아우터클래스 {
class 이너클래스 {
// ...
}
}
아우터 클래스의 바이트 코드로 '아우터 클래스.class', 인스턴스 멤버 이너 클래스의 바이트 코드로 '아우터 클래스$이너 클래스.class' 파일이 생성된다. 이너 클래스로 생성되는 파일명에서 볼 수 있는 것처럼 이너 클래스는 독립적으로 사용할 수 없고, 반드시 아우터 클래스를 이용해야만 사용할 수 있다.
인스턴스 이너 클래스 객체 생성하기
이번에는 인스턴스 멤버 이너 클래스의 객체를 생성하는 방법을 알아보자. 앞에서 말한 것처럼 인스턴스 멤버 이너 클래스는 아우터 클래스의 객체 내부에 존재한다. 따라서 이너 클래스의 객체를 생성하기 위해서는 먼저 아우터 클래스의 객체를 생성해야 한다. 이후 생성한 아우터 클래스 객체의 참조 변수를 이용해 객체 내부에 있는 이너 클래스의 생성자를 다음과 같이 호출한다.
이는 인스턴스 필드나 메서드를 사용하기 위해 클래스의 객체를 먼저 생성해야 하는 것과 같은 원리다.
// 인스턴스 멤버 이너 클래스의 객체 생성 방법
아우터클래스 아우터클래스참조변수 = new 아우터클래스();
아우터클래스.이너클래스 이너클래스참조변수 = 아우터클래스참조변수.new 이너클래스();
class A {
class B {
}
}
A a = new A();
a.B b = a.new B();
여기서 1가지 주의해야 할 점은 이너 클래스 객체의 자료형은 B가 아니라 A.B라는 것이다. 그 이유는 생성되는 바이트 코드가 A$B.class이기 때문이다. 다시 한번 말하지만, 이너 클래스는 B b와 같이 단독으로 쓰일 수 없으며, 반드시 A.B b와 같이 아우터 클래스를 이용해야만 선언할 수 있다. 객체를 생성하는 모양이 다소 특이해 보이지만, 인스턴스 이너 클래스 자체가 아우터 클래스 객체 내부에 존재한다는 점을 이해하면 왜 이런 문법이 나왔는지 어느 정도 이해할 수 있을 것이다.
만일 B b가 가능하려면 B.class 바이트 코드가 있어야 하는데, 이때 B.class는 $가 포함되지 않았기 때문에 이너 클래스가 아니다.
// 인스턴스 이너 클래스의 아우터 클래스 멤버 사용 및 객체 생성
class A {
public int a = 3;
protected int b = 4;
int c = 5;
private int d = 6;
void abc() {
System.out.println("A 클래스 메서드 abc()");
}
// 인스턴스 이너 클래스
class B {
void bcd() {
// 아우터 클래스의 필드 사용
// 아우터 클래스의 모든 멤버를 접근 지정자와 상관없이 사용 가능
System.out.println(a); // 3
System.out.println(b); // 4
System.out.println(c); // 5
System.out.println(d); // 6
// 아우터 클래스의 메서드 호출
abc(); // A 클래스 메서드 abc()
}
}
}
public class CreateObjectAndAccessMember {
public static void main(String[] args) {
// 아우터 클래스 객체 생성
A a = new A();
// 멤버 사용
A.B b = a.new B();
b.bcd();
}
}
아우터 클래스의 객체 참조하기
앞에서 인스턴스 이너 클래스에서는 아우터 클래스의 모든 멤버를 마치 자기 것인양 사용할 수 있다고 했다. 그렇다면 아우터 클래스의 필드나 메서드와 동일한 이름을 이너 클래스 안에서 정의했을 때 이너 클래스의 내부에서는 누구의 필드 또는 메서드가 참조될까? 당연히 이너 클래스의 필드나 메서드가 참조된다. 참조 객체명을 생략할 때는 자기 자신의 객체를 가리키는 this 키워드를 컴파일러가 자동으로 추가하기 때문이다. 이너 클래스 내부에서 this.의 의미는 이너 클래스 자신이 된다. 이너 클래스의 내부에서 아우터 클래스의 멤버를 참조하고 싶다면 어떻게 해야 할까? 이때는 '아우터클래스명.this.'를 명시적으로 붙여 사용한다. 이 역시 조금은 특이한 형태이므로 기억해 놓길 바란다.
다음 예를 살펴보자. 클래스 B 내부의 메서드에서 a와 같이 참조 변수 없이 멤버를 사용하거나 this.a를 사용하면 B 클래스의 필드 a를 의미한다. 반면 아우터 클래스의 필드 a를 참조하기 위해서는 A.this.a와 같이 사용해야 한다. 메서드도 이와 비슷하게 동작한다.
이너 클래스의 메서드 내에서 참조 변수 없이 멤버를 사용하면 컴파일러는 일단 this.를 붙여 멤버를 찾는다. 만일 해당되는 멤버가 없으면 아우터클래스.this.를 붙인 후 아우터 클래스의 멤버를 검색해 실행한다.
// 이너 클래스 내부에서 아우터 클래스의 필드/메서드 사용
class A {
int a = 3;
int b = 4;
void abc() {
System.out.println("A 클래스 메서드");
}
// 인스턴스 이너 클래스 정의
class B {
int a = 5;
int b = 6;
void abc() {
System.out.println("B 클래스 메서드");
}
void bcd() {
// 이너 클래스의 멤버 호출 또는 사용
System.out.println(a);
System.out.println(b);
abc();
// 아우터 클래스의 멤버 호출 또는 사용
System.out.println(A.this.a);
System.out.println(A.this.b);
A.this.abc();
}
}
}
public class UseMembersOfOuterClass {
public static void main(String[] args) {
// 아우터 클래스 객체 생성
A a = new A();
// 이너 클래스 객체 생성
A.B b = a.new B();
b.bcd();
}
}
// 실행 결과
5
6
B 클래스 메서드
3
4
A 클래스 메서드
13.1.2 정적 멤버 이너 클래스
정적 멤버 이너 클래스는 이너 클래스 앞에 static 키워드가 포함된 이너 클래스다. 정적 메서드와 동일하게 아우터 클래스의 정적 멤버에만 접근할 수 있는데, 이는 이너 클래스의 특성이 아닌 정적(static) 특성이다. 즉, 아우터 클래스의 객체를 생성하지 않아도 정적 이너 클래스의 객체를 생성해 사용할 수 있어야 하므로 아우터 클래스의 멤버 중 객체 생성 없이 바로 사용할 수 있는 정적 멤버만 정적 이너 클래스 내부에서 사용할 수 있는 것이다.
정적 이너 클래스 객체 생성하기
컴파일 이후에는 인스턴스 멤버 클래스와 동일하게 '아우터클래스.class', '아우터클래스$이너클래스.class'의 바이트 코드 파일이 생성된다. 반면 객체를 생성하는 방법은 다음과 같이 훨씬 간단하다. 정적 이너 클래스도 말 그대로 정적 멤버이므로 클래스명으로 바로 접근할 수 있다.
// 정적 멤버 이너 클래스의 객체 생성 방법
아우터클래스.이너클래스 이너클래스참조변수 = new 아우터클래스.이너클래스();
class A {
static class B {
}
}
A.B b = new A.B();
'아우터클래스.이너클래스'를 1개의 긴 클래스명으로 생각하면 결국 A a = new A()의 형태인 것이다. 이는 정적 이너 클래스가 아우터 클래스의 객체를 생성하지도 않고 바로 사용할 수 있는 정적 멤버이기 때문이다. 다만 여기서 주의해야 할 점은 아우터 클래스의 객체 생성 전에도 사용할 수 있어야 하므로 정적 이너 클래스 내부에서는 아우터 클래스의 정적 멤버만 사용할 수 있다는 것을 다시 한번 기억하자.
// 정적 이너 클래스에서의 외부 멤버 사용 및 객체 생성
class A {
int a = 3;
static int b = 4;
void method1() {
System.out.println("instance method");
}
static void method2() {
System.out.println("static method");
}
// 정적 이너 클래스
static class B {
void bcd(){
// 필드 사용
// 정적 이너 클래스는 아우터 클래스의 정적 멤버만 사용 가능
// System.out.println(a);
System.out.println(b);
// 메서드 호출
// method1(); // 불가능
method2();
}
}
}
public class CreateObjectAndAccessMember {
public static void main(String[] args) {
// 정적 이너 클래스의 객체 생성
A.B b = new A.B();
// 메서드 호출
b.bcd();
}
}
4
static method
13.1.3 지역 이너 클래스
지역 이너 클래스는 클래스의 멤버가 아닌 메서드 내에서 정의되는 클래스다. 지역 변수처럼 정의된 메서드 내부에서만 사용할 수 있으므로 일반적으로 지역 이너 클래스는 선언 이후 바로 객체를 생성해 사용하며, 메서드가 호출될 때만 메모리에 로딩된다. 따라서 지역 이너 클래스는 정적 클래스로 지정할 수 없다.
지역 클래스를 컴파일하면 생성되는 클래스명이 조금 독특한데, '아우터클래스$+숫자+지역이너클래스.class'와 같이 지역 이너 클래스 이름 앞에 숫자가 포함된 이름으로 클래스 파일이 생성된다. 숫자가 포함되는 이유는 지역 이너 클래스는 메서드 내에서만 정의하고 사용할 수 있으므로 여러 개의 메서드에서 동일한 이름의 지역 이너 클래스를 생성할 수도 있기 때문이다. 동일한 지역 클래스명이 있을 때는 숫자가 1부터 하나씩 증가한다. 정적 클래스가 되기 위해서는 객체가 생성돼야 사용할 수 있는 메서드가 호출되기 전에 정적 영역에 로딩될 수 있어야 한다.
지역 이너 클래스 객체 생성하기
지역 이너 클래스도 다른 이너 클래스처럼 아우터 클래스의 멤버를 접근 지정자와 상관없이 사용할 수 있으며, 추가로 자신이 정의된 메서드의 지역 변수도 클래스 내부에서 사용할 수 있다. 단, 지역 변수를 사용할 때는 반드시 해당 지역 변수가 final로 선언돼야 하며, 만일 final로 선언되지 않은 지역 변수를 지역클래스 내부에서 사용할 때는 컴파일러가 강제로 해당 지역 변수에 final을 추가해 준다. 지역 이너 클래스의 객체 생성 방법은 일반 클래스의 객체 생성 방법과 동일하지만, 클래스가 정의된 메서드 내부에서만 객체를 생성할 수 있다는 차이점이 있다.
// 지역 이너 클래스의 객체 생성 방법
class A {
void abc() {
class B {} // 지역 이너 클래스
B b = new B(); // 지역 이너 클래스 객체 생성
}
}
// 지역 이너 클래스 내부에서 외부 멤버 및 메서드 지역 변수 활용
class A {
int a = 3; // 필드
void abc() {
int b = 5; // 지역 변수
class B { // 지역 이너 클래스
void bcd() {
System.out.println(a);
System.out.println(b);
a = 5;
// b = 7; // 지역 이너 클래스에서 사용하는 지역 변수는 자동 final 선언
}
}
B bb = new B();
bb.bcd();
}
}
public class AccessMemberAndLocalVariable {
public static void main(String[] args) {
// 객체 생성 및 메서드 호출
A a = new A();
a.abc();
}
}
// 실행 결과
a
b
지역 이너 클래스명이 중복될 때 클래스가 어떻게 생성되는지를 확인하기 위해 다음 예를 살펴보자. 아우터 클래스 A에는 2개의 메서드, 각각의 메서드 내부에는 2개의 지역 이너 클래스가 있다. 그중 지역 이너 클래스 C는 2개의 메서드 내에 동일한 이름으로 존재한다. 이때 컴파일 이후의 생성된 클래스 파일명은 A.class, A$1B.class, A$1C.class, A$2C.class, A$1D.class가 된다.
class A { // A.class
void abc() {
class B {} // A$1B.class
class C {} // A$1C.class
}
void bcd() {
class C {} // A$2C.class
class D {} // A$1D.class
}
}
13.2 익명 이너 클래스
앞의 추상 클래스와 인터페이스의 객체를 생성하는 과정에서 익명(anoymous) 이너 클래스를 활용하는 방법을 알아봤다. 이 절에서는 익명 이너 클래스를 좀 더 자세하게 알아보자.
13.2.1 익명 이너 클래스의 정의와 특징
익명 이너 클래스는 말 그대로 '이름을 알 수 없는 이너 클래스'를 의미한다. 익명 이너 클래스는 정의된 위치에 따라 분류할 수 있는데, 클래스의 중괄호 바로 아래에 사용했을 때는 인스턴스 익명 이너 클래스, 메서드 내부에서 사용했을 때는 지역 익명 이너 클래스를 의미한다. 이는 앞 절의 이너 클래스에서 살펴본 바와 같다. 다음 예를 살펴보자. 정적 익명 이너 클래스는 존재할 수 없다. 정적이려면 객체 생성 없이 클래스명만으로 객체를 생성해야 하는데, 익명 이너 클래스는 이름을 알 수 없기 때문이다.
먼저 클래스 A1 내부에는 인터페이스 C를 구현(implements)하고 있는 인스턴스 이너 클래스 B가 있다. 필드에는 인터페이스 C 타입의 참조 변수가 있고, 생성자 B()로 객체를 생성해 초기화했다. 그리고 abc() 메서드가 정의돼 있다. 즉, 클래스 A1에서는 C 타입의 객체를 생성하기 위해 이너 클래스를 B라는 이름으로 직접 정의해 사용했다. 이를 익명 이너 클래스로 정의한 클래스 A2를 살펴보자. C 타입의 생성자를 호출한 후 중괄호 안에 인터페이스에 포함된 추상 메서드 bcd()를 구현해 표현했다. 이렇게 되면 컴파일러는 인터페이스 C를 상속받아 추상 메서드를 구현한 클래스를 내부적으로 생성한 후 해당 클래스로 객체를 생성해 참조 변수에 대입한다. 즉, 익명의 이너 클래스를 생성해 사용하는 것이다.
// 인터페이스를 상속한 이너 클래스를 생성해 인터페이스 객체 생성
class A {
C c = new B();
void abc() {
c.bcd();
}
class B implements C {
public void bcd() {
System.out.println("인스턴스 이너 클래스");
}
}
}
interface C {
public abstract void bcd();
}
public class AnonymousClass_1 {
public static void main(String[] args) {
// 객체 생성 및 메서드 호출
A a = new A();
a.abc();
}
}
// 실행 결과
인스턴스 이너 클래스
// 익명 이너 클래스를 활용해 인터페이스 객체 생성
class A {
C c = new C() {
public void bcd() {
System.out.println("익명 이너 클래스");
}
};
void abc() {
c.bcd();
}
}
interface C {
public abstract void bcd();
}
public class AnonymousClass_2 {
public static void main(String[] args) {
// 객체 생성 및 메서드 호출
A a = new A();
a.abc();
}
}
// 실행 결과
익명 이너 클래스
이번에는 조금 특이한 예를 고려해보자. 앞의 예와 비슷하지만, 인터페이스 C를 구현한 자식 클래스에서 메서드(cde())를 추가로 정의했을 때다.
왼쪽처럼 자식 클래스 타입으로 객체를 선언하고 생성하면 오버라이딩된 메서드와 추가 메서드를 모두 사용할 수 있다. bcd() 메서드와 cde() 메서드 모두 클래스 B 내부에서 정의돼 있으며, 참조 변수가 B 타입이므로 두 메서드를 모두 호출할 수 있다. 반면 익명 이너 클래스를 사용할 때는 항상 부모 타입으로만 선언할 수 있으므로 추가로 정의한 메서드 cde()는 항상 호출할 수 없다.
호출을 할 수 없으므로 애초에 만들 필요가 없을까? 그렇지는 않다. 오버라이딩 메서드 내부에서는 호출할 수 있으므로 작성해야 할 내용이 많을 때 메서드를 분리해 작성하는 것이 효율적이며, 이때 익명 이너 클래스 정의식 내부에 추가 메서드를 정의해 사용하는 것이 편할 수 있다.
13.2.2 익명 이너 클래스를 활용한 인터페이스 타입의 입력매개변수 전달
이번에는 인터페이스 타입의 입력매개변수로 익명 이너 클래스를 이용해 생성한 객체를 전달하는 방법을 알아보자. 다음 예제에서 인터페이스 A에는 추상 메서드 abc(), 클래스 C에는 인터페이스 A 타입의 객체를 입력매개변수로 포함하고 있는 메서드 cde(A a)가 있다.
interface A {
public abstract void abc();
}
class C {
void cde(A a) {
a.abc();
}
}
이때 클래스 C의 객체를 생성한 후 cde(A a) 메서드를 호출하기 위해서는 입력매개변수로 사용될 인터페이스 A 타입의 객체를 생성해야 한다. 인터페이스로는 객체를 직접 생성할 수 없으므로 인터페이스 A를 구현한 자식 클래스의 객체가 전달돼야 할 것이다. 결국 인터페이스 A의 객체 생성 문제와 동일해진다. 인터페이스 A의 객체를 생성하고, 메서드의 입력매개변수로 전달하는 방법은 문법적으로 크게 4가지 형태로 분류할 수 있다. 먼저 다음과 같이 인터페이스 A의 자식 클래스 B를 직접 정의할 때를 살펴보자.
class B implements A {
public void abc() {
// ...
}
}
자식 클래스를 직접 정의했으므로 자식 클래스의 생성자를 호출함으로써 객체는 얼마든지 생성할 수 있을 것이다. 이때 생성한 객체를 메서드 입력매개변수로 전달하는 방식에 따라 다음과 같이 다시 2가지 형태를 지닌다.
C c = new C();
// 방법 1
A a1 = new B();
c.cde(a1);
// 방법 2
c.cde(new B());
먼저 방법 1은 B() 생성자를 이용해 생성한 객체를 A 타입의 참조 변수에 저장하고, 입력매개변수로 참조 변수를 넘겨 주는 방식이다. 이때 객체 참조 변수(a1)의 역할은 객체의 참조값을 전달하기 위해서만 사용된다. 단순히 객체의 참조값만을 전달할 목적이라면 굳이 참조 변수를 사용하지 않고 메서드 입력매개변수 위치에서 바로 new B()와 같이 객체를 생성하면 생성된 객체의 참조값이 메서드로 전달될 것이다. 이것이 방법 2이다. 그럼 이번에는 자식 클래스를 별도로 정의하지 않고, 익명 이너 클래스를 사용해 메서드 입력매개변수로 객체를 전달하는 방법을 살펴보자.
C c = new C();
// 방법 3
A a = new A() {
public void abc() {
// ...
}
};
c.cde(a);
// 방법 4
c.cde(new A() {
public void abc() {
// ...
}
});
익명 이너 클래스 문법을 활용했다는 점을 제외하면 방법 1, 2와 같다. 방법 3은 익명 이너 클래스를 사용해 객체를 생성하고, 객체를 참조하는 참조 변수(a)를 cde() 메서드의 입력매개변수로 전달했다. 반면 방법 4는 참조 변수를 대입하지 않고, 메서드 입력매개변수 자리에서 곧바로 익명 이너 클래스 객체를 생성해 전달하는 방식이다. 4가지 방법 중 어떤 것을 사용해도 괜찮지만, 생성하는 객체의 개수, 객체의 사용 시점 등 상황에 따라 효율적인 방법이 다르므로 4가지 방법을 모두 알아 두자. 특히 마지막 방법은 이벤트 처리 등에 가장 많이 사용되는 형식이므로 꼭 기억해 두길 바란다.
13.3 이너 인터페이스
이제 마지막으로 이너 인터페이스(inner interface)를 알아보자. 이너 클래스와 마찬가지로 인터페이스를 클래스 내부에 정의하는 것은 해당 클래스에 의존적인 기능을 수행할 때다. 예를 들어 버튼 클릭을 감지하는 인터페이스는 버튼 클래스 내부에 위치시키는 것이 바람직할 것이다. 이러한 이유로 이너 인터페이스는 '사용자 인터페이스(user interface)'의 이벤트 처리에 가장 많이 사용된다.
일반적으로 사용자 인터페이스에서 이벤트를 감지하는 인터페이스를 '리스너'라고 한다.
13.3.1 이너 인터페이스의 정의와 특징
이너 인터페이스의 중요한 특징 중 하나는 정적 이너 인터페이스만 존재할 수 있다는 것이다. 만일 이너 인터페이스 앞에 static 제어자를 생략하면 컴파일러가 자동으로 추가해 준다.
컴파일하면 이너 클래스와 같이 '아우터클래스명$이너인터페이스명.class' 형태로 바이트 코드인 .class 파일이 생성된다. 이너 인터페이스도 인터페이스이므로 자체적으로는 객체를 생성할 수 없다. 따라서 객체를 생성하기 위해서는 해당 인터페이스를 상속한 자식 클래스를 생성한 후 생성자를 이용해 객체를 생성하거나 익명 이너 클래스를 활용해 객체를 생성해야 한다. 즉, 일반적인 인터페이스 객체를 생성하는 방법과 동일하며, 유일한 차이점은 인터페이스가 클래스 내부에 존재하므로 객체의 타입을 '아우터클래스명.이너인터페이명'과 같이 사용해야 한다는 것이다.
class A {
interface B { // 이너 인터페이스는 자동으로 static 지정
public abstract void bcd();
}
}
class C implements A.B {
public void bcd() {
System.out.println("이너 인터페이스 구현 클래스 생성");
}
}
public class CreateObjectOfInnerInterface {
public static void main(String[] args) {
// 객체 생성 방법 1(자식 클래스 직접 생성)
A.B ab = new C();
C c = new C();
c.bcd();
// 객체 생성 방법 2(익명 이너 클래스 생성)
A.B b = new A.B() {
public void bcd() {
System.out.println("익명 이너 클래스로 객체 생성");
}
};
b.bcd();
}
}
// 실행 결과
이너 인터페이스 구현 클래스 생성
익명 이너 클래스로 객체 생성
13.3.2 이벤트 처리 기능 작성하기
이번에는 사용자 인터페이스에서 사용되는 일반적인 이벤트 처리 기능을 이너 인터페이스를 이용해 작성해 보자. 이제까지 배운 문법만으로 충분히 작성할 수 있다. 먼저 Button 클래스를 살펴보자. Button 등과 같은 기본적인 사용자 인터페이스 클래스는 API로 제공된다. 기본적인 구조는 다음과 같다.
// API가 제공하는 버튼의 일반적인 구조
class Button {
onClickListener ocl;
void setOnClickListener(OnClickListener ocl) {
this.ocl = ocl;
}
interface OnClickListener {
public abstract void onClick();
}
void click() {
ocl.onClick();
}
}
내부에 OnClickListener라는 이너 인터페이스가 정의돼 있고, ocl 필드는 이 인터페이스의 타입이다. setOnClickeListener() 메서드는 이 인터페이스 객체를 입력매개변수로 넘겨받아 필드를 초기화하는 기능을 수행한다. 마지막으로 click() 메서드는 초기화된 필드 객체 내부의 onClick() 메서드를 실행시킨다. 즉, Button은 기능은 내부에서 정해지는 것이 아니라 외부에서 정해 입력받는 것이다. 이제 이렇게 API에서 제공받은 Button 클래스를 사용해 개발자가 Button에 기능을 부여하는 경우를 살펴보자.
setOnClickListener(OnClickListener ocl) 메서드는 외부에서 onClick() 메서드가 구현된 객체를 전달받기 때문에 이 메서드가 호출된 이후에는 언제든지 onClick() 메서드를 호출할 수 있다.
'자바 > Do it! 자바 완전 정복' 카테고리의 다른 글
Do it! 자바 완전 정복: 14장 예외 처리 (0) | 2025.01.31 |
---|---|
Do it! 자바 완전 정복: 12장 추상 클래스와 인터페이스 (1) | 2025.01.31 |
Do it! 자바 완전 정복: 11장 자바 제어자 2 (0) | 2025.01.31 |
Do it! 자바 완전 정복: 10장 클래스의 상속과 다형성 (0) | 2025.01.31 |
Do it! 자바 완전 정복: 9장 자바 제어자 1 (0) | 2025.01.31 |