자바 객체지향의 원리와 이해 - #B 자바 8 람다와 인터페이스 스펙 변화

jhkim·2023년 3월 27일
0

Java

목록 보기
3/3

기존의 코어 하나인 CPU가 발전되며 멀티 코어 프로세서들이 등장하면서, 프로그래머에게도 병렬화에 대한 고민이 필요해졌다.

자바 8부터는 병렬화를 위해 컬렉션을 강화했고, 그 컬렉션을 효율적으로 사용하기 위해 Stream을 강화했다. 스트림을 효율적으로 사용하기 위해 또 함수형 프로그래밍이, 그를 위해 람다가, 람다를 위해 인터페이스의 변화가 수반됐다.

병렬화를 위한 컬렉션?

Vector와 HashTable은 동기화된 메소드로 구성되어 멀티 스레드 환경에서 안전하게 요소를 처리할 수 있지만, ArrayList, HashSet HashMap등은 동기화된 메소드로 구성되지 않아서 멀티 스레드 환경에서 안전하지 않다.
따라서 컬렉션 프레임워크는 비동기화된 메소드를 동기화된 메소드로 래핑할 수 있도록 synchronizedXXX()메소드를 제공한다.

ArrayList는 멀티스레드에 안전한 List로,
HashSet은 Set으로,
HashMap은 Map으로 래핑한다.



람다

기존에는 반드시 메서드 내에 코드 블록이 존재해야 했는데, 람다의 도입으로 코드 블록을 위한 메서드나 익명 객체를 만드는 수고를 덜었다. 또한 람다를 메서드의 인자나 반환값으로 사용할 수 있다.
📌코드를 변수처럼 사용할 수 있게 됐다.

public class 클래스 {
	public static void main() {
    
    Mytest mt = new MyTest();
    
    Runnuble r = mt;
    
    r.run();
	}
}

class MyTest implements Runnable {
	public void run() {
    	print(테스트해보자);
    }
}

Java 8 이전 버전을 사용하는 경우, 테스트해보자를 출력하기 위해 불필요하게 MyTest를 정의하고 생성하여 사용해야 한다.

그러나 람다를 지원하는 자바 8부터는 익명 객체조차 없이 바로 코드 블록만 사용하면 된다.

public class 클래스 { 
	public static void main() {
    	Runnuble r = () -> {
        	print(테스트해보자);
        }
    }
}

MyTest가 필요 없이 바로 출력하는것을 볼 수 있다.
new Runnable을 하지 않아도 Runnable타입으로 참조 변수 r을 만들고 있으므로
컴파일러가 알아서 판단한다.

Runnable은 java.lang패키지에서 기본으로 제공되는 인터페이스로, 람다를 사용하고자 하는 클래스에서 Runnable을 implements하면 된다.
Runnable내에 run이라는 추상메소드 하나만 정의되어 있으므로, () -> {} 형태만으로도 run()함수를 실행한다.
로직이 한 줄로 표현되는 경우, 블록 기호 또한 생략 가능하다. return구문이 있다면 return도 함께 생략해야 한다.

함수형 인터페이스

추상 메서드를 하나만 가지는 인터페이스를 함수형 인터페이스라고 한다.
함수형 인터페이스만을 람다식으로 변경할 수 있다.
@FunctionalInterface를 인터페이스 위에 붙이면, 컴파일러가 함수형 인터페이스의 조건에 맞는지 검사한다.

public class 클래스 {
	public static void main() {
		MyFunctionalInterface mfi = a -> a * a;    
    }
}

@FunctionalInterface
public interface MyFunctionalInterface {
	public abstract int multiple(int count);
}

인터페이스에 타입이 정의되어 있으므로, (int a)->a * a 하지 않아도
타입 추정 기능이 있어 int로 추정한다.



메소드 호출 인자로 람다 사용

public static void doIt(myFunctionalInterface mfi) {
int b = mfi.multiple(5);
}
이와 같이 doIt을 구현해두고 main메소드에서는 인터페이스의 구현체 mfi를 생성한 뒤,
doIt(mfi인스턴스)해도 된다.
람다식을 단 한 번만 사용할 경우, mfi같은 인터페이스 참조 변수를 정의할 필요도 없이 바로
doIt(a -> a*a)해도 된다.

메소드 호출 인자로 사용할 수 있듯이 메서드 반환값으로도 람다를 사용할 수 있다.



컬렉션 스트림에서 람다 사용

ages가 나이가 들어있는 Integer 배열이라고 하자.
Arrays.stream(ages)
	.filter(age -> age < 20)
    .sorted()
    .forEach(age -> System.out.println(age));

이와 같이스트림은 컬렉션 스트림을 다른 스트림으로 변환하는 map, sum, average 등 많은 메서드를 제공한다.

메서드 레퍼런스, 생성자 레퍼런스

    .forEach(age -> System.out.println(age));

인자를 아무런 가공 없이 출력하는 이런 코드의 경우에는 메서드 레퍼런스를 사용할 수 있다.

    .forEach(System.out::println);

메서드 레퍼런스의 종류에 대해서는 생략하겠다.


자바 8 인터페이스의 변화

기존의 인터페이스는 정적 상수와 추상 인터페이스 메서드만을 가질 수 있었는데,
몇 가지가 추가되었다.

자바 8부터는 인터페이스가

  • 정적 상수
  • 추상 인터페이스 메서드
  • 구체 인스턴스 메서드 - 디폴트 메서드
  • 정적 메서드

이제 인터페이스도 몸체를 가진 메서드를 가질 수 있게 됐다.

0개의 댓글

관련 채용 정보