목차
1. 중첩클래스란?
2. 왜 사용하는가?
3. 종류
3.1 Member Inner Class
3.2 Anonymous inner class(익명 클래스)
3.3 local inner class(내부 클래스)
3.4 static nested inner class
1. 중첩클래스란?
객체 지향 프로그램에서 클래스들은 서로 긴밀한 관계를 맺고 상호작용을 한다.
중첩 클래스란 클래스 내부에 선언한 클래스를 말하는데 쉽게 말해 클래스내에 또 다른 클래스를 구성함을 뜻한다.
※ 주의 : 메소드 내에는 다른 메소드를 정의 할 수 없음!!
class A{ //외부클래스, Outer클래스, Top-level클래스
//필드
//메소드
//1차 자원 정의
int i=11;
void hello(){
print(); (X)
B b = new B();
b.print(); (O)
}
class B{ //내부클래스, Inner클래스
//2차 자원 정의
void callTest(){
hello(); (O)
}
void print(){
}
}//B class
}//A class
저장: A.java ----> 컴파일 결과 ---> A.class A$B.class
2. 왜 사용하는가?
1. 이너클래스는 외부 클래스의 모든 멤버들에 접근할 수 있습니다. 즉, 접근성이 좋다.
2. 논리적인 그룹을 나타낼 때 쓴다. 주로 model 객체에서 상위모델과 하위모델이 필요할 때 쓴다.
(Static Nested Class를 많이 사용한다.)
3. 코드 최적화: 외부에 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다. 복잡성이 줄어들면 자연스럽게 가독성이 좋아진다.
1. 한 장소에서만 사용되는 클래스를 논리적으로 그룹화하는 방법이다. 한 클래스가 다른 클래스에서만 유용 할 때 다른 클래스에 해당 클래스를 포함시키고 두 클래스를 하나의 클래스에 유지하는 것이 논리적이다. 이러한 "helper classes" 중첩은 패키지를 더욱 간소하게 만든다.
2. 캡슐화가 증가한다. A와 B 두 가지 외부 클래스가 있을 때, B는 A의 멤버로 접근해야 한다. 그렇지 않다면 private으로 선언되어야 한다. 클래스 A에 클래스 B를 숨기려면, A의 멤버를 private으로 선언하면 B가 액세스 할 수 있습니다. 또한 B 자체는 외부 로부터 숨겨진다.
3. 읽기 쉽고 유지 관리가 쉬운 코드가 된다. 외부 클래스 내에 내부 클래스를 중첩하면 코드가 사용되는 위치가 더 가깝게 배치된다.
※ 참조 : https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html의 Why Use Nested Classes?
※ 참고 : Class에서 멤버는 변수와 메소드를 뜻한다.
3. 종류
Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.
참조: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html 의 용어정의
중첩클래스는 2가지 타입
1. 정적 중첩 클래스(static nested class) 외부의 자원을 사용할 때 static붙는 인스턴스만 접근가능
2. 비정적 중첩 클래스(inner class)
상세종류표
No. | Name | Description |
1 |
Member Inner Class (Instance Class) |
외부 클래스의 멤버변수 선언위치에 선언, 외부 클래스의 인스턴스멤버처럼 다루어진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용된다. |
2 | Static Nested Class |
외부 클래스의 멤버변수 선언위치에 선언 외부 클래스의 static 멤버처럼 다루어진다. 주로 외부 클래스의 static멤버, 특히 static메서드에서 사용될 목적으로 선언 |
3 | Local Inner Class |
외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다. |
4 | Anonymous Inner Class |
클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스 |
정의 되는 위치로 나누어 보는 멤버클래스와 지역클래스
♣ 멤버클래스
1. 멤버 변수와 동일한 위치에 선언
2. 동일한 클래스 뿐만 아니라 다른 클래스에서도 활용 가능
3. 클래스의 멤버 변수와 성격이 비슷
static 예약어가 붙은 static 멤버(Static Nested Class) | static 예약어가 없는 instance 멤버(Member Inner Class) |
♧ 지역클래스
1. 메서드 내에서 정의
2. 활용범위가 메서드 블록 내부로 제한되는 특징(지역변수와 성격이 비슷)
이름이 붙음(Local Inner Class) | 이름이 없음(Anonymous Inner Class) |
1. Member inner class
정의
1. 멤버 변수를 선언하는 위치에서 정의하는 내부 클래스
2. 외부 클래스의 인스턴스가 생성되어야만 객체 생성이 가능
class TestMemberOuter1{
private int data = 30;
class Inner{
void msg(){System.out.println("Data is" + data);}
}
public static void main(String args[]){
TestMemberOuter1 obj = new TestMemberOuter1();
TestMemberOuter1.Inner in = obj.new Inner();
in.msg();
}
}
내부 동작
자바 컴파일러는 이너 클래스의 케이스 안에 2개의 클래스 파일을 만든다.
이너 클래스의 클래스 파일 이름은 "Outer$Inner" 이다.
만약 이너클래스를 생성하길 원한다면, 외부 클래스의 인스턴스를 만들어야 한다.
외부 클래스의 인스턴스 안에 이너 클래스의 인스턴스가 만들어 진다. 즉, 멤버 이너 클래스는 외부 클래스의 참조를 가진다. 그렇기 때문에 외부 클래스의 모든 데이터 멤버에 접속 할 수 있다.
import java.io.PrintStream;
class Outer$Inner
{
final Outer this$0;
Outer$Inner()
{ super();
this$0 = Outer.this;
}
void msg()
{
System.out.println((new StringBuffer()).append("Data is")
.append(Outer.access$000(Outer.this)).toString());
}
}
2. static nested class
정의
1. 정적 중첩 클래스라고 불리는 클래스 안에서 만들어 진다.
2. 동적 데이터 맴버와 메소드에는 접속할 수 없다.
3. private을 포함하는 외부 클래스의 정적 데이터 멤버에 접속 할 수 있다.
인스턴스 메소드와 자바 정적 중첩 클래스의 예시
class TestOuter1{
static int data=30;
static class Inner{
static void msg(){System.out.println("Data is" + data);}
}
public static void main(String args[]){
TestMemberOuter1.Inner obj = new TestOuter1.Inner();
obj.msg();
}
}
정적 중첩 클래스의 인스턴스를 만들 필요가 있는데 이 클래스는 인스턴스 메소드 msg()를 가지고 있기 때문입니다.
컴파일러에 의한 내부 코드 발생
import java.io.PrintStream;
static class TestOuter1$Inner
{
TestOuter1$Inner(){}
void msg()
{
System.out.println((new StringBuffer()).append("Data is")
.append(TestOuter1.data).toString());
}
}
중첩 클래스가 정적이고 외부 클래스의 객체를 만들 필요가 없다면, 메소드나 클래스에 객체 없이 접근 할 수 있습니다.
class TestOuter2{
static int data=30;
static class Inner{
static void msg(){System.out.println("Data is" + data);}
}
public static void main(String args[]){
TestOuter2.Inner.msg();//정적 중첩 클래스의 인스턴스를 만들 필요가 없습니다.
}
}
3.local inner class
정의
1. 지역변수처럼 사용
2. 로컬 내부 클래스 메모리 생성시점: 메소드가 호출될 때
3. 로컬 내부 클래스 메모리 소멸시점: 메소드가 종료될 때
4. 외부클래스의 멤버변수와 상수값만 접근 가능
5. 로컬 내부 클래스가 정의된 메소드내의 로컬변수에는 접근 할 수 없다.
6. 객체 생성은 해당 클래스가 포함된 메소드 내에서만 가능
구성형식
class OuterClass{
...
public void method(){ //멤버 메소드
class InnerClass{
}
}
...
}
4.Anonymous inner class(익명 클래스)
정의
클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한번만 사용될 수 잉ㅆ고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스
특징
- 이름이 없기 때문에 생성자를 가질 수 없다.
- 조상클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의
- 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다. 즉, 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다.
사용이유
일반 클래스를 만들고, 그 클래스를 호출하면 그 정보는 메모리에 올라간다.
즉, 클래스를 많이 만들면 만들수록 많은 메모리가 필요해지고,
애플리케이션을 시작할 때 더 많은 시간이 걸리게 됩니다.
별도의 클래스를 정의하지 않으면 로딩시간이 짧아진다.
예제
class InnerEx6{
Object iv = new Object() { void method(){} };
static Object cv = new Object() { void method(){} };
void myMethod(){
Object lv = new Object(){ void method(){} );
}
}
컴파일
InnerEx6.class
InnerEx6$1.class
InnerEx6$2.class
InnerEx6$3.class
→ '외부클래스명$숫자.class' 형태
예제(독립된 클래스 → 익명 클래스)
import java.awt.*;
import java.awt.event.*;
class InnerEx7{
public static void manin(String[] args) {
Button b = new Button("Start");
b.addActionListener(new EventHandler());
}
}
class EventHandler implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("ActionEvent occuered!");
}
}
------------------------------------------------------
import java.awt.*;
import java.awt.event.*;
class InnerEx8{
public static void manin(String[] args) {
Button b = new Button("Start");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("ActionEvent occuered!");
}
}
);
}//main
}// InnerEx8
Kotlin
내부(inner) 클래스와 중첩(nested) 클래스
자바에서는 A 클래스 안에 B 클래스를 정의하면 B 클래스는 자동으로 내부 클래스가 되었지만 코틀린에서는 반대
방법
한 클래스안에 다른 클래스를 정의하면 기본적으로는 중첩 클래스가 되고, 내부 클래스로 만들고 싶다면 inner 키워드로 클래스를 선언
예시
내부 클래스는 기본적으로 외부 클래스를 참조
중첩 클래스는 그렇지 않다.
// nested class 중첩 클래스
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
위와 같이 중첩 클래스에서는 외부 클래스를 참조하지 않기 때문에 Outer.Nested().foo()의 값이 2가 된다.
// inner class 내부 클래스
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
반면, 내부 클래스에서는 외부 클래스를 항상 참조하고 있기 때문에 Outer().Inner().foo()의 값이 1이 된다.
즉, 내부 클래스를 사용하면 항상 외부 클래스의 객체를 참조하기때문에 객체가 적절한 시점에 삭제되지 못하고 메모리 누수가 발생한다.
누수가 위험한 이유
명시적(컴파일 시 오류가 발생하는 등 명식적으로 알 수 있는 것)인 것이 아니라 암묵적(프로그래머가 발견하기 전까지는 알 수 없는 것)인 것이기 때문에 특별한 경우가 아니라면 내부 클래스 사용을 지양하고 중첩 클래스를 사용하는 것이 권장
From코틀린 공식문서와 코틀린 인 액션 책을 참고하여 작성
참고할만한 내용
내부클래스의 변수 선언
1. 멤버변수: 클래스에서 선언된 변수
클래스 내부 어디에서나 사용 가능
수식어(public, private, static, etc.) 사용 가능
2. 지역변수: 메소드 안에서 선언하는 변수
지역변수가 선언된 곳에서부터 변수가 속한 블록이 끝나는 곳까지 사용 가능
접근 수식어를 사용 불가
final은 사용 가능
<접근지정자> Access Control
1. public - 서로 다른 패키지에서도 접근 가능.
2. protected - 서로 같은 패키지에서 접근 가능.
(서로 다른 패키지라도 상속관계에 있는 자식클래스에서는 접근가능)
3. default(생략) - 서로 같은 패키지에서 접근 가능.
4. private - 같은 클래스내에서만 접근 가능.
(같은 클래스내의 멤버끼리만 호출가능 - 외부클래스에서는 호출불가, 정보은
닉)
[접근지정자 (public,생략)] class 클래스명{
[접근지정자 4개] 자료형 변수명;
[접근지정자 4개] 리턴형 메소드명(){
}
참조
https://onsil-thegreenhouse.github.io/programming/java/2017/12/12/java_tutorial_1-16/
https://shinjekim.github.io/kotlin/2019/08/29/Kotlin
https://c10106.tistory.com/1866
https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
[Java의 정석] 남궁 성 지음 도우출판사
'JAVA' 카테고리의 다른 글
#11 PersonMenu MVC, Thread(스레드) (0) | 2018.12.10 |
---|---|
#10 AWT vs Swing,계산기 만들기 (0) | 2018.12.05 |
#9 Event 처리 (0) | 2018.12.02 |
#8 중첩클래스, TUI, AWT, 데이터저장 (0) | 2018.11.26 |
#7 배열사용이유, 배열(Vector), Exception(예외처리), Matches, StringTest (0) | 2018.11.25 |