외부 클래스 ( Outer Class )

  -> 내부 클래스를 감싸는 클래스

 

내부 클래스 ( Inner Class )

  -> 클래스 내 선언 되어있는 클래스

  -> 네스티드 클래스 ( Nested Class ) 라고도 부름

 

아래 코드로 외부, 내부 클래스를 이해하자

(1). 외부 클래스

(2). 내부 클래스

 

외부 클래스는 우리가 지금까지 사용했던 그냥 클래스임

그래서 외부 클래스를 두고 굳이 외부 클래스라고 부르지 않고 그냥 클래스 라고 부름


내부 클래스는 두 가지 기준에 따라 종류가 나뉨

 

Static 키워드 여부에 따라 나눈 내부 클래스

  -> Non-Static Inner Class

  -> Static Inner Class

내부 클래스가 선언된 위치에 따라 나눈 내부 클래스

  -> Member Inner Class

  -> Local Inner Class

  -> Anonymous Inner Class

 

< Static 키워드 여부에 따라 나눈 내부 클래스 >

1. Non-Static Inner Class : static 키워드가 붙지 않은 내부 클래스

더보기

package jpaStudy;

public class Outer {
	// Outer 클래스의 멤버 변수, 메서드, Getter, Setter 등
	
	class NonStaticNestedClass {
		// static nested class의 멤버 변수, 메서드, Getter, Setter 등
		
	}
}

 

2. Static Inner Class : static 키워드가 붙은 내부 클래스

더보기

package jpaStudy;

public class Outer {
	// Outer 클래스의 멤버 변수, 메서드, Getter, Setter 등
	
	static class StaticNestedClass {
		// static nested class의 멤버 변수, 메서드, Getter, Setter 등
		
	}
}

 

 

 

< 내부 클래스가 선언된 위치에 따라 나눈 내부 클래스 >

1. Member Inner Class : 멤버 변수, 메서드를 선언하는 위치인 클래스 내에 선언된 내부 클래스

더보기

아래 Outer 클래스와 Inner 클래스가 있음

Inner 클래스는 Outer 클래스 내에 있고 Outer 클래스의 구성 요소인 멤버 변수, 메서드와 같은 위치에 선언되어있음

이러한 내부 클래스를 Member Inner Class 라 함

package jpaStudy;

public class Outer {
	private int outerField;
	
	public void outerMethod() {
		// 어떤 코드1
		// ...
		// 어떤 코드n
	}
	
	public int getOuterField() { return outerField; }

	public void setOuterField(int outerField) { this.outerField = outerField; }

	class Inner {
		private int innerField;
		
		public void increseOuterField(int n) {
			outerField = outerField + n;
		}
		
		public int getOuterField() {
			return outerField;
		}
	}
}

특히 이 Member Inner Class의 경우 외부 클래스의 멤버 변수에 접근할 수 있는 점이 특징임

 

이제 내부 클래스를 사용해 객체를 생성해보자

이때 내부 클래스에 static 키워드가 붙지 않았기 때문에 내부 클래스를 사용해 객체를 생성하려면 우선 외부 클래스로 객체를 생성해야함

그 후 외부 클래스로 생성한 객체를 사용해 내부 클래스를 사용해 객체를 생성할 수 있음

 

package jpaStudy;

public class Example {
	public static void main(String[] args) {
		Outer o1 = new Outer();
		Outer o2 = new Outer();
		
		Outer.Inner o1i1 = o1.new Inner();
		Outer.Inner o1i2 = o1.new Inner();
		
		Outer.Inner o2i1 = o2.new Inner();
		Outer.Inner o2i2 = o2.new Inner();
		
		o1i1.increseOuterField(3);
		System.out.println(o1i2.getOuterField());
		
		o2i1.increseOuterField(5);
		System.out.println(o2i2.getOuterField());
	}
}

< 코드 설명 >

(1). 위에서 언급한대로 Outer 클래스가 갖고 있는 Inner 클래스를 사용해 객체를 생성하거나 인스턴스를 생성하기 위해서는 반드시 Outer 클래스의 객체가 필요함
  Outer 클래스의 객체 없이 Inner 클래스를 사용해 객체를 생성하려면 Inner 클래스를 Static Inner Class로 만들어주면 됨
  단, Static Inner Class가 되면 멤버 변수에 접근하거나 메서드를 호출 할 때 제약이 생김

(2). o1 객체를 사용해 Outer 클래스가 갖고 있는 Inner 클래스 객체를 두 개 생성했음

  이럴 경우 o1i1, o1i2 두 객체는 한 객체(o1)을 통해 만들었기 때문에 o1 객체가 갖고 있는 멤버 변수를 공유해서 사용함

  즉, o1i1 객체를 통해서 o1 객체가 갖고 있는 outerField 멤버 변수에 접근할 수 있고 o1i2 객체를 통해서 o1 객체가 갖고 있는 outerField 멤버 변수에 접근할 수 있음

  당연한 얘기지만 o1 객체를 통해서도 같은 outerField 멤버 변수에 접근할 수 있음

(3). o1i1 객체를 통해서 outerField 멤버 변수의 값을 3 증가 시켰음 그 후 o1i2 객체를 통해서 outerField 멤버 변수의 값을 가져와 출력하고 있음

  이를 통해 o1i1, o1i2 두 객체가 같은 outerField 멤버 변수에 접근 하고 있다는걸 알 수 있음


Member Inner Class 는 클래스의 정의를 감추고 싶을 때 사용함

개발자가 직접 Member Inner Class 를 선언하고 활용하는 경우는 드물지만 자바의 Iterator에서 Member Inner Class를 활용함

 

2. Local Inner Class : if, while, 메서드 등 코드 블록 내에 선언된 내부 클래스

  내부 클래스 자체가 사용 빈도가 떨어지는데 Local Inner Class 의 경우 사용 빈도가 더 떨어짐

  따라서 Local Inner Class 가 있다 정도로만 생각하고 넘어가자

 

3. Anonymous Inner Class : 인터페이스 또는 추상클래스의 구현과 동시에 인스턴스를 생성할 때 사용하는 인스턴스의 타입으로 사용되는 클래스

더보기

Interface는 당연히 인스턴스를 생성할 수 없다는 것을 잘 알 것

다음과 같은 Interface가 있을 경우 당연히 이 Interface를 구현하는 클래스가 별도로 있을 것

package jpaStudy;

public interface Printable {
	void print();
}

 

그러나 Anonymous Inner Class 를 사용하면 Interface 를 구현하는 클래스 없이 Interface 구현과 동시에 인스턴스를 생성할 수 있음

package jpaStudy;

public class Sample {
	public static void main(String[] args) {
		Printable p = new Printable() {
			@Override
			public void print() {
				System.out.println("익명 클래스를 활용해");
				System.out.println("인터페이스 구현과 동시에 객체 생성");
			}
		};
		
		p.print();
	}
}

< 코드 설명 >

위 main 코드에서 익명 내부 클래스는 파란색 네모 쳐진 부분임
잘 알고 있듯 원래 인터페이스는 인스턴스를 생성할 수 없음
지금이 설명에 사용된 코드도 인터페이스로 인스턴스를 생성한 게 아님

코드만 보기에는 인터페이스로 인스턴스를 생성한 것처럼 보이지만 익명 클래스를 사용해 인터페이스를 구현하고 그 익명 클래스를 사용해 구현된 print 메서드를 갖고 있는 인스턴스가 생성 된 것

 

이런 형태의 코드는 특히 안드로이드를 개발할 때 많이 활용됨

즉, 익명 클래스가 특별한게 아닌 일반적이라는 것

 

또 다른 예시를 보고 익명 클래스를 마무리 하자

package jpaStudy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Sample {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("ROBOT");
		list.add("APPLE");
		list.add("BOX");
		
		System.out.println(list);
		
		Collections.sort(list, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.length() - o2.length();
			}
		});
		
		System.out.println(list);
	}
}


여기까지 외부 클래스와 내부 클래스에 대해서 배웠음

여기서 배운 것 중 특히, 익명 클래스가 중요함

 

자바로 안드로이드 개발하면 많이 보게 될 코드이기도 하고

자바로 웹 개발을 할 때는 익명 클래스를 활발히 사용하진 않지만 이제부터 배울, 그리고 자바에서는 없어선 안될 람다(Lambda)의 형태와 익명 클래스의 형태가 비슷하게 생겼기 때문에 람다를 친숙하게 만들어줌

 

여기서 배운 것들 중 익명 클래스가 람다 코드를 친숙하게 만들어 준다 외에 크게 중요한 점은 없음

728x90
LIST

'자바 > Chap10. 내장 클래스와 람다' 카테고리의 다른 글

Chapter10. 람다(Lambda) - 기초  (0) 2023.08.21