날아라쩡글이의 블로그입니다.

내부 클래스, 익명클래스 본문

중앙 HTA (2106기) story/java story

내부 클래스, 익명클래스

날아라쩡글이 2021. 9. 28. 21:43
반응형

Car라는 인터페이스에는 에는 여러가지 기능이 존재한다. 

이러한 Car객체는 이런기능을 가지고 있다. 이러한 기능들로 

인터페이스를 구현하게되면 표준안으로 인해서 구현하지 않을 기능까지 전부다 갖게 된다. 

단점으로는 

  1. 구현하지도 않을 메소드를 재정의하게 되고 
  2. 실제로 이차가 어디까지 구현을 하게 되는지 가독성이 떨어지게 된다. 
  3. 그렇기 때문에 인터페이스의 기능을 나눠서 구현받게 만들 예정이다. 

이것이 인터페이스의 분리의 법칙이다 SOLID의 법칙중의 하나로 

하나의 인터페이스를 4가지로 분리하여 하나이상의 인터페이스를 상속박아서 또 다른 표준을 만들었다. 

원래대로라면 전체의 기능을 구현했어야했다. 

그러나 인터페이스의 기능을 세부적으로 나눠서 다중상속을 진행하여 구현을 받도록 진행하였다. 

좀더 쉽게 구현이 가능하게 되었다. 

abstract로 상속받게 만들어서 각각의 차에서 구현하지 않게 만들어 낼 수 있다. 

  • 경차는 인터페이스로 명확하게 구현이 가능하게 되었다. 
  • 중형차의 경우 abstract로 주행보조편의사항으로 구현부담이 줄어들었고, 구현된 것을 포함했다. 
  • 경계가 뚜렷해졌다. 
  • 고급차의 경우 추상클래스로 만들어서 진행해도 된다. 
  • 고급차를 상속받아서 제네시스를 상속받을 수 있다. 
  • 기본편의사항이 다를경우 구현메소드에서 재정의를 해주면 된다. 
  • 기능을 조합해서 다양한 종류의 차량구현이 가능한다. 
  • 불필요한 소스코드 구현이 필요가 없어졌기 때문에 좀더 경제적인 코드 구현이 가능하게 되었다. (자신이 제공하지 기능을 구현해야하기 때문에 메모리에 대한 소모를 하지 않기 때문에 경제적으로 코드 구현을 한다. )
  • 인터페이스 기능을 사양별로 나눠서 분리시키고 조합해서 다양한 기능의 스펙, 표준안을 만들어 낼 수 있다. 
  • 복잡한 기능을 simple하게 만들어서 가독성이 높아지게 만들었다. 
  • 작은 인터페이스를 나눠서 조합하여 기준을 만들 수 있다. 
  • 구현한 구현클래스를 작성할 수 있다. 
  • 필요하면 추상클래스를 촉구할 수 있다. 
  • 하나의 큰 인터페이스보다는 작게 만들어서 다양한 경우의 수를 만들 수 있다. 
  • 이것이 인터페이스 분리의 원칙이다. 

inner class 내부클래스 : 클래스안의 클래스 

  • 내부클래스는 중첩클래스라고도 불린다. 
  • 클래스 내부에 정의된 클래스다. 
  • 클래스의 역활은 객체를 만들기 위해서 정의하는 것이다. 설계도이기 때문에 new연산자로 만들 수 있다. 
  • 설계도는 독립적으로 정의가 되어있는데, 설계도를 사용하면 재사용성이 높아진다. 객체 생성할 때 여러번 사용하는 것이다. 
  • 그러나 내부클래스를 정의하여 외부클래스의 멤버변수, 메소드에 접근이 가능하도록 만든 것이다. 본래 다른클래스에서의 접근의 경우 은닉화가 되어 있기 때문에 접근이 어렵다. 
  • 중첩클래스를 정의하는 이유는 
    1. 클래스가 특정클래스와 밀접한 관계를 맺고 있을 때, 클래스의 내부에 선언하는 것이 유리한 경우 사용한다. 
    2. 외부클래스의 구성요소들을 서로 쉽게 사용할 수 있기 때문에 사용한다. 
  • 내부클래스의 포함하고 있는 클래스를 컴파일하면 내부클래스의 갯수만큼 컴파일 된 클래스 파일이 추가로 생성된다. 
    • 식별자의 경우 문자, 숫자, 언더바, 달라표시를 사용할 수 있는데 특수문자가 되는 것은 언더바와 달라표시이다. 
    • 언더바의 경우 상수로 사용할 때 사용된다.
    • 내부클래스의 컴파일된 클래스의 파일명 : 외부클래스 $ 내부클래스. class이다.
  • 내부클래스의 종류는 총 3가지 이다. 
    1. 멤버 내부클래스 
    2. 로컬 내부클래스
    3. 정적 내부 클래스 
  1. 멤버 내부클래스의 경우 static이 없는 키워드이다. 
    • 외부클래스의 객체가 생성되면 사용할 수 있는 내부클래스 
    • 외부클래스의 모든변수, 모든 메소드를 자유롭게 사용할 수 있다.
  2. 로컬 내부클래스의 경우 메소드 내부에 정의된 내부클래스 이다 
    • 해당 메소드가 실행되는 동안만 사용할 수 있는 내부클래스 
    • 외부클래스의 모든 변수, 모든 메소드를 자유롭게 사용할 수 있다.
    • 로컬내부클래스가 정의된 메소드의 지역변수, 매개변수 중에서 값이 변하는 것은 사용할 수 없다. 
    • final 변수 혹은 final변수처럼 동작하는 변수만 사용할 수 있다. 
  3. 정적메소드의 경우 static이 있는 키워드 이다.  
    • 외부클래스의 객체생성여부와 상관없이 사용할 수 있는 내부클래스다. 
    • 외부클래스의 정적변수, 정적메소드만 사용할 수 있다. 

일반적으로 멤버내부클래스를 많이사용하고, 내부클래스 종류에 따라서 멤버 접근도에 따른 차이가 존재한다. 

멤버 내부 클래스 

  •  멤버변수와 멤버메소드 모두 사용 가능 하다. 
  •  외부클래스 구성요소를 전부 사용 가능하다. 
  • 멤버 내부클래스는 객체를 생성하고 사용할 수 있기 때문에 객체를 생성안하고 사용할 수 있는 정적인 것은 사용할 수 없다 (Static은 사용불가 )
  • 객체를 만들고 나서야 사용할 수 있다. 
  • 멤버 내부클래스에서는 정적변수를 선언할 수 없다. 
  • 멤버 내부클래스에서는 정적메소드를 선언할 수 없다. 
  • 정적변수와 메소드는 객체를 생성하지 않고 사용이 가능하기 때문이다

정적 내부 클래스 

  • 외부에서 내부를 사용하는 경우가 많다. 
  • 내부에서 외부의 것을 사용하는 경우가 적다. 
  • 정적 내부클래스의 경우는 별다른 제약없이 정적변수로 선언이 가능하며, 정적메소드로도 작성할 수 있다,
  • static이기 때문에 static에만  접근이 가능하다. 
  • 외부클래스의 멤버메소드를 사용할 수 없다. 멤버메소드의 경우 객체를 만들고 사용할 수 있기 때문에 사용할 수 없다. 

로컬 내부 클래스 

  • 로컬 내부 클래스는 정적변수를 정의 할 수 없다. 
  • 로컬 내부 클래스는 값이 변하는 변수의 값은 사용할 수 없다. 
  • 외부클래스의 멤버변수, 멤버메소드, 외부클래스의 정적변수, 정적 메소드를 사용할 수 있다. 
  • 로컬 클래스 내부에서는 정적메소드를 선언할 수 없다. 
  • 지역 내부 클래스는 많이 사용하지 않는다. 

 

멤버 내부 클래스의 사용법 

  • StudentScore 클래스를 생성한다. 
  • 그리고 StudentScore에 생성자를 입력한다. 
  • StudentScoreRepository를 생성하여, 로직구현 프로그래밍을 진행한다. 
  • private로 StudentScore[] 형태의 참조변수를 입력한다. 
  • database = new StudentScore[] -->StudentScore에 연결된 database를 입력한다. 
  • 학생의 getter성적정보를 반환하는 기능을 작성한다. 
  • 중간에 public ScoreStats getStats()입력하여, return 값으로 new ScoreStats();객체가 반환이 되도록 메소드를 작성한다. 
  • ScoreApp에서 메서드를 사용하면 ScoreStats의 객체가 반환이 된다. 
  • class는 만들었지만, 다른클래스에서 사용하지 않을 클래스를 입력을 원한다. 정보에 관련한 밀접한 관련이 있기 때문에 내부클래스로 입력을 넣어준다. 
  • 학생성적에 대한 통계정보를 제공하는 멤버내부클래스를 구현한다. 
  • 학생들의 정보가 어떻게 되어있는지 모르지만, 평균과 통계에 대해서 알고 싶어, 내부클래스를 입력한다. 
  • 클래스안에서만 사용할 것이기 때문에 별도로 클래스를 만들지 않고, 멤버내부클래스로 입력을 원한다. 
  • class ScoreStats으로 내부클래스를 정의해준다.
  • 객체를 생성해서 내부에서 계산된 통계정보를 전달하기 위하여 만들었는데, 
  • 중요한것은 전달해주는 메소드가 있어야한다. 객체반환기능이 직관적으로 있어야 하기 때문,
  • 객체통계정보를 반환해주는 클래스의 객체가 획득되어야하는데 
  • 그 것이 public ScoreStats getStats() {
  •                 return new ScoreStatis(); 
  • }
  • 이 메소드를 외부클래스에 작성을 해준다. 
  • ScoreStats의 타입이면서 return은 객체를 생성하여 반환해준다. 
  • ScoreApp의 클래스를 작성하여 main class로 작성을 해줄 것이다. 
  • 성적정보에 관련한 통계기능이 구현된 내부클래스를 가지고 있는 객체를 제공하는 클래스를 new연산자와 참조변수를 이용하여 생성을 해준다. 
  • 생성후 ScoreStats의 타입으로 작성하고 = 대입하며 성적정보에 관련한 통계기능이 구현된 외부클래스를 담는 참조변수를 사용하여 getStats();으로 new ScoreStats값을 대입을 해준다. 

그렇다면 왜 내부클래스를 사용하는 것일까?

  1. 클래스는 1개의 일만 해야한다. (단일책임의 원칙) : 스스로가 잘하는 것만 해주어야 한다. StudentScoreRepository의 기능의 경우 학생성적정보를 추가/ 변경/ 조회/ 삭제 의 업무를 진행하고 있기 때문에 통계까지 진행하는 것은 2가지의 일을 하는 것이기 때문이다. 
    • 내부클래스로 책임이 분리되어있기 때문에 다른 클래스가 책임지게 하는 것이 가능하다. 
  2. 추가/ 변경/ 조회/ 삭제끼리 통계끼리만 모여있었으면 좋겠다. 구별이 가능하게 하는 것은 응집력이 높다는 것이다. 
  3. 학생 정보의 저장방식이 달라지며, 배열에 저장을 할 수 있게 되었다.
  4. 내용안에 return타입이 어떻게 되던지 반환타입을 인터페이스로 작성을 해주면 구현을 받은 클래스의 경우 클래스 형변환으로 내부에 있는 같은 주소의 인터페이스를 바라보게 된다. 메소드 재정의로 인해서 결과값은 다르지만 어떤 저장매체에 사용하던지 사용법이 동일하게 작성될 수 있다. 

ScoreStats로 무조건 반환타입을 상위인터페이스로 작성하여, 어떤 방식으로 Data가 관리되던지 간에 통계는 ScoreState에 대해서만 반환이 이뤄질 수 있게 한다. 내부구현은 전부 다를수 있다. 가지고 오는 통계정보획득의 타입을 우리 구현타입과 구현체계로 구현해오면 된다. 

그렇가면 이런식으로 결과값을 도출해 낼수 있다. 

내부클래스를 바깥에서 작성하지 않은 경우는 

  • 분리해서 표준을 정의하여 내부클래스로 구현해놓고, 저장매체도 다른방식으로 이뤄져 있기 때문에 외부로 내보내기 직관성이 떨어지기 때문에 내부에 작성을 하였다. 
  • 멤버 내부 클래스의 일반적인 형태이다. 
  • 내부클래스의 구체적인 이름은 몰라도 어쨋거나 반환타입은 ScoreStats로 작성하면 클래스 형변환으로 값을 가지게 된다. 
  • getStats로 scoreStats를 획득하면 된다 (위에 작성해놓은 메소드의 형태 )
  • 1. 통계정보  2. 히스토리를 제공받을 수 있다. 
  • 이전에 그려놓은 것이다. 이 클래스들 모두 Interator interface를  implements하고 있다. 클래스들의 name은 전부 다르지만 사용하는 메소드들의 이름은 같기 때문에 클래스의 종류에 상관없이 내부클래스로 들어가 있어 사용이 가능하다. 
  • ArrayList list  = new ArraysList();
  • Iterater it = list.Iterator();
  • it.next()// 내부클래스의 이름은 몰라도 지장없고, 메소드 재정의를 실행할 수 있다. 
  • LinkedList list = new LinkedList();
  • Iterator it = list.Iterator();
  • it.next();//위와 마찬가지로 사용방법은 동일하게 이용할 수 있다.
  •  

정적내부클래스의 가장 대표적인 예시 Bulider패턴

  1. 생성자의  문제점은 어떤것이 있을까?
  2. 엄청나게 많은 생성자를 만들 경우 어떤 생성자인지 파악하기 어렵고, 순서를 파악하기 얼렵다는 것이다. 그리고 생성자를 여러가지 만들어야하는데 이렇게 만들지 말아보자
    1. 첫번째로는 외부클래스의 멤버변수와 생성자를 전부 private화를 시킨다. 
    2. getter/ setter을 입력한다. 
    3. 객체를 생성하는 정적메소드를 입력한다. 
      • public static EmployeeBuilder builder() {
      • return new EmployeeBuilder ();
      • }
      • 객체의 타입을 빌더로 만들어 호출시 new EmployeeBuilder(); 로 객체가 생성되게 만들어준다. 
      • static 으로 선언되었기 때문에 클래스 변수에 위치해 있어 객체를 생성하지 않고 입력을 해줄 수 있다. 
    4. EmployeeBuilder를 static클래스로 정적클래스로 만들어줘 내부에 위치할 수 있게 만들어준다. 
    5. 똑같이 입력받은 매개변수/ 매개변수의 생성자를 각각으로 만들어 준다. 
  3. 전부다 return으로 this객체를 받기 때문에 골라서 사용할 수 있는 장점이 존재한다. 
  4. 여기서 중요한 점 Employee객체를 생성하고 입력된 값을 사용해서 초기화된 Employee객체를 반환하도록 메소드를 추가로 입력해준다. 
  5. public Employee build() { //build라는 메소드를 이용하여 객체를 내보내줌
    Employee employee = new Employee(no,name,dept,position,email,tel,job,salary,commission,hireDate- 매개변수 전부 입력);
    return employee;} 이렇게 작성하여 메소드를 호출시 new연산자로 Employee 가 생성되게 만들어주고 작성을 해준다. 
  6. 그럼 Bulid()라는 메소드를 호출하게 되면 객체가 나오게 되는 것이다. 
  7. 이렇게 만들고 EmployApp으로 main메소드를 호출하면 
  8. Employee의 객체의 경우 private로 되어있기 때문에 생성자를 이용하여 생성할 수 없다. 
  9. 생성자도 객체를 생성하지 못한다. 
  10. 제일 많이 사용하는 방법으로 Bulider의 객체를 이용하여 객체를 생성하고 초기화 된 객체를 획득하는 방법으로 
  11. EmployeeBuilder(내부 정적클래스 )의 타입으로 참조변수를 만든다. 
  12. 그리고 = 대입연산자에는 Employee의 객체의 Builder();를 입력한다. 그렇다면? 
      • public static EmployeeBuilder builder() {
      • return new EmployeeBuilder ();
      • }
      • 이 호출되니 당연하게 new EmployeeBulider();연산자와 연결이 되는  것이다.
      • = 참조변수.no(100)
      • .name("홍길동") ....build();가 나올때까지 생성자를 전부호출, 혹은 호출을 몇개만 빼고 할 수 있다. EmployeeBuilder을 호출했기 때문에 
      • public Employee build() { // 맨마지막에는 build라는 메소드를 이용하여 객체를 내보내줌
        Employee employee = new Employee(no,name,dept,position,email,tel,job,salary,commission,hireDate- 매개변수 전부 입력);
        return employee;} 
      • 마지막에 build라는 메소드를 통하여 Employee를 new연산자로 주소값을 호출하였다. 

생성자가 너무 많으면 Builder를 사용하도록 권장된다. 

작성하기 어렵기 때문에 라이브러리화 되어있고, static의 내부클래스의 대표적인 사용방법으로 Builder패턴이 존재한다. 

이것이 메소드 체이닝이다. Builder패턴 사용하기, static 정적 내부클래스 사용하기 이다. 

 

부가적인 설정을 해서 내가 필요한 것만 볼 수 있게 만들 수 있음 --> Builder패턴으로 사용하고 가공함 

정적내부클래스로 빌드패턴 구현하기, 객체의 생성방법이 달라짐 

익명객체 

  • 익명객체란 이름이 없는 객체다 . 
    • 이름이 없다는 의미는 설계도가 없다는 것이다. 
    • 즉, 설계도인 클래스가 없다는 것이다. 
    • 설계도를 만드는 목적의 경우
      • 객체를 생성해야한다. 
      • 똑같은 객체를 여러개를 만들기 위해서다.
  • 주로 상위클래스를 상속받거나 인터페이스를 구현해서 즉석에서 객체를 생성하는 것이다. 
  • 상위클래스나 인터페이스 없이 단독으로 객체를 생성할 수 없다. 
  • 단 한번 사용되는 기능이 필요하다면 별도의 클래스 정의 없이 익명객체로 생성하는 것이 유리하다. 
    • 주로 GUI프로그램의 이벤트 처리, 스레드 구현들에 사용된다. 
  • 어플을 생성하고 이전으로 가던지 next 새로고침, 새로열기등의 클릭을 하면 동작이 일어나지만 굳이 클래스를 만들어서 구현할 필요가 없는 것이다. 
  •  

 

  • 각각의 버튼의 기능이 전부 다 다르고
  • 버튼도 많고 구현도 각각 다르고 1개만 사용되고, 객체는 1번만 사용된
  • ex) 그림판의 버튼의 경우 버튼마다 구현이 전부 다르고 1개밖에 없고, 버튼은 23개밖에 없음 
  • 1개를 그냥 클래스 name없이 만들어서 클릭시 출력이 되도록 만듦, class의 갯수가 많아지면 힘듦
  • Click 인터페이스를 구현한 클래스는 대부분 하나의 버튼과 연결되어 있다. 
  • Click 인터페이스를 구현한 클래스로 만들어진 객체는 여러곳에서 사용되는 일이 없다. 
  • Click인터페이스를 구현한 클래스는 사용되는 곳이 한 곳뿐이기 때문에 객체를 여러번 생성할 일도 없다. 
  • 따라서 객체를 하나만 만들어서 사용하는데, 굳이 클래스까지 만들 필요가 있을까?(의문)
    • 익명객체로 클래스정의를 하지 않고 객체를 생성하는 거야
    • 익명객체가 필요한 경우이다.
    • 즉석에서 객체를 구현한다.
    • 인터페이스를 구현하는데 Click의 타입으로 구현을 해준다. 
    • 이런식으로 작성할 수 있다. 
    • 타입은 인터페이스의 타입으로 작성하고 참조변수 = Click의 타입의 생성 메소드를 구현한다. 
    • 람다식의 표현방법 : 무조건 performed의 메소드가 있다는 것을 확인 할 수 있다. 어떤 인터페이스인지 추론이 가능하며 메소드가 1개밖에 없을 때만 사용이 가능하다. 
      • 사용하게 되면 실제로는 = new Click (){
      • public void performed(){}가 생략이 되었다는 것을 알 수 있다. 
    • 인터페이스의 내용을 확인후 사용 확인이 가능하고, 자바의 8버젼부터 람다식표현방법이 가능하며 버젼 7부터 타입추론이 가능하다. 
    • Event방식으로 개발패턴을 정의해놓는 것, 그리고 event를 이용하여 연결시켜 놓았다. 
    • 구현해놓은 익명객체가 실행되도록 만들어 놓았다. 
    • 익명객체 생성방법을 사용하면 별도의 클래스 구현없이 객체를 생성한다. 
    • 익명객체를 생성하기 위해서는 반드시 부모 (인터페이스와 추상클래스 ->거의 인터페이스가 사용됨)가 필요하다
    • 부모의 불완전메소드를 재정의한 코드로 객체를 생성한다
    • 익명클래스를 확인해보면 
    • class로 이렇게 만들어져있다. 즉, 익명클래스의 객체도 내부클래스로 생성이 된다는 뜻이다. 
    • 이렇게 바라보고, 메소드 재정의가 실행되어서 구현프로그래밍이 다르게 구현된다는 것을 알 수 있다. 

익명객체는 어디에 사용될 까?

  1. 이벤트 처리, 누를 때마다 화면이 바뀌는 것
    1. CLI
      • commend Line Interface
      • cmd를 이용하여 명령어를 직접 쳐서 수행결과가 나오게 하는 방법
    2. GUI
      • Grapic User Interface
      • 그래픽으로 눈에 그려지게 미리 창이 걸려있어서 마우스등을 이용하여 실행되게 만드는 방법 클릭시 내용이 다르게 나오는 것. 

Event Driven Development(이벤트 주도 개발)

  • GUI 프로그램은 Event Model를 이용해서 프로그램을 개발하고 사용자와 상호작용을 하도록 만든다. 
  • Event Model의 주요 구성 요소들
    1. EventSource 
      • 사용자와의 상호작용으로 Event를 발생시키는 곳
      • 메뉴, 버튼, 입력필드, 체크박스, 셀렉터박스 등
      • EventSource에서 기대하는 Event가 발생할 때 실행하는 곳 
    2. EventListener
      • EventSource에서 기대하는 Event가 발생하는지 기다리고 있는 곳 
      • Queue의 형태의 배열에서 FIFO의 형태로 기다리고 있다. 
      • EventSource에서 기대하는 Event가 발생하면 미리등록해도 EventHandler가 실행된다. 
    3. EventHandler
      • EventSource에서 기대하는 Event가 발생했을 때 실현할 코드가 구현되어 있는 곳 
    4. Event
      • 사용자가 프로그램과 상호작용(클릭, 키입력, 마우스클릭하기 등등) 할때마다 생겨나는 곳
      • Event는 상호작용방식으로 상호작용대상들의 정보가 포함되고 있다. 
      • 마우스를 움직일 때마다 이벤트라는 객체가 생성된다. 
    5. 개발자의 역활은??
    6. 개발자의 경우 eventSource를 선정하고 , EventHanler를 구현하고, EventHanler를 등록을 한다. 
    7. 모든 응용프로그램 웹, IOS, 안드로이드가 이벤트 모델기반으로 프로그램이 되어있다. 
    8. 이벤트 모델의 경우 자바의 이벤트 처리로 이벤트 발생, 이벤트 발생확인, 등록된 이벤트 헨들러 실행의 경우 자바의 System이 실행된다. (JVM이 실행된다. 즉, 프로그램이 알아서 진행한다. )

 

 

 

 

 

숙제에서 가장 중요한것 return의 빠른 종료를 사용하여, 아래가 뭐가 있던지 실행되지 않도록 하는 것이 중요하다. 코드의 복잡성을 감소시키고 잘못된 경우를 빠르게 손절할 수 있다. 

 

반응형

'중앙 HTA (2106기) story > java story' 카테고리의 다른 글

Exception ,throw ,try~catch문  (0) 2021.10.08
추상화와 인터페이스 정리  (0) 2021.09.28
추상클래스  (0) 2021.09.26
추상화  (0) 2021.09.24
상속, 매개변수, 멤버변수, 반환타입의 다형성  (0) 2021.09.20
Comments