네스티드Nested ( nest, 둥지를 틀다. 새 둥지) 는 중첩을 의미합니다.
클래스 안에 들어있는 클래스를 네스티드 클래스라고 합니다.
네스티드 클래스 안에는 Static 클래스와 Non-Static 클래스가 있습니다.
Static 클래스의 경우에는 정적 클래스
Static 클래스가 아닐 경우에는 Non-Static 네스티드 클래스 또는 이너 클래스라고 합니다.
이너 클래스는 다시 멤버 클래스, 로컬 클래스, 익명 클래스로 나뉘어집니다.
- 멤버 클래스는 클래스 안에서 멤버 역할을 하는 클래스
- 로컬 클래스는 메소드 안에 만들어진 클래스 (메소드 안의 영역을 로컬이라고 합니다.)
- 익명 클래스는 클래스를 일회성으로 재정의해서 사용하는 클래스
● Static 네스티드 클래스
Static 네스티드 클래스는 static 선언이 갖는 특성이 반영된 클래스입니다.
따라서 자신을 감싸는 외부 클래스의 인스턴스와 상관없이 Static 네스티드 클래스의 인스턴스 생성이 가능합니다.
package day19;
/*
* 클래스 멤버는 인스턴스 멤버에 접근할 수 없다.
* 클래스 멤버에서는 인스턴스 멤버의 존재 여부를 보장할 수 없기 때문이다.
*/
class Outer{
static int num = 10; // 스테틱 멤버는 인스턴스 멤버에 대한 직접적 접근 불가, 그 반대는 가능.
static class INum { //아우터 안의 스테틱 클래스. 정적인 클래스이기 때문에 아우터의 인스턴스가 없어도 바로 사용할 수 있습니다. 아이넘에 접근 가능
void showNum() {
System.out.println(num);
}
}
}
public class Nested01 {
public static void main(String[] args) {
Outer.INum obj = new Outer.INum();
obj.showNum();
}
}
/
클래스 멤버는 인스턴스 멤버의 존재 여부를 보장할 수 없기 때문에 인스턴스 멤버에 접근할 수 없습니다.
Static 네스티드 클래스는 외부 클래스의 메소드에도 접근할 수 없습니다.
● 이너 클래스의 구성에 대해서 알아볼게요.
○ 멤버 클래스 (Member Class)
→ 인스턴스 변수, 인스턴스 메소드와 동일한 위치에 정의
class Outer {
class MemberInner {...} // 멤버 클래스
○ 로컬 클래스 (Local Class)
→ 중괄호 내에, 특히 메소드 내에 정의
void method() {
class LocalInner {...} // 로컬 클래스
}
}
○익명 클래스 (Anonymous Class)
→ 클래스인데 이름이 없음.
각각의 클래스를 살펴볼게요.
(1) 멤버 클래스
멤버 클래스의 인스턴스는 외부 클래스의 인스턴스에 종속적입니다.
class Outer {
private int num = 0;
class Member { // 멤버 클래스의 정의
void add(int n) { num += n; }
int get() { return num; }
}
}
class MemberInner {
public static void main(String[] args) {
Outer o1 = new Outer();
Outer o2 = new Outer();
// o1 기반으로 두 인스턴스 생성
Outer.Member o1m1 = o1.new Member();
Outer.Member o1m2 = o1.new Member();
// o2 기반으로 두 인스턴스 생성
Outer.Member o2m1 = o2.new Member();
Outer.Member o2m2 = o2.new Member();
// o1 기반으로 생성된 두 인스턴스의 메소드 호출
o1m1.add(5);
System.out.println(o1m2.get());
// o2 기반으로 생성된 두 인스턴스의 메소드 호출
o2m1.add(7);
System.out.println(o2m2.get());
}
}
멤버 클래스는 클래스의 정의를 감추어야 할 때 유용하게 사용이 됩니다.
가령 메인 클래스에서 인스턴스를 생성해서 사용한 클래스를 감출 때 사용합니다.
//멤버 클래스 Bean을 포함한 외부 클래스 Coffee
class Coffee{
void getCoffee() {
new Bean().thisIs();
}
class Bean{
void thisIs() {
System.out.println("에티오피아 원두");
}
}
}
public class Nested01 {
public static void main(String[] args) {
Coffee c = new Coffee();
c.getCoffee();
}
}
(2) 로컬 클래스
로컬 클래스는 멤버 클래스와 상당부분 유사하고, 지역 내에 정의되는 점에서만 차이가 있습니다.
class Bag{ // 가방 클래스 안에
void getPencil() { // 겟 펜슬 메소드 (지역 변수)
class Pencil{ // 그 안의 펜슬 클래스
public String toString() { // 그 안의 연필 값
return "연필";
}
}
Pencil p = new Pencil(); // 겟 펜슬 메소드 안에서만 쓸 수 있는 펜슬
System.out.println(p);
}
// Pencil p = new Pencil(); 겟 펜슬 메소드 바깥에서는 사용할 수 없음.
}
public class Nested01 {
public static void main(String[] args) {
new Bag().getPencil();
}
}
(3) 익명 클래스
일회성으로 새롭게 정의하여 사용하는 클래스입니다.
한 번 구현하고 마는 중괄호의 표현입니다.
사용법을 로컬 클래스와 비교해서 확인해볼게요.
로컬 클래스 | 익명 클래스 |
public Printable getPrinter() { class Printer implements Printable { // 로컬 클래스 Printer의 정의 public void print() { System.out.println(con); } } return new Printer(); // Printer 인스턴스의 생성 } |
interface Printable { void print(); } public Printable getPrinter() { return new Printable() { // 익명 클래스의 정의와 인스턴스 생성 public void print() { System.out.println(con); } }; } |
즉, 이름을 정의하지 않은 채 직접적으로 클래스를 작성함으로써 일회성의 클래스르 사용하는 것이에요.
package day19;
interface Player{
void training();
}
public class Nested02 {
public static void main(String[] args) {
// 인터페이스 플레이어의 추상 메소드가 정의되지 않아서 사용할 수 없는데
//그것을 인스턴스 생성시에 같이 일회성으로 정의를 하는 것.
Player son = new Player() {
public void training() {
System.out.println("드리블 연습.");
}
}; // 익명 클래스 정의 뒤에는 세미콜론이 필수 입니다.
Player park = new Player() {
public void training() {
System.out.println("변화구 연마.");
}
};
son.training();
park.training();
}
}
마지막으로 사용의 예시에 대해서 한 번 더 확인을 해볼게요.
왼쪽은 익명 클래스를 사용하지 않을 때고 오른쪽은 익명 클래스를 사용할 것입니다.
class StrComp implements Comparator<String> { @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); } } class SortComparator { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("ROBOT"); list.add("APPLE"); list.add("BOX"); StrComp cmp = new StrComp(); Collections.sort(list, cmp); System.out.println(list); } } |
class AnonymousComparator { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("ROBOT"); list.add("APPLE"); list.add("BOX"); Comparator<String> cmp = new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); } }; Collections.sort(list, cmp); System.out.println(list); } } |
두 코딩의 결과는 같습니다.
이후에 람다를 배울 때 인터페이스의 익명 클래스의 구현과 유사한 점이 있기 때문에 숙지해두는 것이 좋습니다.
'자바 > 자바 입문 공부일지' 카테고리의 다른 글
자바 기초 공부 일지 47. 람다와 함수형 인터페이스 (0) | 2022.11.04 |
---|---|
자바 기초 공부 일지 46. 람다lambda (0) | 2022.11.03 |
자바 기초 공부 일지 44. 열거형 (0) | 2022.11.03 |
자바 기초 공부 일지 43. ArrayList 정렬하기, Comparable과 Comparator, Comparator<T> (0) | 2022.11.03 |
자바 기초 공부 일지 42. 컬렉션 클래스 (5) 맵HashMap<K, V> (0) | 2022.11.03 |