반응형

출처 : 이것이 자바다

중첩 클래스와 중첩 인터페이스

9.1 중첩 클래스와 중첩 인터페이스란

중첩 클래스와 중첩 인터페이스란?

중첩 클래스: 클래스 멤버로 선언된 클래스

 

중첩 인터페이스: 클래스 멤버로 선언된 인터페이스

      UI 컴포넌트 내부 이벤트 처리에 많이 활용

 

 

안드로이드에서

public class View{

           public interface OnClickListener{

                       public void onClick(View v);

}

}

 

9.2 중첩 클래스

v  중첩 클래스의 분류

 

클래스 생성시 바이트 코드 따로 생성

 

9.2.1 인스턴스 멤버 클래스

인스턴스 멤버 클래스는 static 키워드 없이 선언된 클래스

인스턴스 필드와 메소드만 선언이 가능하고 정적 필드와 메소드는 선언할 수 없다.

 

 

 

 

9.2.2 정적 멤버 클래스

    • static 키워드로 선언된 클래스, 모든 종류의 필드, 메소드 선언 가능

 

9.2.3로컬 클래스 – 메소드 내에서만 사용

 

 

 

중첩 클래스

/** 바같 클래스*/

public class A {

           A() {System.out.println("A 객체가 생성됨"); }

   

    /**인스턴스 멤버 클래스**/

    class B {

        B() {System.out.println("B 객체가 생성됨");}

        int filed1;

        void method1() { }

    }

   

    /**정적 멤버 클래스**/

    static class C {

        C() {System.out.println("C 객체가 생성됨");}

        int field1;

        static int field2;

        void method1() { }

        static void method2() { }

    }

   

    void method() {

        /**로컬 클래스**/

        class D {

            D() {System.out.println("D 객체가 생성됨");}

            int field1;

            void method1() { }

        }

        D d = new D();

        d.field1 = 3;

        d.method1();

    }

}

 

중첩 클래스 객체 생성

public class Main {

    public static void main(String[] args) {

        A a = new A();

       

        //인스턴스 멤버 클래스 객체 생성

        A.B b = a.new B();

        b.filed1 = 3;

        b.method1();

       

        //정적 멤버 클래스 객체 생성

        A.C c = new A.C();

        c.field1 = 3;

        c.method1();

        A.C.field2 = 3;

        A.C.method2();

       

        //로컬 클래스 객체 생성을 위한 메소드 호출

        a.method();

    }

}

 

9.3 중첩 클래스의 접근 제한

9.3.1 바깥 필드와 메소드에서 사용 제한

바깥 필드와 메소드에서 사용 제한

package textbook.chapter9.examl01;

 

public class A {

           // 인스턴스 필드

           B field1 = new B();

           C field2 = new C();

 

           // 인스턴스 메소드

           void method1() {

                       B var1 = new B();

                       C var2 = new C();

           }

 

           // 정적 필드 초기화

           // static B field3 = new B();

           static C field4 = new C();

 

           // 정적 메소드

           static void method2() {

                       // B var1 = new B():

                       C var = new C();

           }

 

           // 인스턴스 멤버 클래스

           class B {}

 

           // 정적 멤버 클래스

           static class C {}

}

 

9.3.2 멤버 클래스에서 사용 제한

 

 

 

 

 

멤버 클래스에서 사용 제한

package textbook.chapter9.exam02;

 

public class A {

           int field1;

    void method1() { }

   

    static int field2;

    static void method2() { }

   

    class B {

        void method() {        //모든 필드와 메소드에 접근할 수 있다.

            field1 = 10;

            method1();

           

            field2 = 10;

            method2();

        }

    }

   

    static class C {

        void method() {        //인스턴스 필드와 메소드는 접근할 수 없다.

            //field1 = 10;

            //method1();

           

            field2 = 10;

            method2();

        }

    }

}

 

멤버 클래스에서 사용 제한

 

 

9.3.3 로컬 클래스에서 사용 제한

로컬 클래스 내부에서는 바깥 클래스의 필드나 메소드를 제한 없이 사용할 수 있다.

문제는 메소드의 매개변수나 로컬 변수를 로컬 클래스에서 사용할 때이다.

결론적으로 말해서 로컬 클래스에서 사용 가능한 것은 final로 선언된 매개 변수와 로컬 변수뿐

우리는 로컬 클래스의 내부 복사 위치에 신경 쓸 필요 없이 로컬 클래스에서 사용된 매개 변수와 로컬 변수는 모두 final 특성을 갖는다는 것만 알면 된다.

자바 7까지는 반드시 final 키워드를 붙여야 되지만, 자바 8부터는 붙이지 않아도 final 특성을 가지고 있음을 주목

 

 

class LocalClass{

int arg2 = 매개값;

int var2 = 2;

void method(){

  int arg1 = 매개값;

  int var1 = 1;

  int result = arg1 +arg2+var1+var2;

}

}

void outMethod(final int arg1, int arg2){

final int var1 =1;

int var2 = 2;

class LocalClass{

  void method(){

 int result = arg1+arg2+var1+var2;

}

}

}

 

 

 

final 매개변수와 로컬 변수는

로컬 클래스의 메소드의 로컬변수로 복사

(final 붙이지 않으면 컴파일 오류 발생)

매개변수와 로컬 변수는 final 특성을 가지며,

로컬 클래스의 필드로

   
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

로컬 클래스에서 사용 제한

public class Outter {

            //자바 7 이전

    public void method1(final int arg) {

        final int localVariable = 1;

        //arg = 100;    (x)

        //localVariable = 100; (x)

        class Inner {

            public void method() {

                int result = arg + localVariable;

            }

        }

    }

   

    //자바 8 이후

    public void method2(int arg) {

        int localVariable = 1;

        //arg = 100;     (x)

        //localVariable = 100; (x)

        class Inner {

            public void method() {

                int result = arg + localVariable;

            }

        }

    }

}

 

9.3.4 중첩 클래스에서 바깥 클래스 참조 얻기

 

 

클래스 내부에서 this는 객체 자신의 참조이다.

중첩 클래스에서 this 키워드를 사용 바깥 클래스의 객체 참조가 아니라, 중첩 클래스의 객체 참조가 된다.

따라서 중첩 클래스 내부에서 this.필드,this.메소드()로 호출하면 중첩 클래스의 메소드가 사용된다.

중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻으려면 바깥 클래스의 이름을 this 앞에 붙여주면 된다.

 

ex) 바깥클래스.this.필드

     바깥클래스.this.메소드();

 

중첩 클래스에서 바깥 클래스 참조 얻기

public class Outter {

           String field = "Outter-field";

 

           void method() {

                       System.out.println("Outter-method");

           }

 

           class Nested {

                       String field = "Nested-field";

 

                       void method() {

                                  System.out.println("Nested-method");

                       }

 

                       void print() {

                                  System.out.println(this.field); // 중첩 객체 참조

                                  this.method();

                                  System.out.println(Outter.this.field); // 바깥 객체 참조

                                  Outter.this.method();

                       }

           }

}

 

실행 클래스

public class OutterExample {

           public static void main(String[] args) {

                       Outter outter = new Outter();

                       Outter.Nested nested = outter.new Nested();

                       nested.print();

           }

}

 

9.4 중첩 인터페이스

중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다.

 

class A{

interface I{

  void method(); //중첩 인터페이스

}

}

 

중첩 인터페이스

 

 

중첩 인터페이스

public class Button {

           OnClickListener listener; // 인터페이스 타입 필드

 

           void setOnClickListener(OnClickListener listener) { // 매개 변수의 다형성

                       this.listener = listener;

           }

 

           void touch() { // 구현 객체의 onClick() 메소드 호출

                       listener.onClick();

           }

 

           interface OnClickListener { // 중첩 인터페이스

                       void onClick();

           }

}

 

구현 클래스

public class CallListener implements Button.OnClickListener {

 

           @Override

           public void onClick() {

                       System.out.println("전화를 겁니다.");

           }

}

 

 

구현 클래스

public class MessageListener implements Button.OnClickListener{

 

    @Override

    public void onClick() {

        System.out.println("메시지를 보냅니다.");

    }

 

}

 

버튼 이벤트 처리

public class ButtonExample {

           public static void main(String[] args) {

                       Button btn = new Button();

                      

                       btn.setOnClickListener(new CallListener());

                       btn.touch();

                      

                       btn.setOnClickListener(new MessageListener());

                       btn.touch();

 

           }

}

 

 

9.5 익명 객체

v  익명 객체: 이름이 없는 객체

익명 객체는 단독 생성 불가

      클래스 상속하거나 인터페이스 구현해야만 생성 가능

사용 위치

      필드의 초기값, 로컬 변수의 초기값, 매개변수의 매개값으로 주로 대입

      UI 이벤트 처리 객체나, 스레드 객체를 간편하게 생성할 목적으로 주로 활용

 

9.5.1 익명 자식 객체 생성 – 초기값 설정에 주목

class Child extends Parent{  }//자식 클래스 선언

 

class A{

Parent field = new Child(); //필드에 자식 객체를 대입

void method(){

  Parent localVar = new Child();//로컬 변수에 자식 객체를 대입

}

}

 

자식 클래스가 재사용하지 않고 , 오로지 해당 필드와 변수의 초기값으로만 사용할 경우라면 익명 자식 객체를 생성해서 초기값으로 대입하는 것이 좋은 방법이다.

주의할 점은 하나의 실행문이므로 끝에는 세미콜론(;) 을 반드시 붙여야 한다.

부모클래스 [필드|변수] = new 부모클랫(매개값, ..){

           //필드

//메소드

};

 

일반 클래스와의 차이점은 생성자를 선언할 수 없다는 것이다.

class A{

Parent field = new Child(); // A클래스의 필드 선언

int childField;

void childMethod();

@Override //Parent의 메소드를 오버라이딩

void parentMethod(){}

}

 

로컬 변수 익명 자식 객체

 

 

메소드의 매개 변수가 부모 타입일 경우 메소드 호출 코드에서 익명 자식 객체를 생성해서 매개값으로 대입할 수도 있다.

class A{

 void method1(Parent parent) { }

 

 void method2(){

method1{ //method1()메소드 호출

 new Parent(){ // method1()의 매개값으로 익명 자식 객체를 대입

  int childField;

  void childMethod() { }

  @Override

  void parentMethod(){}

}

}   

 }

}

v  익명 객체에 새롭게 정의된 필드와 메소드

익명 객체 내부에서만 사용

외부에서는 익명 객체의 필드와 메소드에 접근할 수 없음

      이유: 익명 객체는 부모 타입 변수에 대입되므로 부모 타입에 선언된 것만 사용 가능

 

 

부모 클래스

public class Person {

           void wake() {

        System.out.println("7시에 일어납니다.");

    }

}

 

익명 자식 객체 생성

public class Anonymous {

           //필드 초기값으로 대입

    Person field = new Person() {    // 필드 선언과 초기값 대입

        void work() {

            System.out.println("출근합니다.");

        }

        @Override

        void wake() {

            System.out.println("6시에 일어납니다.");

            work();

        }

    };

   

    void method1() {    //로컬 변수 선언과 초기값 대입

        //로컬 변수값으로 대입

        Person localVar = new Person() {

            void walk() {

                System.out.println("산책합니다.");

            }

            @Override

            void wake() {

                System.out.println("7시에 일어납니다.");

                walk();

            }

        };

        //로컬 변수 사용

        localVar.wake();

    }

   

    void method2(Person person) {

        person.wake();

    }

}

 

익명 자식 객체 생성

public class AnonymousExample {

           public static void main(String[] args) {

                       Anonymous anony = new Anonymous();

                       // 익명 객체 필드 사용

                       anony.field.wake();

                       // 익명 객체 로컬 변수 사용

                       anony.method1();

                       // 익명 객체 매개값 사용

                       anony.method2(new Person() {

                                  void study() {

                                              System.out.println("공부합니다.");

                                  }

 

                                  @Override

                                  void wake() {

                                              System.out.println("8시에 일어납니다.");

                                              study();

                                  }

                       });

           }

}

 

9.5.2 익명 구현 객체 생성

인터페이스 타입으로 필드나 변수를 선언하고 , 구현 객체를 초기값으로 대입하는 경우

class TV implements RemoteControl{}

 

class A{

 RemoteControl field = new TV(); //필드에 구현 객체를 대입

 void method(){

  RemoteControl localVar = new TV();//로컬 변수에 구현 객체를 대입

}

}

 

 

 

    • 초기값 설정에 대해서는 p. 409~ 416 참고

 

다음은 필드를 선언할 때 초기값으로 익명 객체를 생성해서 대입하는 예:

class A{

 RemoteControl field = new RemoteControl(){ //클래스 A의 필드 선언

   @Override   //RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드

   void turnOn() {}

 };

}

 

메스드의 매개 변수가 인터페이스 타입일 경우 , 메소드 호출 코드에서 익명 구현 객체를 생성해서 매개값으로 대입할 수도 있다.

class A{

 void method1(RemoteControl rc){}

 void method2(){

method1(){//method1 ()메소드 호출

   new RemoteControl(){//method1()의 매개값으로 익명 구현 객체를 대입

    @Override  

    void turnOn() {}

}

}

}

}

 

인터페이스

package textbook.chapter9.exam01;

 

public interface RemoteControl {

           public void turnOn();

           public void turnOff();

}

 

익명 구현 클래스와 객체 생성

package textbook.chapter9.exam01;

 

public class Anonymous {

           //필드 초기값으로 대입

           RemoteControl field = new RemoteControl() { //필드의 선언과 초기값 대입

                      

                       @Override

                       public void turnOn() {

                                  System.out.println("TV를 켭니다.");

                       }

                      

                       @Override

                       public void turnOff() {

                                  System.out.println("TV를 끕니다.");

                       }

           };

          

           void method1() { //로컬 변수 선언과 초기값 대입

                       //로컬 변수 값으로 대입

                       RemoteControl localVar = new RemoteControl() {

                                 

                                  @Override

                                  public void turnOn() {

                                              System.out.println("Aidio를 켭니다.");

                                  }

                                 

                                  @Override

                                  public void turnOff() {

                                              System.out.println("Aidio를 끕니다.");

                                  }

                       };

                      

                       //로컬 변수 사용

                       localVar.turnOn();

                      

           }

          

           void method2(RemoteControl rc) {

                       rc.turnOn();

           }

}

 

익명 구현 클래스와 객체 생성

package textbook.chapter9.exam01;

 

public class AnonymousExample {

           public static void main(String[] args) {

                       Anonymous anony = new Anonymous();

                       //익명 객체 필드 사용

                       anony.field.turnOn();

                       //익명 객체 로컬 변수 사용

                       anony.method1();

                       //익명 객체 매개값 아ㅛㅇㅇ

                       anony.method2(new RemoteControl() {//매개값

                                 

                                  @Override

                                  public void turnOn() {

                                              System.out.println("SmartTV를 켭니다.");

                                  }

                                 

                                  @Override

                                  public void turnOff() {

                                              System.out.println("SmartTV를 끕니다.");

                                  }

                       });

           }

}

 

UI클래스

package textbook.chapter9.exam01;

 

public class Button {

           OnClickListener listener; // 인터페이스 타입 필드

 

           void setOnClickListener(OnClickListener listener) { // 매개 변수의 다형성

                       this.listener = listener;

           }

 

           void touch() { // 구현 객체의 onClick() 메소드 호출

                       listener.onClick();

           }

 

           interface OnClickListener { // 중첩 인터페이스

                       void onClick();

           }

}

 

UI 클래스

package textbook.chapter9.exam01;

 

public class Window {

          

           Button button1 = new Button();

           Button button2 = new Button();

          

           //필드 선언과 초기값 대입

           //필드로 선언한 익명 구현 객체가 담당하고

           Button.OnClickListener listener = new Button.OnClickListener() {

                      

                       @Override

                       public void onClick() {

                                  System.out.println("전화를 겁니다.");

                       }

           };

          

           //setOnClickListener()를 호출할 때 매개갑으로 준 익명 구현 객체가 담당하도록 했다.

           Window(){

                       button1.setOnClickListener(listener); //매개값으로 필드 대입

                       button2.setOnClickListener(new Button.OnClickListener() {

                                  //매개값으로 익명 구현 객체 대입

                                  @Override

                                  public void onClick() {

                                              System.out.println("메시지를 보냅니다.");

                                  }

                       });

           }

}

 

실행 클래스

package textbook.chapter9.exam01;

 

public class Main {

           public static void main(String[] args) {

                       Window w = new Window();

                       w.button1.touch();

                       w.button2.touch();

           }

}

 

9.5.3 익명 객체의 로컬 변수 사용

익명 객체 내부에서는 바깥 클래스의 필드나 메소드는 제한 없이 사용할 수 있다.

 

문제는 메소드의 매개 변수나 로컬 변수를 익명 객체에서 사용할 때이다.

 

메소드 내에서 생성된 익명 객체는 메소드 실행이 끝나도 힙 메모리에 존재해서 계속 사용할 수 있다.

 

우리는 익명 클래스의 내부 복사 위치에 신경 쓸 필요 없이 익명 객체에서 사용된 매개 변수와 로컬변수는 모두 final 특성을 갖는다는 것만 알면 된다.

 

다음 예제는 매개 변수와 로컬 변수가 익명 객체에서 사용할 때 final 특성을 갖고 있음을 잘 보여준다.

 

자바 7에서는 반드시 final 키워드를 붙여야 되지만, 자바 8부터는 붙이지 않아도 final 특성을 가지고 있음을 주목해야 한다.

 

void outMethod(final int arg1, int arg2){

final int var1 =1;

int var2 = 2;

class LocalClass{

  void method(){

 int result = arg1+arg2+var1+var2;

}

}

}

 

 

 

인터페이스

package textbook.chapter9.exam02;

 

public interface Calculatable {

           public int sum();

}

 

익명 객체의 로컬 변수 사용

package textbook.chapter9.exam02;

 

public class Anonymous {

           private int field;

          

           public void method(final int arg1, int arg2) {

                       final int var1 =0;

                       int var2 = 0;

                      

                       field = 10;

                      

                       //arg1 = 20;//(x)

                       //arg2 = 20;//(x)

                      

                       //var1 = 30;//(x)

                       //var2 = 30;//(x)

                      

                       Calculatable calc= new Calculatable() {

 

                                  @Override

                                  public int sum() {

                                              int result = field + arg1+arg2+var1+var2;

                                              return result;

                                  }

                                 

                       };

                      

                       System.out.println(calc.sum());

           }

}

 

실행 구현

package textbook.chapter9.exam02;

 

public class AnonymousExample {

           public static void main(String[] args) {

                       Anonymous anony = new Anonymous();

                       anony.method(0, 0);

           }

}

 

확인문제

1. 중첩 멤버 클래스에 대한 설명으로 틀린 것은 무엇입니까? (4)

 

1. 인스턴스 멤버 클래스는 바깥 클래스의 객체가 있어야 사용될 수 있다.

2. 정적 멤버 클래스는 바깥 클래스의 객체가 없어도 사용될 수 있다.

3. 인스턴스 멤버 클래스 내부에는 바깥 클래스의 모든 필드와 메소드를 사용할 수 있다.

4. 정적 멤버 클래스 내부에는 바깥 클래스의 인스턴스 필드를 사용할 수 있다.

 

2. 로컬 클래스에 대한 설명으로 틀린 것은 무엇입니까? (3)

 

1. 로컬 클래스는 메소드 내부에 선언된 클래스를 말한다.

2. 로컬 클래스는 바깥 클래스의 모든 필드와 메소드를 사용할 수 있다.

3. 로컬 클래스는 static 키워드를 이용해서 정적 클래스로 만들 수 있다.

4. final 특성을 가진 매개 변수나 로컬 변수만 로컬 클래스 내부에서 사용할 수 있다.

 

3. 익명 객체에 대한 설명으로 틀린 것은 무엇입니까? (3)

 

1. 익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성될 수 있다.

2. 익명 객체는 필드, 매개 변수, 로컬 변수의 초기값으로 주로 사용된다.

3. 익명 객체에는 생성자를 선언할 수 있다.

4. 부모 클래스나 인터페이스에 선언된 필드와 메소드 이외에 다른 필드와 메소드를 선언할 수 있지만, 익명 객체 내부에서만 사용이 가능하다.

 

4. 다음과 같이 Car 클래스 내부에 Tire Engine이 멤버 클래스로 선언되어 있습니다. NestedClassExample에서 멤버 클래스의 객체를 생성하는 코드를 작성해보세요.

package textbook.chapter9.excersize;

 

public class Car {

            class Tire { }

            static class Engine { }

}

 

package textbook.chapter9.excersize;

 

public class NestedClassExample {

           Car myCar = new Car();

          

           Car.Tire tire = myCar.new Tire();

           Car.Engine engine = new Car.Engine();

}

 

5. AnonymousExample 클래스의 실행 결과를 보고 Vehicle 인터페이스의 익명 구현 객체를 이용해서 필드, 로컬 변수의 초기값과 메소드의 매개값을 대입해보세요.

인터페이스

package textbook.chapter9.excersize;

 

public interface Vehicle {

           public void run();

}

 

익명 구현 클래스와 객체 생성

package textbook.chapter9.excersize;

 

public class Anonymous {

           Vehicle field = new Vehicle() {

                      

                       @Override

                       public void run() {

                                  System.out.println("자전거가 달립니다.");

                       }

           };

          

           void method1() {

                       Vehicle localVar = new Vehicle() {

                                 

                                  @Override

                                  public void run() {

                                              System.out.println("승용차가 달립니다.");

                                  }

                       };

                       localVar.run();

           }

          

           void method2(Vehicle v) {

                       v.run();

           }

}

 

익명 구현 클래스와 객체 생성

package textbook.chapter9.excersize;

 

public class AnonymousExample {

           public static void main(String[] args) {

                       Anonymous anony = new Anonymous();

                       anony.field.run();

                       anony.method1();

                       anony.method2(

                                              new Vehicle() {

                                                        

                                                         @Override

                                                         public void run() {

                                                                    System.out.println("트럭이 달립니다.");

                                                                    

                                                         }

                                              }

                       );

           }

}

 

6. 다음 Chatting 클래스는 컴파일 에러가 발생합니다. 원인이 무엇입니까?

package textbook.chapter9.excersize;

 

public class Chatting {

           void startChat(String chatId) {

                       String nickName = null;

                       nickName = chatId;

                       Chat chat = new Chat() {

                                  @Override

                                  void start() {

                                              while (true) {

                                                         String inputData = "안녕하세요";

                                                         //String message = "[" + nickName + "]" + inputData;

                                                         //sendMessage(message);

                                              }

                                  }

                       };

                       chat.start();

           }

 

           class Chat {

                       void start() {

                       }

 

                       void sendMessage(String message) {

                       }

           }

}

 

5라인과 6라인의 String nickName = null; nickName = chatId;

 

String nickName = chatId; 로 바꾸어준다.

 

 

반응형

' > 이것이 자바다' 카테고리의 다른 글

11. 기본 API 클래스  (0) 2020.10.02
10. 예외처리  (0) 2020.10.01
08. 인터페이스  (0) 2020.09.30
07. 상속  (0) 2020.09.29
06. 클래스  (0) 2020.09.28

+ Recent posts