。゚(*´□`)゚。

코딩의 즐거움과 도전, 그리고 일상의 소소한 순간들이 어우러진 블로그

[네이버클라우드] 클라우드 기반의 개발자 과정 7기/웹프로그래밍

[NC7기-43일차(6월26일)] - 웹프로그래밍 24일차

quarrrter 2023. 6. 26. 17:49

제네릭(Generic)적용 

: Object 타입처럼 다양한 타입에 대응할 수 있다.

: 특정 타입으로 제한할 수 있다. 

=> 마치 각 타입 별로 클래스를 정의한 듯한 효과를 볼 수 있다. 

 

1. 다형적 변수 적용 전 

BoardHandler <- BoardList(<-Board), MemberHander <- MemberList(<-Member) 각 객체의 배열을 저장 

단점: 각 타입별로 List 클래스를 만들어야 한다. 타입은 다른데 List의 기능은 같음. => 코드 중복 

 

2. 다형적 변수 적용 

Object [ ]<-ArrayList(<-MemberHandler & BoardHandler)

Object: Member, Board 담을 수 있음, 다양한 타입의 인스턴스(주소)를 저장할 수 있다. => 타입별로 List를 만들 필요가 없다.

단점:

인스턴스를 꺼낼 때마다 형변환을 해야해서 귀찮음 

특정 타입만 다루도록 제한할 수 없음 => 타입 안정성을 해친다. 

 

3. Generic => Type parameter

클래스가 다룰 타입정보를 넘긴다.

class ArrayList<What> { 

   public void add(What obj) { ... }

   public what get(int index) { ... }

}

What : 타입 이름을 받을 변수 = Type parameter

 

 

 

 

What: 타입을 받는 변수 . List를 코드에서 사용하는 시점에 타입을 받겠다 .

Board를 받으면 다 Board, Member면 Member 

일반 변수가 아닌 타입을 받는 거니까 보통 이름을 알파벳 하나로 "T"로 적음 

 

 

 

 

 

 

 

일반 변수로 오해할 수 있기 때문에 what 대신 What이라고 적기. 

일반 클래스처럼 오해할 수 있기 때문에 What이라고 적기보다 알파벳 하나로 적기. 

일반 변수가 아닌 타입을 받는 거니까 보통 이름을 알파벳 하나로 "T"로 적음 

 

<많이 쓰는 거 >

E Element / 어떤 목록(항목)에서 하나, 리스트에 저장할 항목의 타

K Key / hash map에서 key 객체를 가르킬때

N Number / 숫자를 가르킬때

T type

V value

S U V = 2nd, 3rd, 4th types / 여러개를 받아야할 상황이면 덧붙이기.

 

 

1 받는 변수 2로 넘김 1에서 Member를 받으면 List에 Member를 넘김

하지만 E의 타입이 지정되지 않은 상태에서 배열을 생성할 수 없음. 컴파일러 문제! (그냥 그런갑다,,)

제네릭을 사용할 땐 배열 생성하는 곳에선 쓸 수 없다. 

 

경고 띄우지마 supress = >경고 뜨는건 다 없애기 

(형변환)

(E)

 

제네릭에서 타입을 안 알려주면 Object가 기본임 .

 

<T> : 타입 파라미터. 클래스 아니고 파라미터라는 걸 나타내는 것. // toArray를 호출할 때 넘어오는 파라미터를 따라 타입이 결정됨 / / T 라는 타입을 메서드를 호출하는 시점에 넘어오는 타입을 따라 배열을 리턴하겠다

 

객체를 만들어주는 메서드는 팩토리 메서드임. 

객체 생성과정이 복잡할 때 팩토리 메서드를 만들어서 사용함. 

내가 지정한 타입

 


Iterator 패턴을 활용하여 데이터 조회기능을 객체로 추출하기 (캡슐화하기)

1. 목록의 값을 조회하기 

ArrayList get(index)로 값 꺼내기 /리스트로  데이터 저장 방식에 따라 데이터를 조회하는 방법이 다름.  => 데이터 저장 방식에 상관없이 일관된(통일된) 방식으로 데이터를 조회하기
LinkedList get(index)로 값 꺼내기 /리스트로
stack pop()으로 값 꺼내기 /LIFO
Queue poll()로 값 꺼내기 /FIFO

 

통일된 방식

hasNext() / next()
Iterator
데이터를 꺼내주는 일을 하는 객체
get() ArrayList
get() LinkedList
pop() stack
pull() Queue

 

*Iterator mechanism(구동원리)

저장방식에 상관없이 일관된 방식으로 값을 꺼낼 수 있다.

*client : 1. network 분야: 서버에 요청하는 역할을 수행하는 S/W  2. oop 분야: 다른 객체를 사용하는 객체(class)

2. 인터페이스에 따라 만든 클래스의 인스턴스를 리턴한다는 뜻

 

*Iterator 패턴 구현

1. 구현 1

객체지향 프로그램 유지 보수를 위해 쌍방 참조는 안 됨 ! 

 

2. 구현 2- static nested class (중첩클래스 사용)

 

ListIterator = top level class = package member class 일 땐 굳이 빼놓지 말구 중첩클래스로 넣기 ,, !

3. 구현 3 - non static nested class 

4. 로컬 중첩클래스 

특정 메서드 안에서만 사용할 클래스라면 그 메서드 안에 정의하는게 유지보수에 좋다.

인스턴스 메서드 안에서 로컬 클래스 추가하기. 

앞에 this를 붙이면 안 됨

눈에는 안 보이지만 바깥 클래스의 인스턴스 주소를 받는 생성자와 그 주소를 저장할 인스턴스 변수가 자동으로 추가된다. 

 

 

5. anonymous class익명 중첩클래스 

자체적으로 바깥 파라미터를 받을 생성자가 자동으로 생김 

new Iterator<> () { ... }

인스턴스 생성

인터페이스 명 또는 클래스 명 

수퍼클래스 생성과 호출을 위한 파라미터 값

익명클래스 바디(몸체)

 

 @Override
  public Iterator<E> iterator() {

    // 방법1: top level class
    //    return new ListIterator<>(this);

    // 방법2: static nested class
    //    return new ListIterator2<>(this);

    // 방법3: non-static nested class
    //    return this.new ListIterator3<>();
    // 위 문장은 컴파일러가 return new ListIterator3<>(this) 문장으로 변경한다.

    // 방법4: local class
    //    class ListIterator4<T> implements Iterator<T> {
    //      // 여기 눈에는 안보이지만, 바깥 클래스의 인스턴스 주소를 받는 생성자와
    //      // 그 주소를 저장할 인스턴스 필드가 자동으로 추가된다.
    //      int cursor;
    //      @Override
    //      public boolean hasNext() {
    //        return cursor < AbstractList.this.size();
    //      }
    //
    //      @SuppressWarnings("unchecked")
    //      @Override
    //      public T next() {
    //        return (T) AbstractList.this.get(cursor++);
    //      }
    //    }
    //    return new ListIterator4<>();
    // 위 문장은 컴파일러가 return ListIterator4<>(this) 문장으로 변경한다.

    // 방법5: anonymous class
    return new Iterator<E>() {
      // 여기 눈에는 안보이지만, 컴파일러가 바깥 클래스의 인스턴스 주소를 받을 생성자와 필드를 자동으로 추가한다.
      int cursor;
      @Override
      public boolean hasNext() {
        return cursor < AbstractList.this.size();
      }
      @Override
      public E next() {
        return AbstractList.this.get(cursor++);
      }
    };
  }
  // static nested class
  static class ListIterator2<T> implements Iterator<T> {
    List<T> list;
    int cursor;

    public ListIterator2(List<T> list) {
      this.list = list;
    }

    @Override
    public boolean hasNext() {
      return cursor < list.size();
    }

    @Override
    public T next() {
      return list.get(cursor++);
    }
  }

  // non-static nested class = inner class
  class ListIterator3<T> implements Iterator<T> {

    // 바깥 클래스의 인스턴스 주소를 받아서 보관할 인스턴스 변수가 자동으로 추가된다.
    //    List<T> list;

    int cursor;

    // 바깥 클래스의 인스턴스 주소를 받는 생성자가 자동으로 추가된다.
    //    public ListIterator2(List<T> list) {
    //      this.list = list;
    //    }

    @Override
    public boolean hasNext() {
      return cursor < AbstractList.this.size();
    }

    @SuppressWarnings("unchecked")
    @Override
    public T next() {
      return (T) AbstractList.this.get(cursor++);
    }
  }