람다(Lamda)

최형준·2022년 6월 23일
0

람다란 무엇일까?

  • 함수명을 선언하고 사용하는것이 아닌 식별자 없이 실행 가능한 함수
  • 함수의 구현과 호출만으로 프로그램을 만드는 방식
  • 자바 8부터 도입
  • 람다식 구현할때 인터페이스의 메소드는 두개이상 있으면 안된다. 이를 방지하기 위해 @FunctionalInterface 어노테이션 붙이면 컴파일을 통해 방지 가능.
  • 람다는 일단 코드를 간단히 줄여서 사용하는 것
@FunctionalInterface
interface OperatorInterface{
    int oper(int a, int b);
}

람다 캡처링

  • 이 람다 캡쳐링이란 뭘까? 다시한번 JVM을 돌아보게된다. 힙이나 메서드 영역에 저장되는 인스턴스나 클래스 변수 등과 달리, 지역 변수는 스택에 저장된다.
  • 힙과 메서드 영역은 자원을 공유하여 사용된다.

JVM메모리구조

class ,interface ,enum,array(reference type)같은 경우 heap영역에 저장되고 스택에는 해당 주소값이 저장됨

  • 이 과정에서 람다 캡처링 같은 경우 지역변수를 람다가 가져와 사용하는 경우 상수 값으로 고정되는 이유이다. 스택에 쌓이기 때문에 해당 메소드가 실행된 후에는 모든 지역변수의 할당은 해제된다. 그럼에도 불구하고 람다는 더 이상 메모리에 존재하지 않는 지역변수의 값을 아무런 문제 없이 사용할 수있다. 그러한 이유는 원본 지역변수를 복제하여 사용하기 때문이다.

  • 주의점: 위와 같은경우 지역변수는 스태틱 영역이기 때문에 복사해서 사용, 하지만 static으로 선언된 필드나 인스턴스 필드의 경우 람다 내부에 접근제약 존재x 힙 영역 혹은 메서드 영역에 저장되어 있기 때문.

  • 제약 조건이 없다고 좋은것만은 아님

class LambdaCapturingTest {
 private int instanceField = 100; 
private int instanceField2 = 0; // 람다 내부에서 사용되는 인스턴스 필드의 값을 재할당하여도 컴파일 에러가 발생하지 않는다.
 void lambdaCapturing_fieldDoesNotHaveToBeFinal() { Supplier r = () -> this.instanceField;
 System.out.println(r.get()); // 100 this.instanceField = 200; // 컴파일 에러가 발생하지 않는다. 
System.out.println(r.get()); // 200
 }
 // 람다 내부에서 인스턴스 필드의 값을 직접 재할당할 수 있다.
 void lambdaCapturing_canChangeFieldValueFromInsideOfLambda() {
 // 컴파일 에러가 발생하지 않는다.
 Consumer changeFieldValue = (a) -> this.instanceField2 += a; // 1~100의 값을 순차적으로 instanceField2에 더하며 재할당 
IntStream.rangeClosed(1, 100) .forEach(changeFieldValue::accept);
 System.out.println(this.instanceField2); // 5050
 } 
}

병렬 스트림 내부에서 호출할 때(병렬 스트림은 대량의 데이터를 여러 그룹으로 쪼개어 병렬적 처리)

병렬 처리과정에서 데이터가 어떤 값으로 변하는지 정확히 파악할 수없다(디버깅하기 힘듦)

  • 람다의 정의
  • 익명함수를 지칭하는 용어

익명함수란?

  • 함수 리터럴 방식으로 만들어진 이름없는 함수
  • 함수 리터럴이란 함수를 문자 그대로 변수에 담으면 함수 리터럴 방식으로 만드는것
var a=10;

var funA=function(n){
	alert(n);
}
  • 또한 일반 함수와 익명 함수의 차이는 일반 함수는 이름을 가지고 있다 그렇기에 그 이름을 가지고 재 사용이 가능하다. 하지만 익명 함수는 이름이 없다. 그렇기 때문에 일회성이다.

장점

코드의 간결성, 람다를 사용하면 코드를 줄일 수있기 때문에 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현 가능

지연연산 수행 가능

우리는 람다식을 왜 써야할까?

  • 람다식을 쓰기에 앞서 동작 파라미터화를 설정하고자 한다 우리는 클린코드, 간결한 코드를 짜내기 위해 노력한다. 그렇다면 왜 간결하고 깔끔한 코드를 작성해야 할까? 바로 유지 보수성을 위해서 이다. 예를들어 설명하는 것이 가장 좋은것 같다.

만약 강아지의 이미지를 찾아 저장하는 코드를 작성한다고 생각하자.

```java
public List<Dogimg> searchDog(List<Dogimg> images){
	List<Dogimg> result=new ArrayList<>();
	for(Dogimg dog : images){
			if(dog.equals(dog))
				result.add(dog)
	}
	return result;
}
```

이처럼 리스트등과 같은 방법을 통해 강아지 이미지를 저장하려 할것이다.

그렇다면 고양이라면?

``` java

public List<Catimg> searchCat(List<Catimg> images){
	List<Catimg> result=new ArrayList<>();
	for(Catimg cat : images){
			if(cat.equals(cat))
				result.add(cat)
	}
	return result;
}
```

고양이 이미지를 저장하는 메소드를 생성하게 될 것이다.

만약 고양이의 종류 예를들어 샴 고양이, 점박이 고양이 등 다양한 요구사항을 필요로 하게된다면?

우리는 매번 요구사항에 따라 새로운 메소드를 만들어내고 유지보수하기 힘들것이다.

## 이러한 문제를 해결하기 위한 방법 **추상화**

위의 코드 같은 경우 만약 새로운 요구사항을 원한다면 코드를 새로 생성하게 될것이다.

## 이것은 DRY(Do not Repeat Yourself) 법칙을 위배하게 된다!!

소스코드에서 동일한 코드가 반복이 된다는 것은 잠재적인 버그의 위협을 증가시킨다.

반복되는 코드 내용이 변경될 필요가 있는경우, 모든 코드에 찾아가서 수정해야한다.

이 과정에서 실수가 발생한다면 버그가 발생할 것이고 프로젝트 규모가 커질 수록 반복되는 코드로 인해 유지보수 오버헤드도 커진다. 그렇기 때문에 반복되는 코드를 줄이는 습관을 가져야한다.

## 동작 파라미터화 개념

- 아직 어떻게 실행할 것인지 결정하지 않은 코드 블럭을 의미.
- 코드 블럭의 실행은 나중으로 미룸
- 결과적으로 코드 블럭에 따라 메서드의 동작이 파라미터화 한다.
- 메서드가 다양한 동작을 받아서 내부적으로 다양한 동작을 수행할 수있도록 하는 것
- 앞서 했던 행동들을 참, 거짓으로 반환하는 방법이있다(ex.고양이 몸무게가 50이상인가?, 강아지가 말티즈인가?)
- 참,거짓을 반환하는 함수를 Predicate라고한다
- 

```java
public interface CatPredicate {
      boolean test(Cat cat);
  }
```

다음과 같은 방법을 통해 조건에 맞는 결과를 참, 거짓으로 판별할 수 있다.

 

```java
public class CatHeavyWeightPredicate implements CatPredicate {
      @Override
      public boolean test(Cat cat) {
          return cat.weight() > 10;
      }
  }
```

이와 같은 방법을 이용하여 동작을 파라미터화 시켜, 메소드가 다양한 동작을 수행할 수 있게한다.

가독성과 유연성이 향상되고 속성에 관한 모든 변화에 대응이 가능하다.

**하지만 새로운 동작을 원할 때 마다 인터페이스를 구현해서 인스턴스화 해야하나?**

이것을 좀 더 편리하게 해주기 위한 것 바로 람다(익명 클래스) 이다.

밑에 하나의 예시를 통해 람다가 탄생한 마지막 방법을 설명하겟다.

``` java
List<Cat> ShamCats=filter(inventory,new CatPredicate()){
	public boolean test(Cat a){
		return Big.equals(a.getWeight());
	}
}
```

⇒

``` java
List<Cat>result=Catfilter(inventory,(Cat cat)->Big.equals(cat.getWeight));
```

다음 처럼 람다로 한줄로 표현할수있다.

## 람다

- 익명함수를 단순화 시킨것
profile
긍정적으로 하루를 살아가자!!

0개의 댓글