중첩클래스와 중첩 인터페이스란?
객체 지향 프로그램에서에서 클래스들은 서로 긴밀한 관계를 맺고 상호작용을 한다.
어떤 클래스는 여러 클래스와 관계를 맺지만 어떤 클래스는 특정 클래스와 관계를 맺는다.
클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언 하는것이 좋으나
특정 클래스와 관꼐를 맺을 경우에는 관계 클래스를 클래스 내부에 선언하는 것이 좋다
중첩 클래스(Nested Class)란 클래스 내부에 선언한 클래스를 말하는데
중첩 클래스를 사용하면 두클래스의 멤버들을 서로 쉽게 접근 할수 있다는 장점과
외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.
class ClassName{
class NestedClassName{
} -> 중첩 클래스
}
인터페이스도 클래스 내부에 선언 할 수 있다
해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해
class ClassName {
interface NestedInterfaceName{
} -> 중첩 인터페이스
}
중첩 인터페이스 -> UI프로그래밍에서 이벤트 처리 목적
중첩 클래스
중첩 클래스는 클래스 내부에 선언 되는 위치에 따라 두가지로 분류
클래스 멤버 로서 선언 : 멤버 클래스 -> 객체 사용 중 언제든지 재사용 가능
메소드 내부에서 선언되는 중첩 클래스 : 로컬 클래스 -> 메소드 실행 시에만 사용, 실행 종료 사라짐
선언 위치에 따른 분류 | 선언 위치 | 설명 | |
멤버 클래스 | 인스턴스 멤버 클래스 | class A { class B {...} } |
A 객체를 생성해야만 사용 할수 있는 B중첩 클래스 |
정적 멤버 클래스 | class A { static class B{...} } |
A클래스로 바로 접근할수 있는 B중첩 클래스 | |
로컬 클래스 | class A { void method(){ class B{...} } } |
method()가 실행할 때만 사용할수 있는 B중첩 클래스 |
멤버 클래스도 하나의 클래스이기 때문에 컴파일 하면 바이트 코드 파일(.class) 별도로 생성 된다.
A $ B.class (바이트 코드 파일 이름)
A => 바깥 클래스
B => 멤버 클래스
로컬 클래스 일 경우에는 다음과 같이 $1 포함된 바이트 코드 파일이 생성
A $1 B.class
A => 바깥 클래스
B => 로컬 클래스
인스턴스 멤버 클래스
인스턴스 멤버 클래스는 static 키워드 없이 선언된 클래스를 말함
인스턴스 필드와 메소드만 선언 가능, 정적 필드 메소드 선언 불가.
Class A {
/*인스턴스 멤버 클래스*/
class B {
B() {} --- 생성자
int field1; ----- 인스턴스 필드
//static int field2; ----- 정적 필드 (x)
void method1() { } ------- 인스턴스 메소드
//static void method2() {} ------- 정적 메소드 (x)
}
}
A 클래스 외부에서 인스턴스 멤버 클래스 B의 객체를 생성하려면 먼저 A객체를 생성하고 B객체를 생성해야 한다.
A a = new A();
A.B b = a.new B();
b. field1 = 3;
b.method1();
정적 멤버 클래스
정적 멤버 클래스는 static 키워드 선언 클래스
모든 종류의 필드와 메소드 선언 가능
class A{
/*정적 멤버 클래스*/
static class C{
C() { } ---- 생성자
int field1; ----- 인스턴스 필드
static int field2; ----- 정적 필드
void method1() {} ------- 인스턴스 메소드
static void method2(){} ------ 정적 메소드
}
}
A클래스 외부에서 정적 멤버 클래스 C 의 객체를 생성하기 위해서 A 객체 생성 불필요
로컬 클래스
중첩 클래스는 메소드 내에서도 선언 가능
-> 로컬 클래스
로컬클래스는 접근제한자(public, private), static 선언 불가
로컬 클래스는 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없기 때문
로컬 클래스 내부는
인스턴스 필드와 메소드만 선언 가능
정적 필드, 메소드 선언 할 수 없다.
void method(){
/*로컬 클래스*/
class D{
D() {} -- 생성자
int field1; -- 인스턴스 필드
//static int field2; -- 정적 필드(x)
void method() {} -- 인스턴스 메소드
//static void method2() {} -- 정적 메소드(x)
}
D d = new D();
d.field1 = 3;
d.method1();
}
로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야 한다.
주로 다음과 같이 비동기 처리를 위헤 스레드 객체를 만들 때 사용
void method() {
class DownloadThread extends Thread {...}
DownloadThread thread = new DownloadThread();
thread.start();
}
중첩 클래스의 접근 제한
바깥 필드와 메소드에서 사용 제한
멤버 클래스가 인스턴스 또는 정적으로 선언됨
바깥 클래스의 필드와 메소드에 사용 제한이 생긴다.
public class A {
//인스턴스 필드
B field1 = new B(); --- (ㅇ)
C field2 = new C(); --- (ㅇ)
//인스턴스 메소드
void method1(){
B var1 = new B(); --- (ㅇ)
C var2 = new C(): --- (ㅇ)
}
//정적 필드 초기화
//static B field3 = new B(); --- (x)
static C field4 = new C(); ---(ㅇ)
//정적 메소드
static void method2() {
//B var1 = new B(); --- (x)
C var2 = new C(); ---(ㅇ)
}
// 인스턴스 멤버 클래스
class B {}
// 정적 멤버 클래스
static class C {}
}
멤버 클래스에서 사용제한
멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라
멤버 클래스 내부에서 바깥 클래스이 필드와 메소드를 접근 할 때에도 제한이 따른다.
인스턴스 멤버 클래스(B) 안에서는 바깥 클레스와 모든 필드와 모든 메소드에 접근 할 수있지만,
정적 멤버 클래스 (C)안에서는
바깥 클래스의 정적 필드(field2)와 메소드(method2())에 접근만 할 수있고
인스턴스 필드와 메소드(method()) 접근할 수 없다.
Class A {
int field1;
void method1() {...}
static int field2;
static void method2(){...}
class B {
void method(){
field1 = 10;
method1();
field2 = 10;
method2():
}
static class C {
void method(){
field1 = 10; -> (x)
method1(); -> (x)
field2 = 10;
method2();
}
}
}
}
로컬 클래스에서 사용 제한
로컬 클래스 내부에서는 바깥 클래스의 필드나 메소드를 제한없이 사용
메소드의 매개변수나 로컬 변수를 로컬클래스에서 사용 할때
로컬클래스의 객체는 메소드 실행이 끝나도 힙 메모리에 존재해서 계속 사용
매개변수나 로컬변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때뭉에 로컬 객체에서 사용할 경우 문제
-> 컴파일 시 로컬 클래스에서 사용하는 매개변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해두고 사용한다.
매개 변수나 로컬변수가 수정되어 값이 변경 되면
로컬클래스에 복사해 둔 값과 달라지는 문제를 해결하기 위해
매개변수나 로컬 변수를 final 로 선언 해서 막음
-> 로컬 클래스에서 사용 가능한 것은 final로 선언된 매개변수와 로컬변수
자바 7 이전까지는 final 키워드 없이 선언된 매개 변수나 로컬 변수를 로컬 클래스에서 사용하면
--> 컴파일 에러
But 자바 8부터 final 키워드 없이 선언되어도 여전히 값을 수정 할 수 없는 final의 특성을 갖는 다는 것
final 키워드 존재 여부의 차이점은 로컬 클래스의 복사 위치.
final 키워드가 있다면 로컬 클래스의 메소드 내부에 지역 변수로 복사
final 키워드가 없다면 로컬 클래스의 필드로 복사
void outMethod(final int arg1, int arg2){
final int var1 = 1;
int var2 = 2;
class LocalClass {
void method() {
int result = arg1 + arg2 + var1 + var2;
}
}
}
class LocalClass {
int arg2 = 매개값; -> 필드로 복사
int var2 = 2
void method() {
int arg1 = 매개값; -> 로컬 변수로 복사
int var1 = 1;
int result = arg1 + arg2 + var1 + var2;
}
}
로컬 클래스에서 사용된 매개변수와 로컬 변수는 모두 final 특성을 갖는다.
중첩 클래스에서 바깥 클래스 참조 얻기
클래스 내부에서 this는 객체 자신의 참조이다
중첩 클래스에서 this 키워드를 사용하면 바깥 클래스의 객체 참조 아니라. 중첩 클래스의 객체 참조
중첩 클래스 내부에서 this.필드.this.메소드() 로 호출
중첩 클래스의 필드와 메소드가 사용
중첩 클래스 내부에서 바깥 클래스의 객체 참조 얻으려면 바깥 클래스의 이름을 this 앞에 붙여주면 된다.
다음은 바깥 클래스의 필드와 메소드에 접근하기 위해 바깥 클래스 this 사용
바깥클래스.this.필드
바깥클래스.this.메소드();
public class Outer {
String field = "Outter-field"
void method(){
System.out.println("Outter-method");
}
class Nested {
String field = "Nested-field";
void method(){
System.out.println("Nested-method");
}
void print(){
System.out.prontln(this.field);
this.method();
System.out.println(Outter.this.field);
Outter.this.method();
}
}
}
중첩 인터페이스
중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스
해당클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해
특히 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용
Class A {
interface I {
void method();
}
}
public class Button {
OnClickListner listner; --- 인터페이스 타입 필드
void setOnClickListener(OnclickListener listener){
this.listener = listener; ---------- 매개 변수의 다형성
}
void touch(){
listener.onClick(); --- 구현 객체의 onClick() 메소드 호출
}
interface OnClickListener{
void onClick(); ---- 중첩 인터페이스
}
}
public class CallListener implements Button.OnClickListener {
@Oveeride
public void onClick(){
System.out.println("전화를 겁니다");
}
}
public class ButtonExample {
public static void main(String[] args){
Button btn = new Button();
btn.setOnClickListener(new CallListener());
btn.touch();
}
}
익명 객체
익명 객체는 이름이 없는 객체
익명 객체는 단독으로 생성 불가
클래스 상속, 인터페이스 구현
필드의 초기값이나 로컬 변수의 초기값, 매개 변수의 매개값으로 주로 대입
익명 자식 객체 생성
부모 타입 필드 변수 선언, 자식 객체 초기값 대입
부모클래스 [필드|변수] = new 부모클래스(매개값, ,,,) {
//필드
// 메소드
};
class A{
Parent field = new Parent(){ A 클래스의 필드 선언
int childField
void childMethod(){}
@Override
void parentMethod(){} -- Parent의 메소드를 오버라이딩
}
void method(){
Parent localVar = new Parent(){ -- 로컬 변수 선언
int childField;
void childMethod(){}
@Override
void parentMethod(){} -- Parent의 메소드를 오버라이딩
}
}
void method1(Parent parent){}
void method2(){
method1( --- method01() 메소드 호출
new Parent(){ --- method1() 매개값으로 익명 자식 객체를 대입
int childField;
void ChildMethod(){}
@Override
void parentMethod(){}
}
)
}
}
일반 클래스와의 차이점 - 생성자 생성 X
부모클래스(매개값, ...){} 부모 클래스를 상속해서 중괄호 {} 와 같이 자식클래스를 선언
new 연산자는 자식 클래스를 객체 로생성
부모클래스(매개값, ...){} 는 부모 생성자를 호출하는코드
매개값은 부모 생성자의 매개 변수에 맞게 입력
중괄호 내부에는 필드나 메소드를 선언, 부모 클래스의 메소드를 재정의(오버라이딩) 하는 내용
익명 자식 객체에서 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용
외부에서는 필드와 메소드에 접근 불가
부모타입변수에 대입 되므로 부모타입에 선언된것만 사용
class A{
Parent field = new Parent(){
int childField;
void childMethod(){}
@Override
void parentMethod(){
childField = 3 ;
childMethod();
}
}
void Method(){
field.childField= 3 ; --- x
field.childMethod(); --- x
field.parentMethod(); --- ㅇ
}
}
익명 구현 객체 생성
인터페이스 타입으로 필드나 변수를 선언 , 구현 객체를 초기값으로 대입
인터페이스 [필드 | 변수] = new 인터페이스(){
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
// 필드
// 메소드
}
인터페이스(){}는 인터페이스를 구현해서 중괄호{} 같이 클래스를 선언 하라는 뜻
new 연산자는 이렇게 선언된 클래스를 객체로 생성
중괄호{}에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성
-> 컴파일 에러
필드와 메소드를 선언 가능
실체 메소드에서만 사용 가능
외부에서는 사용 X
Class A {
RemoteControl field = new RemoteControl(){ 클래스 A의 필드 선언
@Override -- RemoteControl인터페이스의 추상 메소드에 대한 실체 메소드
void turnOn(){} -- 추상 메소드에 대한 실체 메소드
}
void method(){
RemoteControl localVar = new RemoteControl(){ 로컬 변수 선언
@Override
void turnOn() retmote 인터페이스 추상 메소드에 대한 실체 메소드
}
}
void method1(RemoteControl rc){}
void method2(){
method1( --- mehtod1() 메소드 호출
new RemoteControl(){ method1()의 매개값으로 익명 구현 객체를 대입
@Override
void turnOn(){}
}
);
}
}
익명 객체의 로컬 변수 사용
익명 객체 내부에서는 바깥 클래스의 필드나 메소든는 제한 없이 사용할수 있다.
메소드의 매개변수나 로컬변수를 익명 객체에서 사용 할때.
메소드 내에서 생성된 익명 객체는 메소드 실행이 끝나도 힙 메모리에서 존재해서 계속 사용
매개 변수나 로컬 변수는 메소드 실행 끝나면 스택 메모리에서 사라짐
익명 객체 내부에서 메소드의 매개 변수나 로컬 변수를 사용 할 경우,
final 특성을 가져야함
자바 7 -> final 키워드로 선언
자바 8 -> 선언 하지 않아도 final 특성을 가짐
컴파일시 final 키워드가 있다먄 메소드 내부에 지역 변수로 복사
final 키워드가 없다면 익명 클래스의 필드로 복사
void outMethod(final int arg1, int arg2){
final int var1 = 1;
int var2 = 2;
인터페이스 변수 = new 인터페이스(){
void method(){
int result - arg1 + arg2 + var1 +var2
}
}
}
익명 객체에서 사용된 매개변수와 로컬 변수는 모두 final 특성을 갖는다.
'Back-end > 이것이 자바다[신용권 한빛미디어]' 카테고리의 다른 글
자바 API 도큐먼트 (0) | 2021.10.22 |
---|---|
예외 처리 (0) | 2021.10.22 |
인터페이스 (0) | 2021.10.09 |
추상 클래스 (0) | 2021.10.04 |
타입 변환과 다형성 (0) | 2021.10.04 |