ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ch7 자바 객체] 내부 클래스(inner class)와 익명 클래스(anonymous class)
    프로그래밍 언어/JAVA 2022. 6. 22. 21:12
    내부 클래스(inner class)

     

    내부 클래스는 클래스 내에 선언된 클래이다. 내부 클래스의 장점으로는 2가지가 있다.

    1. 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
    2. 코드의 복잡성을 줄일 수 있다. (캡슐화)

     

     

    왼쪽의 A와 B 두 개의 독립적인 클래스를 오른쪽과 같이 바꾸면 B는 A의 내부 클래스(inner class)가 되고 A는 B를 감싸고 있는 외부 클래스(outer class)가 된다. 

     

     

     

    내부 클래스의 종류와 특징

     

    내부 클래스의 종류는 변수의 선언위치에 따른 종류와 같다. 내부 클래스는 마치 변수를 선언하는 것과 같은 위치에 선언할 수 있으며, 변수의 선언위치에 따라 인스턴스 변수, 클래스 변수(static 변수), 지역변수로 구분되는 것과 같이 내부 클래스도 선언 위치에 따라 다음과 같이 구분되어 진다. 

     

    내부 클래스 특 징
    인스턴스 클래스
    (instance class)
    외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스멤버 처럼 다뤄진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다. 
    스태틱 클래스
    (static class)
    외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static멤버처럼 다루어진다. 주로 외부클래스의 static 멤버, 특히 static메서드에서 사용될 목적으로 선언된다. 
    지역 클래스
    (local class)
    외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
    익명 클래스
    (anonymous class)
    클래스의 선언과 객체 생성을 동시에 하는 이름없는 클래스(1회용)

     

     

     

    내부 클래스의 선언

     

    변수가 선언된 위치에 따라 인스턴스 변수, 클래스 변수(static)변수, 지역변수로 나뉘듯이 내부 클래스도 이와 마찬가지로 선언된 위치에 따라 나뉜다. 그리고, 각 내부 클래스의 선언 위치에 따라 같은 선언위치의 변수와 동일한 유효범위(scope)과 접근성(accessibility)을 갖는다.

     

     

     

     

    ※ 예제 1번 ※

     

    package ch7;
    
    public class Ex7_13 {
    	
    	
    	class InstancsInner{ //인스턴스 클래스
    		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(InstancsInner.CONST);
    		System.out.println(StaticInner.cv);
    		
    		//지역변수인 iv를 출력하려면 StaticInner 클래스 객체를 생성 후 호출
    		//객체 생성 안하려면 static으로 지정해줘야한다. cv처럼
    		StaticInner si = new StaticInner();
    		
    		System.out.println(si.iv);
    
    	}
    
    }

    예제 1번 출력값

     

    내부 클래스 중에서 스태틱 클래스만 static 멤버를 가질 수 있다. 다만 final과 static이 동시에 붙은 변수는 상수(constant)이므로 모든 내부 클래스에서 정의가 가능하다.

     

     

     

    ※ 예제 2번 

     

    package ch7;
    
    public class Ex7_14 {
    	
    	class InstanceInner{}
    	static class StaticInner{}
    	
    	//인스턴스멤버 간에는 서로 직접 접근이 가능하다.
    	InstanceInner iv = new InstanceInner();
    	//static 멤버 간에는 서로 직접 접근이 가능하다
    	static StaticInner cv = new StaticInner();
    	
    	static void staticMehod() {
    		//static멤버는 인스턴스멤버에 직접 접근할 수 없다.
    		//InstanceInner obj1 = new InstanceInner();
    		StaticInner obj2 = new StaticInner();
    		
    		//굳이 접근하려면 아래와 같이 객체를 생성해야한다.
    		Ex7_14 outer = new Ex7_14();
    		InstanceInner obj1 = outer.new InstanceInner();
    	}
    	
    	void instanceMethod() {
    		//인스턴스메서드에서는 인스턴스멤버와 static멤버 모두 접근 가능하다.
    		InstanceInner obj1 = new InstanceInner();
    		StaticInner obj2 = new StaticInner();
    		//메서드 내에 지역적으로 선언된 내부 클래스는 외부에서 접근할 수 없다.
    		//LocalInner lv = new LocalInner();
    	}
    	
    	void myMethod() {
    		class LocalInner{}
    		LocalInner lv = new LocalInner();
    	}
    
    }

     

     

     

    ※ 예제 3번 

     

    package ch7;
    
    public class Outer {
    
    	private int outerIv=0;
    	static int outerCv=0;
    	
    	class InstanceInner{
    		int iiv = outerIv; // 외부 클래스의 private멤버도 접근 가능하다.
    		int liv2 = outerCv;
    	}
    	
    	static class StaticInner{
    		//스태틱 클래스는 외부 클래스의 인스턴스멤버에 접근할 수 없다.
    		//int siv = outerIv;
    		static int scv = outerCv;
    	}
    	
    	void myMethod() {
    		int lv=0;
    		final int LV = 0; //JDK1.8 부터 final 생략 가능
    	
    	
    	class LocalInner{
    		int liv = outerIv;
    		int liv2 = outerCv;
    		//외부 클래스의 지역변수는 final이 붙은 변수(상수)만 가능하다.
    		//JDK1.8부터는 에러가 아니다.
    		int liv3 = lv;
    		int liv4 =LV;
    		
    	}
    	}
    
    }

     

    예제 3번은 내부 클래스에서 외부 클래스의 변수들에 대한 접근성을 알아보았다. 인스턴스클래스(InstanceInner)는 외부(Outer)의 인스턴스멤버이기 때문에 인스턴스 변수 outerIv와 static변수 outerCv를 모두 사용할 수 있다. 심지어 접근 제어자가 private여도 사용 가능하다.

     

    스태틱 클래스(StaticInner)는 외부 클래스(Outer)의 static 멤버이기 때문에 외부 클래스의 인스턴스멤버인 outerIv와 InstanceInner를 사용할 수 없다. 단지 static멤버인 outerVc만을 사용할 수 있다. 지역 클래스(LocalInner)는 외부클래스의 인스턴스 멤버와 static멤버를 모두 사용할 수 있으며, 지역 클래스가 포함된 메서드에 정의된 지역 변수도 사용 가능하다. 

     

     

     

    ※ 예제 4번 

    package ch7;
    
    
    class Outer2{
    	class InstanceInner{
    		int iv = 100;
    	}
    	
    	static class StaticInner{
    		int iv = 200;
    		static int cv = 300;
    	}
    	
    	void myMethod() {
    		class LocalInner{
    			int iv = 400;
    		}
    		LocalInner li = new LocalInner();
    		System.out.println("mymethod() li.iv = " + li.iv);
    	}
    }
    
    
    public class Ex7_15 {
    
    	public static void main(String[] args) {
    		
    		//인스턴스클래스의 인스턴스를 생성하려면
    		//외부 클래스의 인스턴스를 먼저 생성해야 한다.
    		Outer2 oc = new Outer2();
    		Outer2.InstanceInner ii = oc.new InstanceInner();
    
    		System.out.println("ii.iv = " + ii.iv);
    		//System.out.println("Outer2.InstanceInner.iv = " + Outer2.InstanceInner.iv);
    		//static 멤버는 따로 인스턴스 생성 없이 외부 클래스 이름으로 호출 가능하다.
    		System.out.println("Outer2.StaticInner.cv = " + Outer2.StaticInner.cv );
    		
    		//스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
    		Outer2.StaticInner si = new Outer2.StaticInner();
    		System.out.println("si.iv = " +si.iv);
    		
    		oc.myMethod();
    		
    		
    	}
    
    }

    예제 4번 출력 값

     

     

     

    ※ 예제 5번 

     

    package ch7;
    
    
    class Outer3{
    	int value =10;  //Outer3.this.value
    	
    	class Inner{
    		int value =20; //this.value
    		
    		void method1() {
    			int value=30;
    			System.out.println("value : "+value);
    			System.out.println("this.value : " + this.value);
    			System.out.println("Outer3.this.value : " + Outer3.this.value);
    		}
    	}
    }
    
    
    public class Ex7_16 {
    
    	public static void main(String[] args) {
    		Outer3 outer = new Outer3();
    		Outer3.Inner inner = outer.new Inner();
    		inner.method1();
    
    	}
    
    }

    예제 5번 출력 값

     

    예제 5번을 통해 내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 'this'또는 '외부 클래스명.this'를 붙여서 구분할 수 있는 것을 알 수 있다.

     

     

     

    익명 클래스(anonymous class)

     

    익명 클래스는 특이하게 다른 내부 클래스들과 달리 이름이 없다. 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한 번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.

     

    new 조상클래스이름() { 
    //멤버 선언
    }

    or 

    new  구현인터페이스이름() {
    //멤버 선언
    }

     

     

    이름이 없기 때문에 생성자를 가질 수 없으며, 조상클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다. 오직 단 하나의 인터페이스를 상속받거나 단 하나의 인터페이스만 구현할 수 있다. 

     

     

    package ch7;
    
    public class Ex7_17 {
    
    		
    		Object iv = new Object() {
    			void method() {}
    		};
    		
    		static Object cv = new Object() {
    			void method() {}
    		};
    		
    		void myMethod() {
    			Object lv = new Object() {
    				void method() {}
    			};
    		}
    
    
    }

     

    위의 코드를 통해 익명 클래스의 사용법을 알아볼 수 있다. 이 예제를 컴파일 하면 4개의 클래스 파일이 생성된다.

    Ex7_17.class

    Ex7_17$1.class

    Ex7_17$2.class

    Ex7_17$3.class

     

     

     

     

     

    ※ 예제 6번 

    package ch7;
    
    import java.awt.Button;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class Ex7_18 {
    
    	public static void main(String[] args) {
    		Button b = new Button("Start");
    		b.addActionListener(new EventHandler());
    
    	}
    
    }
    
    class EventHandler implements ActionListener{
    	public void actionPerformed1(ActionEvent e) {
    		System.out.println("ActionEvent occurred!!");
    	}	
    }

    위의 예제 6번 코드를 가지고 익명 클래스로 변경해서 예제 7번에 적어보자

     

     

    ※ 예제 7번 

     

    package ch7;
    
    import java.awt.Button;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class Ex7_19 {
    
    	public static void main(String[] args) {
    		
    		Button b = new Button("Start");
    		b.addActionListener(new ActionListener() {
    			public void actionPerformed1(ActionEvent e) {
    				System.out.println("ActionEvent occurred!!");
    			}
    			
    		});
    	}
    
    }

     

    ActionListener의 구현을 받는 EventHandler라는 이름을  가진 클래스를 생성하지 않고 바로 익명 클래스로 작성하는 방법이다. 

     

     

     

    반응형

    댓글

Designed by Tistory.