-
자바의 내부클래스와 익명클래스의 이해스타터스 백엔드 3기 2022. 12. 14. 21:40
내부클래스(inner class) = 중첩클래스
- 어디서 어떻게 사용되는지를 모르니까 이해가 잘 되지 않는다. 익명클래스가 많이 사용된다고 하셨는데, 주로 익명클래스의 용도로 사용되는 걸까?
내부클래스의 용도
- 중요한 정보를 캡슐화해서 접근 방식에 제한을 둘 수 있다. 내부클래스를 사용하기 위해서는 반드시 외부클래스를 사용해야 하기 때문이다.
- 외부클래스 안에 내부클래스가 있다는 포함관계를 확실히 한다.
한 번 쓰고 말 거라서
내부클래스의 종류
변수처럼 생각하자!
instance class - 외부클래스의 멤버변수 위치에 선언한다.
- 주로 외부클래스의 인스턴스 멤버틀과 관련된 작업에 사용될 목적이다.static class - 외부클래스의 멤버변수 위치에 선언한다.
- 주로 외부클래스의 static 멤버, 특히 static 메소드에서 사용될 목적이다.local class - 외부 클래스의 메서드나 초기화 블럭 안에 선언된다.
- 선언된 영역 내부에서만 사용된다. (지역변수 생각하자!)anonymous class - 클래스의 선언 + 객체의 생성 동시에 하는 이름없는 클래스이다. 일회용이다. 예시
class Outer { // 외부 클래스 class InstanceInner { } // 인스턴스 클래스 (내부) static class StaticInner { } // 스태틱 클래스 (내부) void myMehtod { class LocalInner { } // 로컬 클래스 (내부) } }내부클래스 정보
- 외부클래스의 메소드는 내부클래스의 요소에 접근이 불가하다.
- 내부클래스의 메소드는 외부클래스의 요소에 접근 가능하다.
- static class만 static 변수를 가질 수 있다.
- instance class 는 static 변수를 가질 수 없다.
- final static이 붙은 상수는 모든 내부 클래스에서 정의가 가능하다.
- 인스턴스 클래스 간에는 서로 직접 접근이 가능하다.
- static 클래스 간에는 서로 직접 접근이 가능하다.
- Static 멤버는 인스턴스 멤버에 직접 접근 불가하다.
- 인스턴스 메소드에서는 인스턴스 클래스와 static 클래스 모두 접근 가능하다.
- 다른 메서드 안에 지역적으로 선언된 내부 클래스는 외부에서 접근 불가하다.
- 인스턴스 클래스는 외부 클래스의 인수턴스 변수와 static 변수를 모두 사용 가능하다.
- static 클래스는 외부클래스의 static 변수만 사용 가능하다.
- local 클래스는 외부클래스의 인스턴스 변수, static 변수, final 지역변수를 사용 가능하다.
- JDK1.8부터 final을 떼고 지역변수만 적어도 사용 가능해졌는데, 이는 지역변수를 사용 가능한 게 아닌 앞에 자동으로 final이 붙는 것이다.

내부클래스를 ... 이해해보자 !!!
내부클래스는 변수처럼 선언된다. 변수가 선언된 위치에 따라 인스턴스 변수, 클래스 변수, 지역변수로 나뉘듯이 내부클래스도 선언된 위치에 따라 나뉜다.
음 그러니까. 정말 변수처럼 생각하면 될 듯.
물론 그렇다고 변수는 아니다.1. 멤버 inner Class
아래는 아주 기본적인 외내부 클래스이다. 각자 멤버변수와 메소드를 하나씩 부여했다.
mOut1 ===> outMethod1
mInn1 ===> inMethod1
꽤나 어이없는 광경을 볼 수 있다.
타 클래스에서 class In1을 사용하고 싶다면 class Out1을 사용해야 한다.
문제는 class Out1에서도 class In1의 변수를 사용하지 못한다.
이는 외부클래스의 메소드는 내부클래스의 요소에 접근하지 못하기 때문이다.
반대로, 내부클래스의 메소드는 외부클래스의 요소에 접근할 수 있다.
그러면, 어떻게 해야 외부클래스에서 내부클래스의 요소를 사용할 수 있을까?
아래처럼 class In1의 객체를 만들어 사용하면 된다.
... 뭔가 배보다 배꼽이 더 큰 느낌?
- 다음으로는, 타 클래스에서 class In1을 사용해보려 한다.
- 기존의 방법대로 클래스타입 변수명 = new 생성자(); 를 사용해 생성했다.
- 역시나 안 된다. Inner cannot be resolved to a type error
- 위에서 말했듯, 내부클래스는 혼자 객체를 생성할 수 없기에 반드시 외부 객체를 통해서 만들어져야 한다.
내부클래스 객체 생성 방법 1.
Out1 out1 = new Out1();
Out1.In1 innerInstance = out1.new In1();
내부클래스 객체 생성 방법 2.
위의 두 문장을 합친 것이다.
Out1.In1 innerInstance = new Out1().new In1();
내부클래스의 타입은 외부클래스이름.내부클래스이름 임을 알 수 있다.
참고사항 : 자동완성에 In1은 Out1 외부클래스의 innerclass라고 뜬다.사용해보기
특이점은 내부 객체 사용할 때 외부 변수랑 메소드 사용 가능한 줄 알았는데 아니었음.
음 내 생각에는 상속이 아니라 클래스고, 외부.내부라서 내부 클래스를 가리키기 때문에 외부의 것을 타클래스에서 사용 못 하는 게 아닌가 싶다.
2. 멤버 static inner 클래스
- static 클래스는 외부클래스의 static 변수만 사용 가능하다.
package innerclass2; class Outer { static int i = 10; void mo() { System.out.println(i + new Inner().j); } static class Inner { // static 멤버변수 취급한다. static int j = 20; static void mi() { System.out.println(i - j); // static 내부클래스 메소드는 외부클래스 static 요소에만 접근 가능하다. // 만약 외부클래스의 i가 int i = 10; 이라면 static이 아니라 접근 불가하다. } } } public class InnerClassTest2 { public static void main(String[] args) { // Outer o = new Outer(); // Outer.Inner inn = o.new Inner(); // 위의 방법을 사용하지 못하는 이유는 , static이라 이미 메모리에 할당되어 있기 때문이다. // static 변수는 class명.static변수이름 이렇게 사용한다. // static inner class도 같은 방식으로 사용하자. // OuterClassName.StaticInnerClassName.staticMethod(); Outer.Inner.mi(); } }3. 지역 inner 클래스
- 지역변수 취급당한다.
- 객체변수가 아닌 지역변수기에 Outer.LocalInner local = new Outer().new LocalInner(); 를 사용하지 못한다.
- o.test() 형태로 사용한다.
package innerclass3; class Outer { void test() { class LocalInner { // 지역변수 취급한다. int j = 20; void mi() { System.out.println(j); } } new LocalInner().mi(); } } public class InnerClassTest3 { public static void main(String[] args) { // Outer.Inner inn = o.new Inner(); // 지금 inner클래스가 객체변수 아니고 지역변수라 이런 식으로 못 부르나. Outer o = new Outer(); o.test(); } }
익명클래스 (무명클래스)
- 클래스의 선언과 객체의 생성을 동시에 한다.
- 자바스크립트의 화살표함수가 생각나기도 하고?
- 단 한번만 사용되고, 오직 하나의 객체만을 생성 가능하다.
- 어느것을 상속받았는지 확실히 할 수 있다.
- 인터페이스는 객체 생성이 불가하므로, 인터페이스를 상속받은 무명클래스(=하위클래스)를 정의한 뒤 형변환하는 방식으로 진행해야 한다.
그러니까 원래는
class MyClass extends Object { }
MyClass mc = new MyClass();이런 식으로 클래스를 만들고 객체를 생성해야 한다. 하지만 이름이 없다. 여기서 MyClass를 사용할 수 없는 것이다.
이 경우, 조상 혹은 인터페이스의 이름을 사용한다.
이름이 없으므로 일시적으로 사용되고 버려지는 클래스이다.
그냥 new 조상/인터이름 () { 클래스내부내용 } 이게 끝이다. 어렵게 생각하지 말자.
익명클래스 선언 방법 new 조상클래스이름( ) {
// 멤버 선언
}new 구현인터페이스이름( ) {
// 멤버 선언
}
class Event{
public static void main(String[] args) {
Button b = new Button("Start");
b.addEventListener(new EventHandler( ));
}
}
class EventHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("event 호출!!!");
}
}
class Event {
public static void main(String[] args) {
Button b = new Button("Start");
b.addEventListener(new ActionListener( ) {
public void actionPerformed(ActionEvent e) {
System.out.println("event 호출!!!");
}
}
);
}
}package chap7; // 무명클래스 // 내부 인터페이스 ... 도 존재 가능하다. interface I1 { void m1(); } interface I2 { void m2(); } class Ano { } public class AnonymousTest { public static void main(String[] args) { // I1 i1 = new Ii; 이건 잘못됨. 인터페이스는 인스턴스 생성 불가임. // I1 인터페이스를 상속받은 무명 클래스(하위클래스임)를 정의한다. 타입은 형변환으로 I1으로 준다. // 바로 오버라이딩했다. 객체생성문장을 함께 적는다. 누구를 상속받았는지 확실히 한다. I1 i1 = new I1(){ public void m1() { System.out.println("m1 호출"); } }; I2 i2 = new I2() { public void m2() { System.out.println("m2 호출"); } }; i1.m1(); i2.m2(); } }내가 생각한 무명클래스
interface i1 { void m1() }
class Ano implement i1 {
@override
void m1() {}
}
i1 i1 = new i1() {
@override
public void m1() {
// 재정의 했어요!
}
}interface : 이름이 a1
class : 이름이 Ano, a1 인터페이스를 상속받았다.
여기서 Ano의 내용이 1회만 사용되고 재사용 되지 않는 것이라고 하자. 무명클래스로 사용하자. 1회만 사용될 것인데 굳이 이름 줄 필요도 없다.
인터페이스이름 변수이름 = new 인터페이스이름 () {}
- 원래대로면 new 인터페이스이름(); 이렇게 끝나야겠지.
- 하지만 일단 1. 인터페이스라서 객체 생성 안 되고 2. 우리는 무명클래스를 사용할 거다.
- 그래서 new 인터페이스이름() 으로 객체 만들고,
- 바로 뒤에 { 괄호 안에 클래스 내용을 구현하는 것이다. }'스타터스 백엔드 3기' 카테고리의 다른 글
221215. 자바9. 라이브러리(String, Object) (0) 2022.12.15 221215 자바8. 예외처리 (0) 2022.12.15 221214 자바 7. 인터페이스, 내부클래스, 익명클래스, (0) 2022.12.14 221210. 자바5 (클래스 형변환) (0) 2022.12.13 221212. 자바 4 {생성자 (접근제한자, this, super, 상속) } (0) 2022.12.12