programing

lamda 표현식은 코드 행을 저장하는 것 외에 다른 용도가 있습니까?

randomtip 2022. 11. 1. 22:14
반응형

lamda 표현식은 코드 행을 저장하는 것 외에 다른 용도가 있습니까?

lamda 표현식은 코드 행을 저장하는 것 외에 다른 용도가 있습니까?

람다로 인해 쉽게 풀리지 않았던 문제를 해결할 수 있는 특별한 기능이 있나요?지금까지 본 일반적인 사용법은 다음과 같습니다.

Comparator<Developer> byName = new Comparator<Developer>() {
  @Override
  public int compare(Developer o1, Developer o2) {
    return o1.getName().compareTo(o2.getName());
  }
};

lamda 식을 사용하여 코드를 단축할 수 있습니다.

Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());

Lambda 표현은 Java에서 일반적으로 해결할 수 있는 문제를 바꾸지 않지만 어셈블리 언어로 프로그래밍하지 않는 것과 같은 이유로 특정 문제를 쉽게 해결할 수 있습니다.프로그래머의 작업에서 중복된 작업을 제거함으로써 작업이 쉬워지고 (수동으로) 생성해야 하는 코드의 양만큼만 다른 작업에서는 손도 대지 않는 작업을 수행할 수 있습니다.

그러나 람다 표현식은 코드 행만 저장하는 것이 아닙니다.Lambda 식을 사용하면 이전에는 익명 내부 클래스를 회피책으로 사용할 수 있었던 함수를 정의할 수 있으므로 이러한 경우 익명 내부 클래스를 대체할 수 있지만 일반적으로는 대체할 수 없습니다.

특히 람다 표현식은 변환되는 함수 인터페이스와 독립적으로 정의되므로 액세스할 수 있는 상속된 멤버가 없으며 기능 인터페이스를 구현하는 유형의 인스턴스에 액세스할 수 없습니다.으로는 '하다'this ★★★★★★★★★★★★★★★★★」super주변 문맥과 동일한 의미를 가지며, 이 답변도 참조하십시오.또한 주변 컨텍스트의 로컬 변수를 섀도우로 표시하는 새 로컬 변수를 생성할 수 없습니다.함수를 정의하는 의도된 작업의 경우, 이는 많은 오류 소스를 제거하지만, 다른 사용 사례의 경우 기능 인터페이스를 구현하더라도 람다 식으로 변환할 수 없는 익명 내부 클래스가 있을 수 있음을 의미합니다.

,, 그는 는 furthernew Type() { … }는 새로운 를 생성하는 것을 보증합니다.new★★★★★★★★★★★★★★★★★」 인스턴스로 항상 합니다.static콘텍스트에 의존합니다.람다 은 '참조하다'에 대한 언급만을 담고 있습니다.this할 때)this 비(non-time-)를 지정합니다.static멤버입니다.또한 의도적으로 지정되지 않은 ID의 인스턴스를 생성하므로 구현은 실행 시 기존 인스턴스를 재사용할지 여부를 결정할 수 있습니다("lamda 식은 실행될 때마다 힙에 개체를 생성합니까?" 참조).

이러한 차이는 예에 적용됩니다. 내부 새로운 되며 외부 에 대한 될 수 , 「」는 「인스턴스」를 참조합니다.또한 외부 인스턴스에 대한 참조를 캡처할 수도 있습니다.(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName())는 일반적인 구현에서 싱글톤으로 평가되는 비표준 람다 표현입니다.', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다', '아니다',.class파일을 저장할 수 있습니다.

의미와 성능의 차이를 고려할 때, 람다 표현은 프로그래머가 미래에 특정 문제를 해결하는 방법을 바꿀 수 있습니다. 물론 새로운 언어 특징을 이용한 함수 프로그래밍의 아이디어를 수용하는 새로운 API 때문입니다.Java 8 람다 식 및 퍼스트 클래스 값을 참조하십시오.


§ JDK 1.1부터 JDK 17까지JDK 18부터 내부 클래스는 외부 인스턴스를 사용하지 않을 경우 외부 인스턴스에 대한 참조를 유지할 수 없습니다.호환성을 위해 내부 클래스를 직렬화할 수 없습니다.이것은, JDK 18 이후의 내부 클래스를 타겟 JDK 18 이상으로 컴파일(재컴파일) 하는 경우에만 적용됩니다.JDK-82717」도 참조해 주세요.

프로그래밍 언어는 기계가 실행할 수 있는 언어가 아닙니다.

프로그래머가 생각할 수 있는 입니다.

언어는 우리의 생각을 기계가 실행할 수 있는 것으로 바꾸기 위한 컴파일러와의 대화입니다.자바에 대한 다른 언어(또는 다른 언어)에서 온 사람들의 주된 불만 중 하나는 자바가 프로그래머에게 어떤 정신적 모델을 강요한다는 것이었다(즉, 모든 것이 클래스이다).

나는 그것이 좋은지 나쁜지에 무게를 두지 않을 것이다: 모든 것은 균형이다.그러나 Java 8 lamda는 프로그래머들이 함수 측면에서 생각할 수 있게 해주는데, 이것은 이전에 Java에서는 할 수 없었던 것이다.

절차 프로그래머가 Java에 왔을 때 수업의 관점에서 생각하는 것을 배우는 것과 같습니다. 절차 프로그래머가 점차적으로 미화된 구조에서 벗어나 수많은 정적 메서드로 '도움' 수업을 받고 합리적인 OOO 설계(me culpa)와 더 유사한 것으로 넘어가는 것을 볼 수 있습니다.

만약 당신이 그것들을 익명의 내적 수업을 표현하는 짧은 방법이라고 생각한다면, 당신은 아마도 위의 절차 프로그래머가 수업이 대단한 발전이라고 생각하지 않았던 것과 같은 방식으로 그들을 매우 인상적이라고 생각하지 않을 것이다.

코드 행의 저장을 새로운 기능으로 간주할 수 있습니다.이를 통해 보다 짧고 명확하게 로직을 쓸 수 있기 때문에 다른 사람이 읽고 이해하는 데 걸리는 시간이 단축됩니다.

메서드 참조이 없는 경우 ada(또는 메서드 참조)Stream파이프라인의 가독성이 떨어졌을 것입니다.

예를 들어, 다음과 같이 생각해 보십시오.Stream파이프라인은 각 람다 식을 익명 클래스 인스턴스(instance)로 바꾼 것처럼 보입니다.

List<String> names =
    people.stream()
          .filter(p -> p.getAge() > 21)
          .map(p -> p.getName())
          .sorted((n1,n2) -> n1.compareToIgnoreCase(n2))
          .collect(Collectors.toList());

다음과 같습니다.

List<String> names =
    people.stream()
          .filter(new Predicate<Person>() {
              @Override
              public boolean test(Person p) {
                  return p.getAge() > 21;
              }
          })
          .map(new Function<Person,String>() {
              @Override
              public String apply(Person p) {
                  return p.getName();
              }
          })
          .sorted(new Comparator<String>() {
              @Override
              public int compare(String n1, String n2) {
                  return n1.compareToIgnoreCase(n2);
              }
          })
          .collect(Collectors.toList());

이것은 lamda 식을 사용한 버전보다 쓰기 어렵고 오류가 발생하기 쉽습니다.더 이해하기 어렵기도 하다.

이것은 비교적 짧은 파이프라인입니다.

람다 표현식 및 메서드 참조 없이 이를 읽을 수 있도록 하려면 여기서 사용되는 다양한 기능 인터페이스 인스턴스를 유지하는 변수를 정의해야 합니다. 그러면 파이프라인의 로직이 분할되어 이해하기 어려워집니다.

내부 반복

Java Collections를 반복할 때 대부분의 개발자는 요소를 얻은 후 처리하는 경향이 있습니다.그 아이템을 꺼내서 사용하거나 재삽입하는 것 등입니다.Java 8 버전 이전 버전에서는 내부 클래스를 구현하여 다음과 같은 작업을 수행할 수 있습니다.

numbers.forEach(new Consumer<Integer>() {
    public void accept(Integer value) {
        System.out.println(value);
    }
});

Java 8을 사용하면 다음과 같은 기능을 통해 보다 상세하게 작업을 수행할 수 있습니다.

numbers.forEach((Integer value) -> System.out.println(value));

혹은 그 이상

numbers.forEach(System.out::println);

인수로서의 행동

다음과 같은 경우를 생각할 수 있습니다.

public int sumAllEven(List<Integer> numbers) {
    int total = 0;

    for (int number : numbers) {
        if (number % 2 == 0) {
            total += number;
        }
    } 
    return total;
}

Java 8 Predicate 인터페이스에서는 다음과 같은 작업을 수행할 수 있습니다.

public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
    int total = 0;

    for (int number : numbers) {
        if (p.test(number)) {
            total += number;
        }
    }
    return total;
}

다음과 같이 부릅니다.

sumAll(numbers, n -> n % 2 == 0);

출처 : DZone - Java에서 람다 표현이 필요한 이유

이너 클래스 대신 람다를 사용하면 다음과 같은 많은 이점이 있습니다.

  • 언어 구문 시멘틱스를 도입하지 않고 코드를 보다 간결하고 표현적으로 만듭니다.당신은 이미 당신의 질문에서 예를 들었습니다.

  • 람다를 사용하면 집합의 맵 축소 변환과 같은 요소의 스트림에 대한 함수 스타일 연산을 프로그래밍할 수 있습니다.java.disc.function java.disc.stream 패키지 매뉴얼을 참조하십시오.

  • 컴파일러가 람다용으로 생성한 물리적 클래스 파일이 없습니다.따라서 제공되는 응용 프로그램의 크기가 줄어듭니다.메모리가 람다에 어떻게 할당됩니까?

  • 컴파일러는 람다가 범위 밖의 변수에 액세스하지 않는 경우 람다 생성을 최적화합니다.즉, 람다 인스턴스는 JVM에 의해 한 번만 생성됩니다.자바 8에서 메서드 참조 캐싱이 좋은 아이디어입니까라는 질문에 대한 @Holger의 답변을 참조하십시오.

  • Lamdas는 기능 인터페이스 외에 멀티 마커 인터페이스를 구현할 수 있지만 익명 내부 클래스는 다음과 같은 인터페이스를 더 이상 구현할 수 없습니다.

    //                 v--- create the lambda locally.
    Consumer<Integer> action = (Consumer<Integer> & Serializable) it -> {/*TODO*/};
    

람다는 익명의 수업을 위한 통사적인 설탕입니다.

람다 이전에, 같은 것을 성취하기 위해 익명의 수업을 사용할 수 있다.모든 람다 식은 익명 클래스로 변환할 수 있습니다.

IntelliJ IDEA 를 사용하고 있는 경우는, 다음과 같이 변환할 수 있습니다.

  • 람다에 커서를 놓습니다.
  • alt/option + Enter 키를 누릅니다.

여기에 이미지 설명 입력

당신의 질문에 대답하자면, 사실 람다는 당신이 Java-8 이전에는 할 수 없었던 어떤 것도 하게 하지 않고 오히려 당신이 더 간결한 코드를 쓸 수 있게 해준다.이 방법의 장점은 코드가 더 명확하고 유연해진다는 것입니다.

아직 언급되지 않은 한 가지는 람다를 사용하면 사용되는 기능을 정의할 수 있다는 것입니다.

따라서 여러 개의 보일러 플레이트와 함께 별도의 장소에 둘 필요가 없는 간단한 선택 기능이 있다면 간결하고 지역적으로 관련이 있는 람다를 작성하기만 하면 됩니다.

네, 많은 장점이 있습니다.

  • 이치노이러한 함수의 실장을 참조로서 패스할 수 있습니다.
    • 내부적으로 클래스를 만들면 .class 파일이 생성되지만, lamda를 사용하면 클래스가 아닌 함수 구현을 통과하기 때문에 컴파일러에 의해 클래스 생성이 회피됩니다.
  • 코드 재사용성이 이전보다 높아졌습니다.
  • 그리고 당신이 말한 것처럼 코드는 일반적인 구현보다 짧습니다.

함수 구성 및 고차 함수.

람다 함수는 "고차 함수"를 구축하거나 "함수 구성"을 수행하기 위한 구성 블록으로 사용할 수 있습니다.람다 함수는 이런 의미에서 재사용 가능한 구성 요소로 볼 수 있습니다.

람다를 통한 고차 함수 예제:

Function<IntUnaryOperator, IntUnaryOperator> twice = f -> f.andThen(f);
IntUnaryOperator plusThree = i -> i + 3;
var g = twice.apply(plusThree);
System.out.println(g.applyAsInt(7))

기능 구성 예시

Predicate<String> startsWithA = (text) -> text.startsWith("A");
Predicate<String> endsWithX   = (text) -> text.endsWith("x");

Predicate<String> startsWithAAndEndsWithX =
        (text) -> startsWithA.test(text) && endsWithX.test(text);

String  input  = "A hardworking person must relax";
boolean result = startsWithAAndEndsWithX.test(input);
System.out.println(result);

아직 언급되지 않은 장점 중 하나는 제가 가장 좋아하는 것입니다. 람다는 이행을 연기하는 것을 매우 쉽게 만들 수 있습니다.

예를 들어 Log4j2는 이를 사용합니다.이 경우 조건부로 로그를 기록하는 값(계산 비용이 많이 들었을 수 있는 값)을 전달하는 대신 람다를 전달하여 비싼 값을 계산할 수 있습니다.기존에는 이 값이 사용 여부에 관계없이 매번 계산되었지만, 현재는 lamda를 사용하면 로그 레벨이 해당 문을 기록하지 않기로 결정하면 lamda가 호출되지 않으며, 고가의 계산은 이루어지지 않습니다.퍼포먼스가 향상됩니다.

람다 없이 할 수 있을까요?네, 각 로그 문을 if() 체크로 둘러싸거나 자세한 익명 클래스 구문을 사용하지만 코드 노이즈가 심합니다.

비슷한 예가 많다.Lambda는 케이크를 가지고 먹는 것과 같습니다.여러 줄에 최적화된 코드 효율은 모두 한 줄의 비주얼적인 우아함으로 압축됩니다.

편집: 코멘터의 요청에 따라 다음 예를 제시하겠습니다.

이전 방식에서는 log 문이 실제로 사용할지 여부에 관계없이 항상 host Calculation()이 호출됩니다.

logger.trace("expensive value was {}", expensiveCalculation());

트레이스 로그레벨을 유효하게 하지 않는 한 고가의 계산() 콜이 발생하지 않는 새로운 람다 효율화 방법:

logger.trace("expensive value was {}", () -> expensiveCalculation());

언급URL : https://stackoverflow.com/questions/47407180/do-lambda-expressions-have-any-use-other-than-saving-lines-of-code

반응형