강의) 네오 2단계 List + 제네릭
List(ArrayList)와 제네릭(PE-CS) 개념 설명
📜 제목으로 보기
- 리스트
- 제네릭
- Type과 제네릭은 영역 좁히는게 반대다?
- ArrayList는 구현체 List가 인터페이스(추상체)
- 선언시, 변수 생략된T <> 형 + 파라미터, 클래스 <T>형의 제네릭에서의 추상체 <—> 호출시구상체 <특정형> 을 지정해서 사용한다.
- my) intellij의 생성자 파라미터를 T로 지정하여 Generic을 생성한 선T -> 호구지 (선언시 T형 -> 호출시 구상형을 지정해서 사용)
- 제네릭 추상체 <T>형으로 box클래스 정의하기
- my) 제네릭은 Type과 반대로, 변수 및 생성자에서는 제네릭 구상체 인 <특정형> 변수에 , 할당(사용)시 생략된T형<>을 넣어줘, 변수와 생성자에서 좁힌다.
- 할당(사용)시 생략된T형<>을 넣지만, 좁히는 변수와생성자 자리에 들어간<특정형> 변수가 추상체인 인터페이스라면, 넓히는 할당 자리에 <특정형 인터페이스의 구상체형들> 들도 할당가능하다
- 좌항 좁히는 변수가 <특정형>의 제네릭구상체라면 -> 마찬가지 좁히는 생성자에 들어갈 변수도 특정형으로 처리되어야함. 할당(사용, 특정분기)은 생략된T형(제네릭추상체)의 기본생성자 호출 불가
- 할당시 제네릭구상체<특정형>을 지정해주면, 내부의 모든 것이 T->특정형으로 바뀐다. 할당은 변수보다 넓어야하니 변수는 더 좁은 특정형or특정형의 구상체들로 받아줘야할 것이다.
- 좁았던, 제네릭 변수정의 - 생성자 인자의 관계에선, Type처럼 변수-추상체라면, 생성자인자-구상체의 관계를 가진다.
- 실사용 와일드카드
- 제네릭의 클변 메파 정의/ 좁 변수(와) + 메파 제한자(와X) -> 생성자/ 넓 할당
- 제네릭은 정의(class 속 변수, method 파라미터)는 T(E)로 / 변수(와일드카드)와 method 파라미터는 <T> (<E>) 좁게 + 생성자(좁힌 것의 구현체&자식들) + / 할당은 넓게(default로 두면 알아서 생성자에 사용되는 구현체에 맞게)
- P-E C-S(제네릭와일드카드 사용처)
리스트
-
테스트부터 만든다.
-
test>java
에서class
를 앞에패키지.
를 붙혀 만들면 패키지 + class를 동시에 만들 수 있다.list.ListTest
public class ListTest { @Test void name() { new ArrayList<>(); } }
-
ArrayList vs LinkedList
ArrayList
-
arrayList는 내부에
단순 배열
을 가지고 있다.배열
을 사용하기 좋게 감싼일급컬렉션
transient Object[] elementData; // non-private to simplify nested class access
- 배열을 그냥 쓰면, 배열크기가 고정되어있지만, 일급으로 만들어서 크기를 유동적으로 증가 감소, 복사해서 대체해주기 등을 한다.
private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); }
LinkedList
-
배열이 아니라 시작과 끝
노드
를 가지고 있으며 연결되어있다.transient Node<E> first; /** * Pointer to last node. */ transient Node<E> last;
-
연결되어있으므로 배열 공간에 미리할당할 필요없이 하나씩 node를 추가하면 된다.
저장 -> 접근 방식
- 배열의 arrayList: 인덱스로 한번에 접근가능하다.
-
인덱스로 한번에
중간원소 접근
가능 -> 빠르다.- 메모리 공간을 미리 할당해야하므로, 메모리 딸리는 상황에서는 데이터 추가시 느릴 수 있다.
중간원소 삭제
시인덱스를 한칸식 다 땡겨줘야
하므로 시간이 오래 걸린다.- 중복처리 방식
- 리스트 안에 리스트를 넣는방식 이용
- 크기가 커지면 리스트 안에 트리를 넣는 방식 이용
- 사이즈에 맞게 다르게 사용되어서.. api를 사용하는게 좋다.
-
인덱스로 한번에
- 노드의 LinkedList: 통해서 가야하는 줄줄이 노드.
-
타고가야하는 줄줄이 노드는
중간원소 접근
-> 느리다. 중간원소 삭제
시 편하게 삭제
-
타고가야하는 줄줄이 노드는
하드디스크의 물리적으로 순차적인 접근
- 옛날 하드디스크 = 물리적으로 순차적으로 메모리 배열 -> arraylist와 마찬가지 방식 -> 유리했었음
- 요즘은 linkedList가 메모리 파편화된 node들의 집합이어도 별 상관없어졌다.
제네릭
- list처럼 다양한type을 관리하는 경우, 특정type으로 고정시킬 수 있다.
- 제네릭이 멀까?
- 컴파일 타임(핵심)에 처리가 되서, 그 때 버그를 잡아낼 수 있다.
- 클래스를 변수로 만들기?
- 제네릭이 멀까?
제네릭은 컴파일 타임에 처리된다.
-
1을 arrayList에 넣고 출력하는 코드를 작성하고 실행시켜 컴파일
@Test void name() { final ArrayList<Integer> integers = new ArrayList<>(); integers.add(1); System.out.println(integers); }
-
out(build)
부터 시작되는 컴파일코드로 가서shift2번 > show bytecode
를 쳐서 내부적으로 실행된 코드를 확인해보자.// access flags 0x0 name()V @Lorg/junit/jupiter/api/Test;() L0 LINENUMBER 9 L0 NEW java/util/ArrayList DUP // ArrayList 생성할 때, 제네릭정보는 없다. INVOKESPECIAL java/util/ArrayList.<init> ()V ASTORE 1 L1 LINENUMBER 10 L1 ALOAD 1 ICONST_1 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // integer가 들어갈 때, Object로 들어간다. -> 컴파일 될 때 알아서 Type으로 떨어진다. 컴파일 타임까지만 Type실수하지않게 도와주는 것. 런타임 때는 안도와준다. INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z POP L2 LINENUMBER 12 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L3 LINENUMBER 13 L3 RETURN L4 LOCALVARIABLE this Lgeneric/GenericTest; L0 L4 0 LOCALVARIABLE integers Ljava/util/ArrayList; L1 L4 1 // signature Ljava/util/ArrayList<Ljava/lang/Integer;>; // declaration: integers extends java.util.ArrayList<java.lang.Integer> MAXSTACK = 2 MAXLOCALS = 2
-
결과적으로
외부에서 타입을 지정
할 수 있게하는 기술이다.- 생성시에는 타입없이 내부에서 생성
- 외부인 변수에 할당시 타입지정
ArrayList<Integer> integers = new ArrayList();
- 외부에서 타입을 맘대로(나 인티져 넣을거야, 나 스트링 넣을 거야~)지정해서 편하게 사용한다.
final ArrayList<Integer> integers = new ArrayList<>(); final ArrayList<String> strings = new ArrayList<>();
장점
- (사용시 특정타입으로 제한하여) 타입안정성을 얻을 수 있다.
- 캐스팅해서 사용하던 것을 자동으로 처리되도록 됨.
my) [여러 타입가능한 자료구조]사용시 get하면 object로 내려와 (1) 타입확인 (2) 다운캐스팅 해서 사용했어야 했는데 사라지게 된다.
- jdk 5부터 추가되었다.
- 다른type, 다른형을 막아주기 때문에
특별한 이유가 없으면 여러타입가능 자료구조는 무조건 generic
을 사용해라
@Test
void name() {
final List integersNoGeneric = new ArrayList();
integersNoGeneric.add(1);
// 여러타입 가능한 것에 대해 제네릭으로 타입을 안정해준다면,
// 1. <받아올 때> Object형으로 내려온다. -> <받아 사용하는 쪽에서> 다운캐스팅 해야 쓸 수 있다.
final Object o = integersNoGeneric.get(0);
// -> [넣은 것과 동일한 형으로 직접 다운 캐스팅]해야줘야 다시 해당 type으로 사용할 수 있다.
final Integer integer = (Integer) integersNoGeneric.get(0);
// 2. 다른 Type을 넣어버릴 수 도 있어서 type불안정하다.
integersNoGeneric.add("1");
// -> <사용하는 쪽, 타입까지 직접확인해줘야>해서 -> 사용할 때 [넣은 것과 동일한지 type확인도 미리] 다운캐스팅전에 해줬어야했다.
final Object o1 = integersNoGeneric.get(0);
if (o1 instanceof Integer) {
final Integer o11 = (Integer) o1;
}
}
Type과 제네릭은 영역 좁히는게 반대다?
ArrayList는 구현체 List가 인터페이스(추상체)
- type을 받아줄 변수는
인터페이스(추상체)
로 받아야, 메서드들이분기를 대신하는 구상체(전략객체)를 받을 수 있는 추상체(인터페이스) 파라미터
로 정의되어있음을 인지해야한다.
변수와 파라미터
는 전체 분기들을 내포되도록 추상체
로 받고, 사용하는 할당과 메소드호출 인자
에는 분기가 결정된 구상체
를 넣어주자.
my) Type의 경우) 정의하는 -
파라미터를 분기결정된 구상체로 받는다면?
- 다른 구상체들을 못받는다.
void 파라미터를_분기결정된_구상체로_넣는경우(ArrayList<Integer> value) { System.out.println(value); }
// 1. default로 변수추출시 ArrayList 풀로 형이 정해지는데 final ArrayList<Integer> integersArrayList = new ArrayList<>(); // 2. vs 정의하는 변수와 파라미터는 전체분기들을 내포하는 추상체로 받아라 -> 할당과 메소드호출인자에는 분기결정한 구상체를 넣어주자. final List<Integer> integersList = new ArrayList<>(); //3. 정의하는 메소드파라미터를 분기결정된 구상체 -> 해당 구상체밖에 못들어온다. 파라미터를_분기결정된_구상체로_넣는경우(integersArrayList); // 에러안남. 파라미터를_분기결정된_구상체로_넣는경우(integersList); // 에러남 (구상체 정의 -> 추상체를 넣음) }
-
파라미터를 분기내포하는 추상체(인터페이스)로 받는다면?
- 파라미터를 추상체로 받는다? -> 분기들을 내포하여 구상체들을 다 받을 수 있는 상황
-
인터페이스로 분리해서 내가 어떤이점을 얻고 있지? 잘활용하고 있나 고민할 것
- 전략패턴에서는, 파라미터를 전략인터페이스로 정의후
- 호출시점에 전략객체.전략메서드() 호출시켜서 실시간성 부여
- 사용하는 메인에서 메서드호출시 new 구상체를 던져 분기를 정해줌
void 파라미터를_분기내포하는_추상체로_넣는경우(List<Integer> value) { System.out.println(value); }
파라미터를_분기내포하는_추상체로_넣는경우(integersArrayList); // 에러안남. 파라미터를_분기내포하는_추상체로_넣는경우(integersList); // 이것도 에러 안남. //메소드 호출시, 여러분기들의 구상체를 다 받을 수 있다.
생략된T <>
형 + 파라미터, 클래스 <T>
형의 제네릭에서의 추상체 <—> 호출시구상체 <특정형>
을 지정해서 사용한다.
선언시, 변수 my) intellij의 생성자 파라미터를 T로 지정하여 Generic을 생성한 선T -> 호구지 (선언시 T형 -> 호출시 구상형을 지정해서 사용)
- 호출부에서 T형을 집어넣을 수 없으니 -> 일단 아무형이나 넣어서 생성자에 넣고 내부에서 T로 바꿔서 정의한다.
- T형으로 파라미터를 받아도, T는 빨간줄이다. 빨간줄
[Create type T paramter]
를 통해 class 전체를 선택해서 클래스자체도Class<T>
형으로 지정해준다.- 필드가 아닌, 파라미터 속 T에 대해서만 해당 T형 파라미터 자동생성이 활성화된다.
<T>
형으로 box클래스 정의하기
제네릭 추상체 - 외부에서 생성자를 통해 빨간줄로 생성할 땐, 아무형이나 집어넣고 -> 내부에서 T형으로 정의후 ->
Create T type parameter
를 통해 class에도<T>
로 선언되도록 해야한다. (선T호구지)
-
제네릭 추상체
<T>형
클래스 만들기class Box<T> { private final T value; // 1. T형 변수을 감싸는 클래스 box를 만든다. T형이란 미정이므로 -> 2. 클래스 정의시 <T> 제네릭을 달아줘야한다. //3. T형변수를 변수를 생성자를 통해 초기화하고 public Box(final T value) { this.value = value; } //4. T형 변수를 getter로 응답한다. public T getValue() { return value; } }
변수 및 생성자
에서는 제네릭 구상체
인 <특정형> 변수
에 , 할당(사용)
시 생략된T형<>
을 넣어줘, 변수와 생성자에서 좁힌다.
my) 제네릭은 Type과 반대로, -
제네릭 추상체
생략된T형 <>
변수, 제네릭 구상체<특정형>
변수 각각 생성자 대입해보기- 변수가 제네릭 구상체이라면, 할당도 해당하는 형밖에 못넣는다.특정형>
@Test void name2() { final Box<String> stringBox = new Box<>(""); //final Box<String> stringBox = new Box<>(1); // 제네릭은 변수와 생성자에서 좁힌다. 할당시 제네릭추상체-생략된T<>형으로 넓게준다. }
할당(사용)
시 생략된T형<>
을 넣지만, 좁히는 변수와생성자 자리에 들어간<특정형> 변수
가 추상체인 인터페이스
라면, 넓히는 할당 자리에 <특정형 인터페이스의 구상체형들>
들도 할당가능하다
넓은 할당자리에 특정형을 집어넣으면 해당 클래스만 사용가능해진다.
-
무엇이든 다 들어갈 수 있는 제네릭 추상체
Box<T>
에 대해서제네릭 구상체 Box<특정형>
을 지정해줬지만, 그 특정형이 구상체들을 가진다면우변에서 구상체들의 할당
이 가능하다// 8.제네릭에서는 생략된T형인 Box<>에 비해 Box<특정형>은 제네릭구상체다. -> 할당이 <특정형>의 자체 및 그 구상체들만 가능해진다. // -> Animal이 제네릭 특정형으로 들어가 그 제네릭 구상체가 되었더라도, 그것의 구상체들인 Dog, Cat은 할당될 수 있다. new Box<Animal>(); final Box<Animal> animalBox = new Box<Animal>();
변수
가 <특정형>
의 제네릭구상체라면 -> 마찬가지 좁히는 생성자
에 들어갈 변수도 특정형
으로 처리되어야함. 할당(사용, 특정분기)은 생략된T형(제네릭추상체)
의 기본생성자 호출 불가
좌항 좁히는 변수만 좁히고, 할당은 안좁혔을 때 -> 기본 생성자에 인자가 들어간다면 넓은 할당형에 쓰던 것이 생성자로 들어갈 예정이니 에러가난다 -> 인자를 안받는 생성자를 만들어줘서 생략된T형의 인자가 못들어가게 막아주자.
-
List(추상) = ArrayList(구상)
는 내가 특정분기를 우항에서 할당하여 사용하는 것이다.- 하지만 현 상황에서는 제네릭 구상체인
Box<Animal> (구상)
=new Box <> () (추상:생략된T형)
을 넣는 것은 정반대로 주는 것 - **
좌변 변수정의
에서구상체 특정형으로 제한
되어있다면,뭔짓을 한들 생략된 T형 = 제네릭추상체 사용시, 생성자에 건네주는 것으로 class내부 T형 변수
를 초기화해줄 수 없기 때문이다. **
// 9. 좁히는 변수만 좁히고, 할당은 안좁혔을 때 -> 기본 생성자에 인자가 들어간다면 넓은 할당형에 쓰던 것이 생성자로 들어갈 예정이니 에러가난다 final Box<Animal> animalBox = new Box<>(); // 생성자쪽에서 에러 // -> 인자를 안받는 생성자를 만들어줘서 생략된T형의 인자가 못들어가게 막아주자. // -> default생성자(파라미터T가 안되니, 파라미터를 아예 안받고, 내부 T형은 null로 초기화시켜주는)를 추가해주자. public Box() { this(null); }
final Box<Animal> animalBox = new Box<>();
-
마치
제네릭 구상체 변수
에제네릭추상체 할당
이 가능해 보이지만,생성자가 파라미터를 외부에서 아예 안받고 내부적으로 자체 처리해서 제네릭추상체를 감당
하고 있기 때문에 그렇게 보이기만 할 뿐이다.- 특수처리되어서 에러는 사라졌으나 사용은 불가하다.?!
- 하지만 현 상황에서는 제네릭 구상체인
할당
시 제네릭구상체<특정형>
을 지정해주면, 내부의 모든 것이 T->특정형으로 바뀐다. 할당은 변수보다 넓어야하니 변수
는 더 좁은 특정형or특정형의 구상체
들로 받아줘야할 것이다.
// 11. 이미 좁힌 제네릭변수가 있을 때, 할당은 생략된T형 <>으로 넓게 줄 수 있다. (넓은 형으로 들어갈 생성자만 잘 처리되면)
final Box<Animal> animalBox2 = new Box<>();
// 12. 할당에서 특정형을 제네릭구상체로 지정해주면, 내부의 모든 로직이 해당 classType으로 바껴서
// 12-1. 동일형으로 좁힌 변수에는 잘들어간다.
final Box<Animal> animalBox3 = new Box<Animal>();
// 12-2. 만약 넓은 할당쪽이 더 좁아졌다면? (좁힌 변수보다 더 좁은) Type과 다르게 안들어가진다. -> 변수와 생성자가 더 좁아야한다.
final Box<Animal> animalBox4 = new Box<Dog>(); // 에러
변수정의
- 생성자 인자
의 관계에선, Type처럼 변수-추상체라면, 생성자인자-구상체
의 관계를 가진다.
좁았던, 제네릭 //13. 할당시 생략된T형<> -> 변수-생성자 인자 관계에서는, Type처럼 변수 추상체 - 생성자=메서드호출 구상체가 가능하다.
//13-1. 할당시 생략된 T<>으로 내부가 T로 구성된 데이터에, 좁은 생성자 인자를 넣어줄 때, 추상체에 구상체가 들어가므로 괜춘하다.
final Box<Animal> animalBox4 = new Box<>(new Dog());
//13-2. 할당이 넓은 상태라면, 변수-추상체 생성자-구상체로서 동일한 것도 생성자로 넣을 수 있다.
final Box<Dog> animalBox5 = new Box<>(new Dog());
실사용 와일드카드
좁은 변수제네릭에 와일드카드
를 지정해주고 넓은 할당은 생략된T형
이 기본인 상태에서, 넣어줄 생성자에 변수의 좁은제네릭에 들어있는 구상체or상속or와일드카드 범위의 객체를 넣어 사용
해줘야한다.
실사용은 //14. 넓은 제네릭할당에 대해
new Box<>();
//14-1. 좁은 변수의 제네릭에 와일드카드(upper bound) 적용주자.
final Box<? extends Animal> extendsAnimalBox = new Box<>();
//14-1-1. 실사용은 변수에 대해 생성자에 넣어줄 놈은 더 구상체로 넣어주면 된다.
final Box<? extends Animal> extendsAnimalBox2 = new Box<>(new Animal(){});
final Box<? extends Animal> extendsAnimalBox3 = new Box<>(new Dog());
와일드카드변수(제네릭추상체)
구현체를 상속한 것들
도 넣어줄 수 있다.
상속자 자리에 //15. [Animal의 구현체인 Dog]를 상속한 것들도 제네릭 상속자 자리에 넣어줄 수 있다.
// -> 술을 먹어서 개가 된 돌범(상속) 정의
class Dolbum extends Dog { }
//15-2. 제네릭 변수-상속자의 관계는 구상체 자식들 하위단계를 다 넣어줄 수 있다.
final Box<? extends Animal> extendsAnimalBox4 = new Box<>(new Dolbum());
넓은 할당쪽 제네릭은 사실, 생성자에 들어갈 객체의 범위가 default다.
- 할당쪽 제네릭은 와일드카드 사용불가
- 특정형 사용시, 생성자도 그걸로
- 생성자를 넣어줄 시, 그거랑 동일한 것이 default
와일드카드에 대해, 상속자들어갈놈 -> 할당제네릭의 default가 안정해줄 경우, 어떤 Type으로 잡힐까?
-
제일 defaultType은 제일 상단이 나온다.
- Type입장에서는 열려있는 최고 부모를 변수type으로 받아줘야한다.
//16. 와일드카드에 대해, 상속자들어갈놈 -> 할당제네릭의 default가 안정해줄 경우, 어떤 Type으로 잡힐까?
final Box<? extends Animal> extendsAnimalBox5 = new Box<>();
//16-1. upperbound를 가진 extends 와일드카드의 경우, 제일 상단인 Animal이 나오고
final Animal value = extendsAnimalBox5.getValue();
final Box<? super Animal> superAnimalBox5 = new Box<>();
//16-2. lowerbound를 가진 super의 경우, 제일 하단인 Animal이 나올 것 같지만,
// -> 역시 제일 상단인 Object가 나온다.
// --> 와일드카드의 default는 제일 위아래쪽 상관없이 상단 bound ~ Object까지 나오게 된다.
final Object value2 = superAnimalBox5.getValue();
제네릭의 클변 메파 정의/ 좁 변수(와) + 메파 제한자(와X) -> 생성자/ 넓 할당
- Type은
정의하는 변수, 메서드 파라미터
에는 추상체를 /사용하는 할당, 메소드호출
에는 구상체를 넣어서 분기처리를 밖으로 제거해줄 수 있다.
T(E)
로 / 변수(와일드카드)와 method 파라미터는 <T> (<E>)
좁게 + 생성자(좁힌 것의 구현체&자식들) + / 할당은 넓게(default로 두면 알아서 생성자에 사용되는 구현체에 맞게)
제네릭은 정의(class 속 변수, method 파라미터)는
<E>
로 정의 (좁은 변수Type처럼)
메소드 파라미터는 제네릭E로 정의해주지만 (클래스 변수 타입 T형처럼) /히는 작업이 접근제한자와 응답사이에 좁은 꺽쇠//1. T는 클래스의 변수 정의하느라 썼으니
// -> 파라미터는 E로 받지만, 제한자 쪽에다가 꺽쇠붙인 <E>로 좁혀준다.
// -> <E>는 별다르게 좁혀주지 않는 default 어느타입이든 가능임.
public <E> void some(E e) {
}
//2. 제네릭은 변수에 좁게 정의하고, 그 와일드카드에 해당 범위의 객체를 생성자에 넣어주니, 변수까지 정의해서 객체를 생성한다.
new Box<>();
final Box<? super Animal> superAnimalBox6 = new Box<>();
final Object value1 = superAnimalBox.getValue(); // 와일드카드에 구상체 객체를 않넣어주면, T형의 인스턴스변수를 Object로 잡아 반환한다.
final Box<?> value6 = superAnimalBox; //객체는 ? 와일드카드에 그냥 넣어줄 수 있다?
value6.some(""); // E는 기본적으로 Object부터 시작하여 -> 메서드호출시 어떤Type이든지 호출 가능해진다.
value6.some(1); // E는 기본적으로 Object부터 다 받을 수 있다.
메소드 파라미터
는 제한자 쪽에서 좁힌 Type <E>
에 와일드카드 or 직접 꺽쇠에 제한
을 걸수 있다.
좁은 변수처럼, - 잘보면 응답Type void보다 앞쪽에서, 파라미터에 대한 와일드카드 제한을 거는 것이다.
//3. 메서드의 파라미터의 좁힘 제한은 응답Type앞쪽에서 꺽쇠<E>제네릭으로 걸 수 있다.
public <E extends String> void some(E e) {
}
//4.
value6.some(""); // string 이하로 제한건 E에는.. string 및 string상속한 것만 들어간다.
value6.some(1); // 에러
참고) E는 와일드카드 범위내에서 사용된 Type이 추론끝나면 컴파일 타임에서 해당Type으로 대체되어 사라진다.
참고) 정의를 해주는 class옆 꺽쇠 + method파라미터용 앞쪽 꺽쇠에서는 와일드카드 사용이 안된다. 변수 제네릭에서만 와일드카드 가능
// 와일드카드 사용 좁힘시 에러
public <? extends String> void some(E e) {
}
// 와일드카드 사용시 에러
class Box2<? extends Animal> {
}
메서드 파라미터 E
를 좁힐 땐, 와일드카드가 아닌 꺾쇠<E extends >
로 좁히며, super불가 + 와이드카드 불가다..
추가 메서드에서 class와 동일한 타입추론 변수를 사용해도 된다. but…
public <T> void some(T t) {
}
메서드 파라미터의 변수는 지역변수라 … class쪽과 다르다.
//2. class내부에서 동일한 타입추론 변수T를 사용한 메서드는.. 파라미터로 T가 추론된 Dog형만 사용가능할까?
// -> class정의시에서는 T로 똑같은게 사용된 것 같지만, 메서드 파라미터의 변수 -> 지역변수 -> class 제네릭 타입변수 T랑은 별개다.
// -> 타입추론한 Dog와 별개로, 제한안걸린 T로서 모든 타입이 다들어간다.
dogBox.some(new Object());
dogBox.some(1);
dogBox.some("1");
- E로 바꿔도 똑같이 지역변수로 적용되므로… 개별적 처리가 된다.
- 되도록이면 이름 중복 안되게 하는게 좋다.
P-E C-S(제네릭와일드카드 사용처)
-
생성
관련된메소드의 파라미터
에 대해서는extends 상속한 것들만 들어올 수
있게- ArrayList의 생성자 파라미터 정의시
- ArrayList의 add메서드들의 파라미터 정의시
- 소비하는 곳에서는 super를 써서 제네릭을 정의해라
? extends
가 어디서 사용되는지 검색 해보자.
ArrayList를 타고 들어가서 -
생성자 파라미터에서
E의 상속한 자식들(extends)만 들어오도록 제한
을 걸어준다. -
add의 파라미터할때도
? extends
를 사용한다.
? super
는 소비되는 곳에서 사용된다.
-
super는 어떤 값이든 들어와도 되며, 소비되는 곳에 사용된다.
- 소비만 되므로, 실제 어떤 타입인지 중요하지 않다.
-
action은 람다 학습시 필요하다.
- 람다로 넘겨줄 때, Consumer, Predicate 사용시 많이 이용된다.
public void forEachRemaining(Consumer<? super E> action) {