Software Engineering/Java & JSP

[Java] 제5장 제네릭과 람다식

iseop 2023. 4. 30. 19:16

제네릭

  • 다양한 타입이나 객체에 대해 동작하게 만든 클래스를 제네릭이라 한다.
  • 제네릭을 정의할 때 다양한 타입 파라미터를 선언하고, 사용할 때 필요한 타입을 지정한다.
  • 제네릭이 주는 장점은 다음과 같다. 
    • 컴파일러에 의한 타입 검사가 가능해진다.
    • 명시적 형변환이 불필요해진다.
  • 제네릭의 제한 사항은 다음과 같다.
    • 기본 자료형을 타입 매개변수에 사용할 수 없다.
    • 제네릭 타입의 배열 선언은 불가하다.
    • 타입 매개변수를 static 필드에는 사용할 수 없다.
  • 타입 매개변수는 클래스 식별자 뒤에 <>로 표시한다. 
    • List<String> l = new ArrayList<String>();
    • l.add("ilovejava");    String s = list.get(0);
  • 타입 매개변수는 필드의 타입이나 반환형으로 사용된다.
  • <>를 다이아몬드 연산자라 하며, 타입 매개변수를 지정한다.
  • 의미가 명확한 경우, 두번째 다이아몬드에서 타입 지정을 생략할 수 있다.  
    • List<String> s = new ArrayList<String>();
    • List<String> s = new ArrayList<>();
  • 제네릭을 정의할 때 여러 타입 매개변수를 지정할 수 있다.
  • 제네릭을 다른 제네릭의 매개변수로 지정 가능하다.
  • 일반 클래스와 제네릭 클래스의 예시
일반 클래스 제네릭 클래스
class NormalClass { 
    private Object obj;
    public void set(Object obj) {
        this.obj = obj;
    }
    public Object get() {
        return obj;
    }
}
class GenericClass<T> {
    private T obj;
    public void set (T obj) {
        this.obj = obj;

    }
    public T get() {
         return obj;
    }
}

 

제네릭 메소드

  • 자료형을 매개변수로 가지는 메소드를 제네릭 메소드라 한다.
public class MyClass {
    public static <X, Y> boolean compare(Pair<X, Y> p1, Pair<X, Y> p2) {
        ... 
    }
}

 

제네릭의 상속과 형변환

  • 제네릭 타입 간 명시적 상속관계를 정의해야 형변환이 가능하다.
  • class C <T> { ... };
  • 아래 코드는 컴파일이 불가하다.
    • class CC <T> { ... }; 
    • C<Integer> obj = new CC<Integer>();
  • 아래 코드는 컴파일 가능하다.
    • class CC <T> extends C <T> { ... };
    • C<Integer> obj = new CC<Integer>();

제네릭의 타입 제한

  • 다이아몬드 안에 사용 가능한 자료형에 제한을 둘 수 있다.
  • 예를 들어, 아래 클래스는 Number의 서브클래스만 받는다.
  • class C <T extends Number> { ... };

Raw 타입 제네릭

  • 타입 매개변수 없이 사용되는 제네릭 타입을 Raw 타입이라 한다.
  • Raw 타입 클래스 내부의 모든 자료형은 java.lang.Object로 간주되어 실행된다.

람다식(lambda expression)

  • 람다식이란 파라미터를 입력받아 결과값을 반환하는 코드이다.
  • 람다식은 메소드와 비슷하나, 이름이 없고, 메소드 내부에 존재할 수 있다.
  • 람다식은 하나의 메소드만을 가지는 인터페이스 타입인 변수에 저장될 수 있다.
    • Java 8에는 이러한 인터페이스들이 java.util.function 패키지에 존재한다.
    • 이는 Java 8부터 도입된 함수적 프로그래밍을 위한 패키지이다.

java.util.ArrayList.forEach()로부터 값을 받아 출력하는 람다식

import java.util.ArrayList;

public class App {
  public static void main(String[] args) {
    List<Integer> intList = new ArrayList<Integer>();
    intList.add(1);
    intList.add(2);
    intList.add(3);
    intList.forEach(x -> System.out.println(x));
    );
  }
}

 

java.util.function.Consumer에 저장되어 사용되는 람다식

* Consumer 인터페이스는 메소드가 한 개인 single-method interface이다.

import java.util.ArrayList;
import java.util.function.Consumer;

public class App {
  public static void main(String[] args) {
    List<Integer> intList = new ArrayList<Integer>();
    intList.add(1);
    intList.add(2);
    intList.add(3);
    Consumer<Integer> lambdaExpression = n -> System.out.println(n);
    numbers.forEach(lambdaExpression);
  }
}

 

메소드 내부에서 사용되는 람다식

* 어떤 메소드 내부에서 람다식을 사용하기 위해서는 single-interface method를 parameter로 받아야 한다.

interface CustomStringFunction {
  String execute(String string);
}

public class App {

  public static void main(String[] args) {
    CustomStringFunction toUpper = x -> x.toUpperCase();
    CustomStringFunction toLower = x -> x.toLowerCase();
    printWithLambda("abcde", toUpper);
    printWithLambda("ABCDE", toLower);
  }
  
  public static void printWithLambda(String s, CustomStringFunction csf) {
    System.out.println(csf.execute(s));
  }
  
}

 

연습문제 요약

  • 제네릭인 ArrayList를 선언과 동시에 String을 저장하도록 초기화하라.
    • List<String> strList = new ArrayList<String>();
  • 제네릭을 사용할 때는 type parameter로 기본 자료형을 사용할 수 없다.
  • Executable exec = new Executable() { public void execute(){} };를 람다식으로 표현하라.
    • Executable exec = () -> {};
  • java.util.function.Consumer는 accept()메소드만을 가진다.
    • 람다식을 대입하여 원하는 계산을 수행하는 함수처럼 사용할 수 있다.

https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html

 

Consumer (Java Platform SE 8 )

andThen default Consumer  andThen(Consumer  after) Returns a composed Consumer that performs, in sequence, this operation followed by the after operation. If performing either operation throws an exception, it is relayed to the caller of the composed op

docs.oracle.com