ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [이펙티브 자바] 아이템24. 멤버 클래스는 되도록 static으로 만들라
    기타/북스터디 2023. 1. 9. 12:05

    1. 멤버 클래스는 무엇일까?

    해당 아이템에 대해 정리하기 전에 큰 키워드인 멤버 클래스가 무엇인지 알아보자.

    멤버라는 의미는 어떠한 클래스를 구성하는 요소를 의미하고 그에 따라 멤버 클래스는 어떠한 클래스를 구성하는 요소인데 그게 클래스형태인 것이다.

    클래스의 멤버에는 아래와 같이 필드, 메서드, 클래스 등이 있을 수 있다.

    public class OuterClass {
    
      // 멤버 필드(혹은 변수)
      private String name;
      
      // 멤버 메서드
      public void foo() {
      
      }
      
      // 멤버 클래스
      public Class InnerClass {
      }
    }
    더보기

    (보통 비정적 멤버 클래스를 그냥 통상적으로 멤버 클래스라고 부르지만 여기서는 더 큰 의미로 해석해봤다.)

    - 참고자료: https://docstore.mik.ua/orelly/java-ent/jnut/ch03_10.htm

     

     

    2. 중첩 클래스란 무엇일까?

    중첩 클래스란 다른 클래스 안에 정의된 클래스를 의미한다.

    필차가 말하는 것 중 하나는 중첩클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야한다고 한다.

    중첩 클래스의 종류는 정적 멤버 클래스, (비정적) 멤버 클래스, 익명 클래스, 지역 클래스로 4가지가 존재한다.

    여기서, 정적 멤버 클래스는 내부 클래스(inner class)가 아니라고 얘기해주는데 이 부분에 대해서 처음에는 이해가 가지 않았고 찾아본 결과, 그냥 내부 클래스(inner class)라는 정의 자체가 Non-static한 중첩 클래스라고 한다.

    (참고자료: https://www.javatpoint.com/java-inner-class)

     

     

    3. 정적 멤버 클래스란 무엇일까?

    정적 멤버 클래스는 어떠한 클래스 안에 선언되어진 static한 클래스로 바깥 클래스의 static한 private멤버에도 접근할 수 있다.

    특징 중 하나로는 OuterClass의 인스턴스가 필요하지 않고 독립적이다.

    정적 멤버 클래스는 아래와 같이 선언할 수 있다.

    public class OuterClass {
    
      private static String name = "JM";
      
      public static class InnerClass {
        void doSomething() {
          System.out.println(name);
        }
      }
    }
    
    
    import Item12.Foo.InnerClass;
    public class Client {
      InnerClass Innerclass = new InnerClass();
      innerClass.doSomething();
    }

     

     

    4. 정적 멤버 클래스와 비정적 멤버 클래스의 차이는 무엇일까?

    모양새의 차이는 static keyword가 붙냐 안붙냐의 차이이지만 더 들여다보면 큰 차이는 비정적 멤버 클래스의 인스턴스는 바깥 클래스이 인스턴스와 암묵적으로 연결된다는 것이다.

    즉, 비정적 멤버 클래스에서 바깥 클래스의 인스턴스에 대한 참조가 생긴다는 것이고 바깥 클래스 인스턴스 생성없이는 비정적 멤버 클래스 자신의 인스턴스를 생성할 수 없다는 것이다.

    이러한 원리에 따라 비정적 멤버 클래스이 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다.

     

    비정적 멤버 클래스는 아래와 같이 선언하여 사용할 수 있다.

    public class OuterClass {
    
      private String name = "JM";
      
      void printNumber() {
        InnerClass innerClass = new InnerClass();
      }
      
      public class InnerClass {
        void doSomething() {
          System.out.println(name);
          OuterClass.this.printNumber();
        }
      }
    }
    
    
    import Item24.Foo;
    public class Client {
      InnerClass Innerclass = new OuterClass().new InnerClass();
      innerClass.doSomething();
    }

    위처럼, inner class가 outer class 인스턴스에 대한 참조가 많으면 비정적 멤버 클래스로 선언하는 것이 적합할 수 있다.

     

     

    5. 멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙이자.

    멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 static을 붙이는 것을 권고하는 이유는 아래와 같다.

    1. 비정적 멤버 클래스로 선언 시, 비정적 멤버 클래스의 인스턴스 안에 관계 정보가 저장되어 메모리 공간이 낭비되고 생성 시간도 정적 멤버 클래스에 비해 더 걸린다.

    2. 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못하는 경우가 발생할 수 있다.

     

     

    6. 익명 클래스란 무엇인가?

    익명 클래스는 말그대로 이름을 가지고 있지 않은 클래스로 위에서 얘기한 정적 멤버 클래스, 비정적 멤버 클래스와 달리 바깥 클래스의 멤버가 아니다.

    그렇기에 선언되는 시점(직접 익명 클래스가 쓰이는 시점)과 동시에 인스턴스가 만들어진다.

    익명 클래스를 사용하여 작성한 코드는 아래와 같다.

    public class IntArrays {
      static List<Integer> intArrayAsList(int[] a) {
        Objects.requireNonNull(a);
        
        return new AbstractList<>() {
          @Override
          public Integer get(int index) {
            return null;
          }
    
          @Override
          public int size() {
            return 0;
          }
        };
      }
    }

    하지만, java 8에서 추가된 람다 표현식과 메서드 레퍼런스로 익명 클래스를 대체할 수 있기에 java 8이후 버전을 사용한다면 굳이 익명 클래스로 가독성이 좋지 않은 코드를 작성할 필요는 없다.

     

    이러한 익명 클래스의 단점은 아래와 같다.

    1. 선언한 지점에서만 해당 인스턴스를 만들 수 있고 instanceOf와 같이 클래스의 이름을 필요로 하는 데에서는 사용할 수 없다.

    2. 여러 인터페이스를 구현할 수 없고 인터페이스를 구현하는 동시에 다른 클래스를 상속할 수 없다.

    3. 익명 클래스를 사용하는 클라이언트는 그 익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출할 수 없다.

    4. 가독성이 떨어진다.

     

     

    7. 지역 클래스란 무엇인가?

    지역 클래스는 지역변수를 선언할 수 있는 곳이면 어디서든 선언할 수 있고 유효 범위도 지역변수와 같다.

    public class MyClsss {
    
      void doSomething() {
        class LocalClass {
          private void printNumber() {
            System.out.println(number);
          }
        }
        
        LocalClass localClass = new LocalClass();
        localClass.printNumber();
      }
    }

    댓글

Designed by Tistory.