- jpa
- 데이터베이스
- spring security 6
- select
- Docker
- session
- spring mvc
- string
- 스프링부트
- 문자열
- 1차원 배열
- DI
- sql
- ORM
- spring boot
- AWS
- PYTHON
- mysql
- springboot
- 스프링
- SSL
- spring
- 프로그래머스
- java
- 자바
- Django
- @transactional
- Git
- nginx
- join
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
개발하는 자몽
내부 클래스 (Inner Class) 본문
내부 클래스
클래스가 다른 클래스를 포함하는 경우, 내부에 있는 클래스를 내부 클래스라고 한다. 내부 클래스로 선언 시 두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있으며, 외부에는 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다(캡슐화).
내부 클래스는 정의되는 위치에 따라 멤버 클래스와 지역 클래스로 나뉜다. 그리고 각 내부 클래스의 선언 위치에 따라 같은 선언 위치의 변수와 동일한 유효 범위(scope)와 접근성(accessibility)을 갖는다.
멤버 클래스
- 멤버 변수와 동일한 위치에 선언된 내부 클래스
- `static` 예약어가 붙은 static 멤버와 instance 멤버로 나뉨
- 동일한 클래스뿐만 아니라 다른 클래스에서도 활용 가능
- 클래스의 멤버 변수와 성격이 비슷함
지역 클래스
- 메서드 내에 클래스가 정의되어 있는 경우
- 이름이 있는 지역 클래스와 이름이 없는 무명 클래스로 나뉨
- 활용 범위가 메서드 블록 내로 제한되는 특징을 갖는 등 지역 변수와 성격이 비슷함
멤버 클래스 > instance 클래스
- instance 멤버 클래스는 클래스의 멤버와 동일한 위치에서 선언되어 외부 클래스의 instance 멤버처럼 다루어짐
- 멤버 변수/메서드와 동일한 위치에서 선언되었기 때문에 다른 외부 클래스에서 사용 가능
- 주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용
- 내부 클래스의 객체 생성을 위해 외부 클래스 객체 생성 필요
class Outer { //외부 클래스
public class Inner { //내부 클래스 : 멤버 변수와 동일 위치에 선언
...
}
}
public class InnerClassTest {
public static void main(String args[]) {
Outer outer = new Outer(); //내부 클래스의 객체 생성을 위해 외부 클래스 객체 생성
Outer.Inner inner = outer.new Inner();
}
}
멤버 클래스 > static 클래스
- 외부 클래스의 멤버 변수 선언 위치에 선언됨
- 외부 클래스의 static 멤버처럼 다루어짐
- 주로 static 멤버, static 메서드에서 사용
- 외부 클래스의 객체를 생성하지 않고도 내부 클래스 객체 생성 가능
class Outer { //외부 클래스
public static class StaticInner { //static 내부 클래스
...
}
}
public class StaticInnerClassTest {
public static void main(String args[]) {
Outer.StaticInner sInner = new Outer.StaticInner();
}
}
지역 클래스 > 이름이 있는 지역 내부 클래스(지역 클래스, local class)
- 외부 클래스의 메서드나 초기화 블록 내부에 정의된 클래스로서 지역 변수와 동일한 범위를 가짐 → 클래스가 선언된 메서드 블록 내에서만 사용 가능
- 클래스 이름 명시
class Outer { //외부 클래스
void doSomething() {
class hasNameInner() { //지역 내부 클래스 : 이름 명시
...
}
}
}
public class hasNameInnerClassTest {
public void static main(String args[]) {
Outer outer = new Outer();
Outer.hasNameInner inner = outer.new hasNameInner();
}
}
지역 클래스 > 무명 지역 내부 클래스 (익명클래스/익명객체, anonymous class)
- 클래스의 선언과 객체 생성을 동시에 하는 클래스(일회용)
- 부모 클래스의 이름 또는 구현하고자 하는 인터페이스의 이름을 사용해서 정의 → 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스 구현 불가능. 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만 구현 가능
- 이름 없음 → 생성자 없음.
- 외부 클래스명$익명객체정의된순번.class 형식으로 클래스 파일명이 결정됨
package Anonymous;
public class Car { //부모 클래스
void drive() {
System.out.println("자동차는 달린다.");
}
}
public class Anonymous {
//방법 1 : 필드에 익명자식 객체 생성
Car car1 = new Car() { //익명클래스
String str = "테슬라";
void type() {
System.out.println("전기차다");
}
@Override
void drive() {
System.out.println(str + "전기로 달린다.");
}
};
//방법 2 : 로컬 변수의 초기값으로 대입
void test1() { //메서드 내부에서 지역적으로 익명 클래스 생성
Car car2 = new Car() {
String name = "K7";
void type() {
System.out.println("휘발유차다.");
}
@Override
void drive() {
System.out.println(str + "휘발유로 달린다.");
}
};
car2.drive(); //로컬변수이므로 메서드에서 익명 클래스 바로 사용가능
}
//방법 3 : 익명클래스 매개변수로 대입
void test2(Car car) { //부모 클래스를 매개변수로
car.drive();
}
}
public class AnonymouseTest {
public static void main(String args[]) {
Anonymous as = new Anonymous();
//방법 1 : 익명클래스 필드 사용
as.car1.drive();
//방법 2 : 익명클래스 로컬 변수 사용
as.test1();
//방법 3 : 매개변수로 익명클래스 사용
as.test2(new Car() {
String str = "NEXO";
void type() {
System.out.println("수소차다.");
}
@Override
void drive() {
System.out.println(str + "수소로 달린다.");
}
});
//익명클래스 내부에서 새롭게 정의된 필드, 메서드는 부모클래스로 생성된 car1에서 접근 불가
as.car1.name = "경유차"; //접근불가 : 익명클래스에서 새롭게 정의된 필드
as.car1.type(); //접근불가 : 익명클래스에서 새롭게 정의된 메서드
as.car1.drive(); //접근가능 : 부모클래스 Car에서 재정의(오버라이딩)한 메서드
}
}
내부 클래스의 제어자와 접근성
내부 클래스도 클래스이기 때문에 `abstract`, `final`과 같은 제어자를 사용할 수 있다. 또한 멤버 변수들처럼 `private`, `protected`와 같은 접근제어자도 사용 가능하다.
class Outer {
class InstanceInner { //instance class
int a = 100;
static int staticA = 100; //에러 : instance class내에서 static 변수 선언 불가능
final static int const = 100; //final static은 상수이므로 가능
}
static class StaticInner { //static class
int b = 200;
static int staticB = 200; //static 클래스내에서 static 멤버 정의 가능
}
void method() {
class LocalInner { //local class
int c = 300;
static int staticC = 300; //에러 : local class내에서 static 변수 선언 불가능
final static int const = 300; //final static은 상수이므로 가능
}
}
}
|Note : final이 붙은 변수는 상수(constant)이므로 어떤 경우라도 static을 붙이는 것이 가능하다.
내부 클래스 중 static 멤버는 static 클래스만 선언이 가능하다. `final static`은 상수이므로 모든 내부 클래스에서 정의가 가능하다.
class Outer {
class IntanceInner {} //instance class
static class StaticInner {} //static class
InstanceInner in = new InstanceInner(); //instatnce멤버 간에는 서로 직접 접근 가능
static StaticInner sn = StaticInner(); //static멤버 간에는 서로 직접 접근 가능
static void staticMethod() {
InstanceInner a = new InstanceInner(); //에러 : static멤버는 instance멤버에 직접 접근 불가능
StaticInner b = new StaticInner();
//instance 멤버에 접근하려면 아래와 같이 생성해야함
Outer outer = new Outer(); //instance 클래스는 외부 클래스를 통해 생성해야함
InstanceInner a = outer.new InstanceInner();
}
void instanceMethod() {
//instance 메서드에서는 instance멤버와 static멤버 모두 접근 가능
InstanceInner a = new InstanceInner();
StaticInner b = new StaticInner();
//지역클래스는 외부에서 접근 불가능
LocalInner c = new LocalInner();
}
void Method() {
class LocalInner {} //local class
LocalInner li = new LocalInner();
}
}
instance 클래스는 외부 클래스의 instance 멤버를 객체 생성 없이 사용 가능하지만 static 클래스는 외부 클래스의 instance 멤버를 객체 생성 없이 사용할 수 없다.
instance 클래스는 static 클래스의 멤버들을 객체 생성 없이 사용 가능하지만 static 클래스에서는 instance 클래스의 멤버들을 객체생성 없이 사용할 수 없다.
아래는 내부 클래스에서 외부 클래스의 변수에 대한 접근성을 보여주는 코드다.
class Outer {
private int outerP = 100;
static int outerS = 100;
class IntanceInner {
int ii1 = outerP; //외부클래스의 private 멤버 접근 가능
int ii2 = outerB;
}
static class StaticInner {
int si1 = outerP; //에러 : static클래스는 외부 클래스의 인스턴스 멤버에 접근 불가능
static si2 = outerS;
}
void method() {
int n = 100;
final int m = 100; //jdk1.8부터 final 생략가능
class LocalInner {
int li1 = outerP;
int li2 = outerS;
//외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근 가능
int li3 = n; //jdk1.8부터 에러 아님
int li4 = m;
}
}
}
지역클래스는 해당 클래스가 포함된 메서드에 정의된 `final`이 붙은 지역 변수만 사용할 수 있다. 메서드가 끝나고 지역변수가 소멸된 시점에서도 지역 클래스의 instance가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 때문이다.
JDK1.8부터 지역클래스에서 접근하는 지역변수 앞에 `final`을 생략할 수 있게 되었다. 컴파일러가 자동으로 붙여준다. 따라서 해당 변수의 값이 바뀌는 코드가 있다면 컴파일 에러가 발생한다.
참고
'Java' 카테고리의 다른 글
isEqualTo, isSameTo, isInstanceOf (0) | 2022.08.11 |
---|---|
람다식, 스트림(Lambda, Stream) (0) | 2022.07.26 |
Class 클래스, forName(), Reflection 프로그래밍 (0) | 2022.07.20 |
Wrapper 클래스 (0) | 2022.07.19 |
[객체 지향] is-a, has-a (0) | 2022.07.16 |