728x90
반응형
제네릭의 의미
- 제네릭 클래스, 제네릭 인터페이스, 제네릭 메소드
-> 클래스, 인터페이스 메소드를 정의할 때 타입 매개변수(타입 파라미터)를 선언하고 사용 가능
장점
- 여러 유형에 걸쳐 동작하는 일반화된 클래스나 메소드 정의 가능
- 자료형을 한정함으로 컴파일 시점에 자료형 검사 가능
-> 실행 오류를 찾아 고치는게 어려울 수 있기 때문 - 캐스트(형변환) 연산자의 사용 불필요
// ArrayList 클래스는 List 인터페이스를 구현한 클래스
class ArrayList<E> implements List<E>{
bloolean add (E e) {}
E get(int index) {}
E remoce (int index) {}
}
// 제네릭 사용 x
List list = new ArrayList(); // 제네릭이 아니라면 문자열을 Object유형으로 관리함
list1.add("hello"); // 자바의 모든 클래스는 Object를 상속받는다
String s1 = (String)list1.get(0); // 형변환 필요 ( hello가 Object기 때문에 다운 캐스팅)
// 제네릭 사용
List<string> list2 = new ArrayList<String>();
list2.add("hello");
String s2 = list2.get(0); // 형변환 필요 없음
제네릭 클래스
- 클래스 정의에서 타입 파라미터를 선언함
-> 클래스를 사용할 때는 타입을 명시 - 타입 파라미터는 참조형만 가능
-> 필드의 자료형, 메소드 반환형, 인자의 자료형으로 사용할 수 있음 - 컴파일 할 때, 명확한 타입 검사를 수행할 수 있음
-> 메소드 호출 시 인자의 유형이 맞는지
-> 메소드 호출의 결과를 사용할 때 유형이 맞는지 - 자료형을 매개변수로 가지는 클래스와 인터페이스를 제네릭 타입이라고 함
제네릭 클래스의 정의
문법
- class 클래스이름<T1,T2,...> {}
- 클래스 정의에서 클래스 이름의 오른편 <>괄호 안에 타입 파라미터 표시
- 콤마로 구분하여 여러 개의 타입 파라미터 지정 가능
- 타입 파라미터는 타입을 전달 받는다
- 타입 파라미터의 이름은 관례적으로 E, K, V, N, T 등을 사용
제네릭 클래스의 필요성
- 제네릭 타입과 자료형 검사
-> 제네릭 클래스를 사용하지 않으면 컴파일 시점에서 오류 검출 불가 - 의미가 명확하면 생성자 호출 시 괄호만 사용해도 된다
-> Data2<String> b3 = new Data2<>();
제네릭 인터페이스를 구현하는 일반 클래스
-> 클래스 정의 시 인터페이스의 <> 안에 자료형 지정
Raw 타입
- 제네릭 타입이지만 일반 타입처럼 사용하는 경우에 제네릭 타입을 지칭하는 용어
- 타입 매개변수 없이 사용되는 제네릭 타입
-> 자료형을 Object로 처리함 - 예 : Data2 data = new Data2("hello");
-> Data2는 제네릭 타입 Data2<T>의 Raw타입 (타입이 명확하지 않기 때문에 컴파일 시 경고문 출력됨)
제네릭 메소드
- 자료형을 매개변수로 가지는 메소드
-> 하나의 메소드 정의로 여려 유형의 데이터를 처리할 때 유용함- 메소드 정의에서 반환형 왼편, 각 괄호 <> 안에 타입 매개변수 표시
-> 타입 매개변수를 메소드의 반환형이나 메소드 매개변수의 자료형, 지역변수의 자료형으로 사용 가능
- 메소드 정의에서 반환형 왼편, 각 괄호 <> 안에 타입 매개변수 표시
piblic static <T> T getLast(T[] a) {
return a[a.length-1]; // 모든 타입의 마지막 원소를 반환
}
- 인스턴스 메소드와 static 메소드 모두 제네릭 메소드로 정의 가능
- 제네릭 메소드를 호출할 때, 타입을 명시하지 않아도 인자에 의해 추론 가능
class Util {
public static <K, V> boollean compare(Pair<K, V> p1, Pair<K, V> p2){
return p1.getKey().equals(p2.getKey())&&p1.getValue().equals(p2.getValue());
}
}
public class GenericsTest5{
public static void main(String args[]) {
Pair<Integer, String> p1 = new OrderedPair<>(1, "apple");
Pari<Integer, String> p2 = new OrderedPair<>(2, "pear");
boolean same = Util <Integer, String> compare(p1, p2);
System.out.printli(same);
}
}
제네릭의 타입 제한
- 자료형을 매개변수화하여 클래스/인터페이스/메소드를 정의할 때, 적용 가능한 자료형에 제한을 두는 것
<T extends Number> T의 자료형을 Number를 상한으로 정한다
T에 주어지는 자료형은 Number의 서브 클래스만 가능 (Integer, short, double)
제네릭 타입과 형변환
- 상속 관계가 있어야만 상위/하위 자료형의 관계가 존재
-> Integer는 Number의 자식 클래스이지만 Data<Number>와 Data<Integer>는 상하위 관계 없음
class Data<T> {}
class FormattedData<T> extends Data<T> { // 상속 관계
}
public class GenericTypeConversion1 {
public static void main(String args[]){
Data<Number> data = new Data<Number>();
data.set(new Integer(10));
data.set(new Double(10));
Data<Number> data1 = new Data<Integer>(); // 컴파일 오류
Data<Integer> data = new formattedData<Integer>();
}
}
제네릭 타입 사용 시 유의 사항
- 기본 자료형은 타입 매개변수로 지정할 수 없음
-> 참조형만 가능 - 타입 매개변수로 객체 생성 불가
- 타입 매개변수의 타입으로 static 불가
- 제네릭 타입의 배열 선언 불가
람다식
- 인터페이스를 구현하는 익명 클래스의 객체 생성 부분을 수식화 한 것
-> 1개의 추상 메소드를 구현할 때 간단히 표현 가능
람다식 구문
- 메소드 매개변수의 괄호, 화살표, 메소드 몸체로 표현
-> 인터페이스 객체변수 = (매개변수) -> {실행문}
Runnable runnable = new Runnable(){
public void run(){}
};
// 람다식은 객체 생성 부분 생략
Runnable runnable = () -> {};
람다식 기본 문법
익명 구현 클래스의 객체 생성 부분만 람다식으로 표현
-> 익명 서브 클래스의 객체 생성은 람다식이 될 수 없다인터페이스에는 추상 메소드가 1개만 있어야 함
람다식의 결과 타입을 타깃 타입이라고 함
1개의 추상 메소드를 포함하는 인터페이스를 함수적 인터페이스라고 한다
-> 메소드가 1개 뿐이므로 메소드 이름 생략 가능
-> 람다식은 이름 없는 메소드 선언과 유사하다
람다식의 활용
함수적 인터페이스
- 1개의 추상 메소드만 가지는 단순한 인터페이스를 함수적 인터페이스라고 한다
- 패키지 java.util.function 에서 표준 함수적 인터헤이스가 제네릭 인터페이스로 제공됨
- 함수적 인터페이스를 구현하는 클래스를 정의할 때, 익명 클래스 정의를 활용 가능하지만 람다식이 효율적
표준 함수적 인터페이스의 예
- Consumer<T>는 void accept(T t)를 가짐
- Supplier<T>는 T get() 메소드를 가짐
- Function<T, R>은 apply(T t)를 가짐
반응형
'프로그래밍 언어 > Java' 카테고리의 다른 글
Java) 10/19 - 자바와 jdk (0) | 2021.10.19 |
---|---|
Java) 8. java.lang 패키지 (0) | 2021.06.14 |
Java) 인터페이스와 다형성 (0) | 2021.04.21 |
Java) 4. 클래스와 상속 (0) | 2021.04.09 |
Java) 3. 기본 문법(2) - 배열과 문자열, 클래스 (0) | 2021.03.30 |
댓글