24. 내부 클래스와 익명 클래스

Isaiah IM·2023년 8월 17일
0

java basic

목록 보기
26/38
post-thumbnail

1. 내부클래스

1.1 내부클래스란?

내부 클래스란 클래스 내에 선언되는 클래스를 의미한다.

내부 클래스란 아래와 같이 클래스 안에 선언된 클래스를 말한다.

public class InnerClass {
	
	class InnerClassTest{// 내부 클래스
		...// 내부 클래스 내용
	}
}

내부 클래스는 두 클래스가 매우 긴밀한 관계에 있는 경우 사용을 하며, 두 클래스간의 멤버에 서로 쉽게 접근이 가능하며, 외부에 추가적인 클래스를 노출하지 않아도 되서 코드의 복잡성을 줄이고 캡슐화가 가능하다는 장점이 있다.

1.2 내부클래스 종류

내부 클래스는 선언 위치에 따라 인스턴스 클래스, static 클래스, 지역 클래스, 익명 클래스로 나뉜다.

내부 클래스는 선언 위치에 따라 인스턴스 클래스(instance class), static 클래스(static class), 지역 클래스(local class), 익명 클래스(anonymous class)로 나뉜다.

  • 인스턴스 클래스
    인스턴스 클래스는 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다뤄진다.
    인스턴스 클래스는 주로 외부 클래스의 인스턴스 멤버들과 상호작용할때 주로 사용된다.
public class InnerClass {
	int val1=1;
	float val2=2.0f;
	
	class InstanceClass{// 인스턴스 클래스
		
		public void changeOuter() {
			val1=10;// 외부 클래스 값 변경
		}
	}
	
}
  • static 클래스
    static 클래스는 인스턴스 클래스와 동일하게 외부 클래스의 멤버변수 선언위치에 선언하며, static키워드가 붙은 클래스이다.
    static 클래스의 경우 인스턴스 멤버와 static 멤버 둘 다 선언이 가능하나, 일반적인 static 메소드와 동일하게 외부 클래스의 인스턴스 멤버에는 접근이 불가하고, 정적(static) 멤버에만 접근할 수 있다.
public class InnerClass {
	int val1=1;
	float val2=2.0f;
	static int staticVal=10;
	
	static class StaticClass{// static 클래스
		
		public void changeOuter() {
			staticVal=5;
		}
	}
	
}
  • 지역 클래스
    지역 클래스는 외부 클래스의 메소드나 초기화 불럭 안에 선언하며, 선언된 영역 내에서만 사용할 수 있다.
public class InnerClass {
	int val1=1;
	float val2=2.0f;
	static int staticVal=10;
	
	public void method(){
		
		class LocalClass{// 지역 클래스
			...// 내용
		}
	}
	
}

1.3 내부클래스의 접근성

내부 클래스 역시 클래스이기 때문에 abstract, final과 같은 제어자를 사용할 수 있으며, 멤버변수들과 동일하게 private, protect과 같은 접근제어자 역시 사용할 수 있다.

또한 내부 클래스에서는 static class에서만 static멤버를 선언할 수 있다.

class InnerClass { 
    class InstanceInner { 
          int iv = 100; 
//        static int cv = 100;            // 에러! static변수를 선언할 수 없다. 
          final static int CONST = 100;   // final static은 상수이므로 허용한다. 
    } 

    static class StaticInner { 
          int iv = 200; 
          static int cv = 200;       // static클래스만 static멤버를 정의할 수 있다. 
    } 

    void myMethod() { 
          class LocalInner { 
                int iv = 300; 
//              static int cv = 300;          // 에러! static변수를 선언할 수 없다. 
                final static int CONST = 300; // final static은 상수이므로 허용 
          } 
    } 

    public static void main(String args[]) { 
          System.out.println(InstanceInner.CONST); 	//100
          System.out.println(StaticInner.cv); 	//200
    } 
}

output

100
200

2. 익명클래스

2.1 익명클래스란?

익명 클래스는 이름이 없는 클래스로, 단 한번만 사용할 수 있는 일회용 클래스다.

익명클래스는 이름이 없는 클래스이기 때문에 일반 클래스처럼 생성자도 가질 수 없고, 단 한번만 사용되는 일회용 클래스이다.

객체지향은 클래스를 통해 객체화를 해서 클래스의 재사용성을 높이고, 확장 및 유지보수를 용의하게 하는데 중점이 되 있다. 모든것을 객체로 인식해 객체의 공통된 부분을 이용해 확장(상속)하고, 기능을 분류(인터페이스)해서 코드를 모듈화 하는것이 중요하다.
그러나, 익명클래스는 다르다. 같은 클래스라는 이름을 갖고 있음에도 클래스의 재사용과 확장 및 상속을 고려하지 않고, 단 한번만 사용하는 클래스이다. 즉, 상속, 확장, 재사용성을 고려하지 않는다.
이런 클래스는 주로 프로그램에서 일시적으로 사용되는 객체에 적용이 된다. 가령 우리가 화면을 만들때의 UI이벤트 처리, 스레드 객체 등 일시적인 경우에 사용이 된다.

예를 들어보자.
우리가 스마트폰에서 화면의 버튼을 클릭하는 이벤트 처리(버튼 클릭 감지)를 코딩한다 가정하자. 버튼이 눌리면 이벤트가 발생하면서 버튼이 눌렸습니다라는 것을 표시한다고 하자. 그렇다면 이것을 클래스로 만들 수 있겠지만, 일반적으로 특정 버튼을 눌렀을때 특정 이벤트가 발생하는 것은 해당 이벤트에서만 발생하는 경우가 대부분이다. 이럴때는 굳이 클래스로 만들 필요가 없다는 것이다. 만약 버튼클릭을 객체지향으로 만든다면 이렇게 만들어야 할 것이다.

버튼 클릭이라는 공통된 클래스를 만들고, 각 버튼이 클릭되는 클래스를 상속하는 형식으로 만들면 객체지향적으로 설계가 되는 것이다.
그런데 생각해보자. 각 버튼간의 딱히 공통점도 없고, 해봐야 버튼이 클릭됬다는 공통점만 있을 뿐인데 이렇게까지 복잡하게 설계할 필요가 있는가? 어쩌면 이렇게 객체지향적으로 설계하면 유지보수만 귀찮아지고, 구조만 쓸데없이 복잡해진다.

이럴때 익명클래스를 이용하면 해당 버튼이 클릭됬을때 사용하고 사라지기 때문에 버튼을 클릭하는 클래스를 유지보수할 필요가 없다. 왜냐하면 필요할때만 쓰고 없어지기 때문이다. 상속을 받는것도 아니기 때문에 클래스 관계도 신경쓸 필요가 없다.

스레드도 마찬가지이다. 스레드가 종료되면 해당 스레드의 객체는 필요가 없어진다. 마치, 우리가 워드로 작업을 하다가 워드를 종료하면 해당 워드 프로그램은 우리 화면에 남아있을 필요가 없는것 처럼 스레드 역시 스레드가 종료되면 스레드 객체는 사라진다. 즉, 필요할 때만 사용하고 사라지는 존재이기 때문에 굳이 클래스를 만들고, 상속을 하면서 객체지향적으로 설계할 필요가 없는 것이다. 오히려 객체지향적으로 설계하면 구조만 복잡해지고, 유지보수만 귀찮아질 뿐이다.

이처럼 익명클래스는 필요할 때만 사용되는 객체에서 주로 사용이 된다.

2.2 익명클래스의 생성 및 사용

익명 클래스는 부모 클래스를 이용해 생성을 한다.

class Animal {
    public String cry() {
        return "동물이 웁니다";
    }
}
public class Main {
    public static void main(String[] args) {
       
        Animal cat = new Animal() {// 익명클래스
        	@Override
            public String cry() {
                return "야옹";
            }
        }; // 익명클래스 끝에는 세미콜론 필요!
        	
        
        cat.cry();// 익명클래스 사용
    }
}

위와 같이 부모클래스를 이용해 생성하고, 필요한 부분을 오버라이딩 해서 사용한다.

profile
나는 생각한다. 고로 나는 코딩한다.

1개의 댓글

comment-user-thumbnail
2023년 8월 17일

개발자로서 배울 점이 많은 글이었습니다. 감사합니다.

답글 달기