본 게시글은 대학 전공수업을 들으며 노션에 정리한 내용을 블로그로 옮긴 것으로, 노션 웹을 통해 최적화된 형태로 읽으시길 권장드립니다.(➡️ 노션 링크)
6강 - 제네릭, 람다식
제네릭
- 클래스, 인터페이스, 메소드를 정의할 때 타입 매개변수(파라미터)를 선언하고 사용 가능
- 재사용성을 높이고 오류를 줄임
장점
여러 유형에 걸쳐 동작하는 일반화된 클래스 및 메소드 정의
자료형 한정 → 컴파일 시점 자료형 검사 가능
캐스트(형 변환) 연산자 사용 불필요
예
ArrayList 클래스는 List 인터페이스를 구현한 클래스
class ArrayList<E> implements List<E> { public boolean add(E e) { ... } public E get(int index) { ... } //'E'를 리턴함 public E remove(int index) { ... } ... }
public class Main { public static void main(String args[]) { //제네릭 사용x List list1 = new ArrayList(); list1.add("hello"); String s1 = (String) list1.get(0); // 형변환 필요 //제네릭 사용 List**<String>** list2 = new ArrayList**<String>**(); list2.add("hello"); String s2 = list2.get(0); // 형변환이 필요 없음 } }
제네릭 클래스
- 클래스 정의에서 클래스 이름 오른쪽에 타입파라미터를 선언
접근제어자 class 이름<T1,T2...> {...}
- 타입 파라미터는 참조형만 가능
- 필드 자료형, 메소드 반환형, 인자의 자료형으로 사용 가능
- 타입 마라미터 이름은 관례적으로 E, K, V, N, T를 사용
- 컴파일할 때 명확한 타입검사 수행 가능
- ✅메소드 호출시 인자 유형이 맞는지
- ✅메소드 호출 결과를 사용할 때 유형이 맞는지
- 자료형 매개변수로 가지는 클래스와 인터페이스를 ‘제네릭 타입’이라고 함.
- 제네릭 타입을 사용하면 ‘컴파일’ 시점에서, 사용하지 않으면 ‘실행’시 오류가 발생함.
2개의 매개변수 제네릭 인터페이스
코드
interface Pair<K, V> { public K getKey(); public V getValue(); } class OrderedPair<K, V> implements Pair<K, V> { private K key; private V value; public OrderedPair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } public class PairGeneric { public static void main(String[] args) { Pair<String, Integer> p1; p1 = new OrderedPair<>("Even", 2); Pair<String, String> p2; p2 = new OrderedPair<>("hello", "java"); } }
제네릭 인터페이스를 구현하는 일반 클래스
클래스를 정의할 때 제네릭 인터페이스 ‘<>’안에 자료형을 지정하면 됨
코드
class MyPair implements Pair<String, Integer> { private String key; private Integer value; public MyPair(String key, Integer value) { this.key = key; this.value = value; } public String getKey( ) { return key; } public Integer getValue( ) { return value; } } public class Study { public static void main(String args[ ]) { MyPair mp = new MyPair("test", 1); } }
Raw타입
타입 매개변수 없이 사용되는 제네릭 타입
- 타입 검사를 하지 못하므로, 권장 되지는 않음
제네릭 타입인데, 일반 타입처럼 사용하는 경우
타입 매개변수를 Object로 처리함
코드
Data data = new Data("hello");
Data는 제네릭타입 Data
의 raw 타입 class Data<T> { private T t; public Data(T t) { this.t = t; } public void set(T t) { this.t = t; } public T get() { return t; } }
제네릭 메소드
- 자료형을 매개변수로 가지는 메소드
- 하나의 메소드 정의로 여러 유형의 데이터를 처리 할 수 있음
- 메소드 정의시 반환형 왼편에 ‘<타입매개변수>’ 표시
- 타입 매개변수를 반환형, 매개변수의 자료형, 지역변수 자료형으로 사용 가능
//제네릭 메서드 – 배열에서 두 개의 객체를 교환
public **<T>** void swap(**T[]** array, int i, int j) { //반환형과 매개변수에 사용
T temp = array[i]; //지역변수의 자료형으로 사용
array[i] = array[j];
array[j] = temp;
}
인스턴스 메소드, static메소드 모두 제네릭 메소드로 정의 가능
제네릭 메소드를 호출할 때 타입을 명시하지 않아도 됨.
코드
class Util public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } public class GenericMethod { public static void main(String args[]) { Pair<Integer, String> p1 = new OrderedPair<>(1, "apple"); Pair<Integer, String> p2 = new OrderedPair<>(2, "pear"); //boolean same = Util.<Integer, String>compare(p1, p2); //메소드 호출시 타입 명시 boolean same2 = Util.compare(p1, p2); //타입 명시하지 않아도 문제 없음 System.out.println(same); } }
타입 제한
제네릭(클래스/인터페이스/메소드)를 정의할 때 자료형에 제한 두는 것
T Extends Number
와 같이 T를 상한으로 정할 수 있음이 경우, T의 실제 자료형은 Number의 서브클래스여야 함.
예
class Data<T extends Number> {...} public class Main { public static void main(String args[]) { Data<Integer> data1 = new Data<>(20); System.out.println(data1.get()); Data<String> data2 = new Data<>("Hello"); //오류 } }
타입과 형 변환
상속관계까 있으면 상위/하위 자료형의 관계가 존재함.
Integer나 Double은 Number의 자식클래스이지만,
Data
와 Data 사이에는 상속관계가 없다. 즉,
Data<Number> data = new Data<Integer<(); //상속관계 없어서 불가 Number n = new Integer(10); // 상속관계 있으니 가능 //단! class FormattedData<T> extends Data<T> {...} Data<Integer> data2 = new FormattedData<Integer>(); // 자식객체로 formatting했으므로 대입 가능
제네릭 사용시 유의사항
✅ 기본 자료형은 타입 매개변수X
❌ Data
✅타입 매개변수로 객체 생성 불가
❌ class Data
✅타입 매개변수 타입으로 static 데이터필드 선언 x
❌ class Data
✅제네릭 타입의 배열을 선언할 수 없음
❌ Data
람다식
인터페이스를 구현하는 익명클래스의 객체 생성 부분을 수식화
- 구현할 것이 1개의 추상메소드 뿐인 경우, 간단히 표현할 수 있음
// 람다식을 사용하지 않은 경우
Runnable r1 = new Runnable() {
public void run() {
System.out.println("Hello, World!");
}
};
//람다식(실행문이 1개 : '{}' 생략)
Runnable r2 = () -> System.out.println("Hello, World!");
즉, 인터페이스 객체변수 = (매개변수 목록) {실행문}을
인터페이스 객체변수 = () → {실행문}으로 표현!
문법
객체 생성 부분’만’ 람다식으로 표현!
- ❌익명 서브클래스의 객체 생성을 람다식으로 표현
✅인터페이스에는 추상 메소드가 1개만 있어야 함.(=함수형 인터페이스)
- ❌2개 이상의 추상메소드를 포함하는 인터페이스는 불가
매개변수 목록에서 자료형을 생략해 변수이름만 사용
- 자료형은 인터페이스(타깃타입)에서 알 수 있음
실행문이 1개 → 중괄호 생략
실행문이 return문 1개 → 중괄호, return 모두 생략
- f2 = (a,b) → a+b;
코드
interface Addable { int add(int a, int b); } public class LambdaEx1 { public static void main(String[] args) { // 람다x - 익명 구현 클래스의 객체 생성 Addable ad1 = new Addable() { public int add(int a, int b) { return (a + b); } }; System.out.println(ad1.add(100, 200)); //람다o Addable ad2 = (a, b) -> a + b; System.out.println(ad2.add(1000, 2000)); } }
interface MyInterface1 { public void method(int a, int b); } interface MyInterface2 { public void method(int a); } public class LambdaEx2 { public static void main(String args[]) { MyInterface1 f1, f2, f3; MyInterface2 f4, f5; //람다식 : 객체생성 부분만 f1 = (int a, int b) -> { System.out.println(a + b); }; f1.method(3, 4); //람다식 : 자료형까지 생략 f2 = (a, b) -> { System.out.println(a + b); }; f2.method(5, 6); //람다식 : 중괄호까지 생략 f3 = (a, b) -> System.out.println(a + b); f3.method(7, 8); //-------------------------------------------------------- //람다식(인자 하나) f4 = (int a) -> { System.out.println(a); }; f4.method(9); //람다식 : 자료형 + 중괄호 생략 f5 = a -> System.out.println(a); f5.method(10); } }
함수형 인터페이스
: 1개의 추상메소드만 가지는 단순한 인터페이스.
- java.util.function에서, 표준 함수형 인터페이스가 ‘제네릭 인터페이스’로 정의 됨.
- Consumer
는 void accept(T t)를 가짐 - Supplier
는 T get() 메소드를 가짐 - Function<T,R>은 R apply(T t)를 가짐
- Consumer
- 함수형 인터페이스를 구현하는 클래스를 정의할 때, 익명 클래스 정의를 활용할 수 있으나 람다식이 효율적!
'Back-End > Java' 카테고리의 다른 글
(전공 정리) 9강 - Java.io : 입출력스트림, 파일 입출력 (0) | 2024.06.22 |
---|---|
(전공 정리) 8강 - java.lang 패키지: Object, String, StringBuffer (0) | 2024.06.22 |
(전공 정리) 7강 - 패키지와 예외처리 (0) | 2024.06.22 |
(전공정리) 5강 - 추상 클래스, 인터페이스, 다형성, enum, 익명클래스, 중첩클래스 (0) | 2024.06.22 |
(전공 정리)4강 - static, final, 오버로딩/오버라이딩, 상속, this, super, 메소드체이닝 (0) | 2024.06.22 |
(전공 정리) 3강 - 배열, String, Scanner, 클래스, 접근제어자, 생성자 (0) | 2024.06.22 |