[java] Java8 Lambda와 익명 클래스

Java8이 최근에 출시되었고 새로운 람다식이 정말 멋져 보이기 때문에 이것이 우리가 익숙했던 익명 클래스의 종말을 의미하는지 궁금합니다.

나는 이것에 대해 조금 연구 해 왔고 Lambda 표현식이 어떻게 분류를 수행하기 위해 Comparator의 익명 인스턴스를 가져 오는 데 사용되는 Collection의 정렬 방법과 같은 이러한 클래스를 체계적으로 대체하는지에 대한 멋진 예제를 찾았습니다.

Collections.sort(personList, new Comparator<Person>(){
  public int compare(Person p1, Person p2){
    return p1.firstName.compareTo(p2.firstName);
  }
});

이제 Lambda를 사용하여 수행 할 수 있습니다.

Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));

그리고 놀랍도록 간결하게 보입니다. 제 질문은 Lambda 대신 Java8에서 이러한 클래스를 계속 사용하는 이유가 있습니까?

편집하다

같은 질문이지만 반대 방향으로, Lambda는 단일 메서드 인터페이스에서만 사용할 수 있기 때문에 익명 클래스 대신 Lambda를 사용하면 어떤 이점이 있습니까?이 새로운 기능은 몇 가지 경우에만 사용되는 바로 가기입니까 아니면 정말 유용합니까?



답변

AIC (익명 내부 클래스)를 사용하여 추상 클래스 또는 구체적인 클래스의 하위 클래스를 만들 수 있습니다. AIC는 상태 (필드) 추가를 포함하여 인터페이스의 구체적인 구현을 제공 할 수도 있습니다. AIC의 인스턴스는 this메서드 본문에서 사용하여 참조 될 수 있으므로 추가 메서드를 호출 할 수 있고 시간이 지남에 따라 상태가 변경 될 수 있습니다. 이들 중 어느 것도 람다에 적용되지 않습니다.

AIC의 대부분의 용도는 단일 함수의 상태 비 저장 구현을 제공하는 것이기 때문에 람다 식으로 대체 할 수 있지만 람다를 사용할 수없는 AIC의 다른 용도가 있습니다. AIC는 여기에 있습니다.

최신 정보

AIC와 람다 식의 또 다른 차이점은 AIC가 새로운 범위를 도입한다는 것입니다. 즉, 이름은 AIC의 수퍼 클래스 및 인터페이스에서 확인되며 어휘로 둘러싸는 환경에서 발생하는 이름을 섀도 잉 할 수 있습니다. 람다의 경우 모든 이름이 어휘로 확인됩니다.


답변

Lambda는 훌륭한 기능이지만 SAM 유형에서만 작동합니다. 즉, 단일 추상 메서드와 만 인터페이스합니다. 인터페이스에 둘 이상의 추상 메서드가 포함되면 즉시 실패합니다. 익명의 클래스가 유용 할 것입니다.

따라서 우리는 익명 클래스를 무시할 수 없습니다. 참고로, and에 sort()대한 유형 선언을 건너 뛰어 메서드를 더 단순화 할 수 있습니다 .p1p2

Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));

여기에서 메서드 참조를 사용할 수도 있습니다. 클래스에 compareByFirstName()메소드 를 추가 Person하고 다음을 사용하십시오.

Collections.sort(personList, Person::compareByFirstName);

또는에 대한 getter를 추가 firstName하고 Comparatorfrom Comparator.comparing()메서드를 직접 가져옵니다 .

Collections.sort(personList, Comparator.comparing(Person::getFirstName));


답변

익명 클래스를 사용한 Lambda 성능

응용 프로그램이 시작되면 각 클래스 파일을로드하고 확인해야합니다.

익명 클래스는 주어진 클래스 또는 인터페이스에 대한 새 하위 유형으로 컴파일러에 의해 처리되므로 각각에 대해 새 클래스 파일이 생성됩니다.

Lambda는 바이트 코드 생성이 다르며 JDK7과 함께 제공되는 invokedynamic 명령어를 사용하여 더 효율적입니다.

Lambda의 경우이 명령어는 런타임까지 바이트 코드의 람다 표현식 번역을 지연하는 데 사용됩니다. (처음으로 지침이 호출 됨)

결과적으로 Lambda 표현식은 정적 메서드가됩니다 (런타임에 생성됨). (stateles 및 statefull 케이스에는 작은 차이가 있으며 생성 된 메소드 인수를 통해 해결됩니다.)


답변

다음과 같은 차이점이 있습니다.

1) 구문

Lambda 표현식은 AIC (Anonymous Inner Class)에 비해 깔끔하게 보입니다.

public static void main(String[] args) {
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("in run");
        }
    };

    Thread t = new Thread(r);
    t.start();
}

//syntax of lambda expression 
public static void main(String[] args) {
    Runnable r = ()->{System.out.println("in run");};
    Thread t = new Thread(r);
    t.start();
}

2) 범위

익명의 내부 클래스는 클래스이며, 이는 내부 클래스 내부에 정의 된 변수의 범위가 있음을 의미합니다.

반면에 람다 식은 자체 범위가 아니라 둘러싸는 범위의 일부입니다.

유사한 규칙이 적용됩니다. 익명의 내부 클래스와 람다 식을 사용할 때 superthis 키워드에 . 익명 내부 클래스의 경우이 키워드는 로컬 범위를 참조하고 super 키워드는 익명 클래스의 슈퍼 클래스를 참조합니다. 람다 표현식의 경우이 키워드는 둘러싸는 유형의 객체를 참조하고 super는 둘러싸는 클래스의 수퍼 클래스를 참조합니다.

//AIC
    public static void main(String[] args) {
        final int cnt = 0;
        Runnable r = new Runnable() {
            @Override
            public void run() {
                int cnt = 5;
                System.out.println("in run" + cnt);
            }
        };

        Thread t = new Thread(r);
        t.start();
    }

//Lambda
    public static void main(String[] args) {
        final int cnt = 0;
        Runnable r = ()->{
            int cnt = 5; //compilation error
            System.out.println("in run"+cnt);};
        Thread t = new Thread(r);
        t.start();
    }

3) 성능

런타임시 익명 내부 클래스는 클래스로드, 메모리 할당 및 개체 초기화 및 비 정적 메서드 호출이 필요하지만 람다 식은 순수 컴파일 시간 활동이며 런타임 동안 추가 비용이 발생하지 않습니다. 따라서 람다 식의 성능은 익명의 내부 클래스에 비해 더 좋습니다. **

** 나는이 점이 전적으로 사실이 아니라는 것을 알고 있습니다. 자세한 내용은 다음 질문을 참조하십시오. Lambda 대 익명 내부 클래스 성능 : ClassLoader에 대한 부하 감소?


답변

Java 8의 Lambda는 함수형 프로그래밍을 위해 도입되었습니다. 상용구 코드를 피할 수있는 곳. 나는 람다에 관한이 흥미로운 기사를 보았습니다.

http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html

간단한 논리에는 람다 함수를 사용하는 것이 좋습니다. 람다를 사용하여 복잡한 논리를 구현하는 경우 문제 발생시 코드를 디버깅 할 때 오버 헤드가 발생합니다.


답변

람다는 단일 추상 메서드를 사용하는 함수에는 좋지만 다른 모든 경우에는 익명 내부 클래스가 구세주이기 때문에 익명 클래스가 남아 있습니다.


답변

  • 람다 구문은 자바가 추론 할 수있는 명백한 코드를 작성할 필요가 없습니다.
  • 를 사용 invoke dynamic하면 람다는 컴파일 시간 동안 익명 클래스로 다시 변환되지 않습니다 (Java는 객체 생성을 거치지 않고 메서드의 서명 만 신경 쓰고 객체를 생성하지 않고 메서드에 바인딩 할 수 있음).
  • 람다는 우리가하고 싶은 일 대신에 우리가 할 수 있기 전에해야 할 일을 더 강조했습니다.