Java8 새 특성 Lambda 표현식 인스턴스 상세 정보

Java8 새 특성 Lambda 표현식 인스턴스 상세 정보
Lambda 표현식을 설명하기 전에 먼저 하나의 방법만 있는 Interface (일반적으로 리셋 인터페이스라고 함) 를 살펴보겠습니다.

public interface OnClickListener {
  void onClick(View v);
}
우리는 그것을 이렇게 사용한다.

button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    v.setText("lalala");
  }
});
이런 리셋 모델은 각종 프레임워크에서 매우 유행하지만 위와 같은 익명 내부 클래스는 좋은 선택이 아니다. 왜냐하면
  • 문법이 불필요하다
  • 익명 내부류 중의this지침과 변수는 오해하기 쉽다
  • 비final 국부 변수를 포획할 수 없음;
  • 비정적 내부 클래스는 기본적으로 외부 클래스의 인용을 가지고 있으며, 일부 상황에서 외부 클래스가 GC에서 회수되지 못해 메모리 유출을 초래할 수 있다.
  • 기쁘게도 Java8은 Lambda를 가져왔습니다. Lambda를 이용하여 위의 기능을 어떻게 실현하는지 살펴보겠습니다.
    
    button.setOnClickListener(v -> v.setText("lalala"));
    어때?!다섯 줄 코드는 한 줄로 하면 돼!!!
    여기에 개념 함수식 인터페이스를 보충한다.앞에서 언급한 OnClickListener 인터페이스는 단지 하나의 방법일 뿐이다. 자바에서 대부분의 리셋 인터페이스는 이런 특징을 가지고 있다. 예를 들어 Runnable와Comparator;우리는 이 방법 하나만 가지고 있는 인터페이스를 함수식 인터페이스라고 부른다.
    1. Lambda 표현식
    익명 내부 클래스의 가장 큰 문제는 군더더기 문법이다. 예를 들어 앞의 On Click Listener에서 다섯 줄 코드가 한 줄만 작업을 수행하고 있다는 것이다.Lambda 표현식은 익명 방법이다. 앞에서 우리는 그것이 매우 가벼운 문법으로 이 문제를 해결한 것을 보았다.
    다음은 Lambda 표현식의 몇 가지 예를 보여 줍니다.
    
    (int x, int y) -> x + y           // x y 
    () -> 66                   // 66
    (String name) -> {System.out.println(name);} // 
    (View view) -> {view.setText("lalala");}   // View setText 
    
    Lambda 표현식 구문은 매개변수 목록, -> 및 함수체로 구성됩니다.함수체는 표현식일 수도 있고 코드 블록일 수도 있다.
    표현식: 표현식이 실행되고 결과를 되돌려줍니다.그것은 리턴 키워드를 간소화했다.
    코드 블록: 말 그대로 코드 덩어리입니다. 일반적인 방법의 문장과 같습니다.
    2. 목표 유형
    앞의 예를 통해 알 수 있듯이 lambda 표현식은 이름이 없습니다. 그러면 우리는 그 유형을 어떻게 알 수 있습니까?답은 상하문을 통해 유도된 것이다.예를 들어, 다음 표현식의 유형은 OnClickListener입니다.
    
    OnClickListener listener = (View v) -> {v.setText("lalala");};
    이것은 같은lambda표현식이 서로 다른 상하문에서 서로 다른 유형을 가지고 있음을 의미한다
    
    Runnable runnable = () -> doSomething(); // Runnable 
    Callback callback = () -> doSomething(); // Callback 
    
    컴파일러는 lambda 표현식이 있는 상하문에서 기대하는 유형을 이용하여 표현식의 유형을 유도하는데 이 기대되는 유형은 목표 유형이라고 부른다.lambda 표현식은 목표 형식이 함수식 인터페이스인 상하문에만 나타날 수 있습니다.
    Lambda 표현식의 유형과 목표 유형의 방법은 서명이 일치해야 합니다. 컴파일러는 이를 검사합니다. 하나의 lambda 표현식이 목표 유형 T에 값을 부여하려면 아래의 모든 조건을 만족시켜야 합니다.
  • T는 함수식 인터페이스이다
  • lambda 표현식의 매개 변수는 반드시 T의 방법 매개 변수와 수량, 유형과 순서에 일치해야 한다(일일이 대응)
  • lambda 표현식의 반환 값은 T 방법의 반환 값과 일치하거나 그 하위 클래스여야 한다
  • lambda 표현식이 던진 이상과 T 방법의 이상이 일치하거나 그 하위 클래스입니다
  • 목표 형식은lambda 표현식의 매개 변수 형식을 알고 있기 때문에, 우리는 이미 알고 있는 형식을 다시 한 번 반복할 필요가 없다.즉, lambda 표현식의 매개 변수 형식은 목표 형식에서 얻을 수 있다.
    
    // s1 s2 String 
    Comparator<String> c = (s1, s2) -> s1.compareTo(s2);
    // 
    button.setOnClickListener(v -> v.setText("lalala"));
    
    ps: Java7의 범용 방법과 <> 구조기도 목표 유형을 통해 유형을 유도합니다. 예를 들어 다음과 같습니다.
    
    List<Integer> intList = Collections.emptyList>();
    List<String> strList = new ArrayList<>();
    
    3. 역할 영역
    내부 클래스에서 변수 이름과this를 사용하면 오류가 발생하기 쉽습니다.내부 클래스는 계승된 구성원 변수 (object 포함) 를 통해 외부 클래스의 구성원 변수를 덮어쓸 수 있으며, 제한되지 않은this 인용은 외부 클래스가 아닌 내부 클래스 자신을 가리킨다.
    한편, 람다 표현식의 의미는 매우 간단하다. 이것은 부류에서 어떠한 변수도 계승하지 않고 새로운 작용역을 도입하지 않는다.lambda 표현식의 매개 변수와 함수체 안의 변수는 외부 환경의 변수와 같은 의미를 가진다.
    다음은 우리 밤을 들자!
    
    public class HelloLambda {
    
      Runnable r1 = () -> System.out.println(this);
      Runnable r2 = () -> System.out.println(toString());
    
      @Override
      public String toString() {
        return "Hello, lambda!";
      }
    
      public static void main(String[] args) {
        new HelloLambda().r1.run(); 
        new HelloLambda().r2.run();
      }
    }
    
    
    위의 코드는 결국 두 개의 Hello, lambda!를 출력합니다.이와 유사한 내부 클래스는 HelloLambda$와 유사하게 인쇄됩니다.1@32a890및 HelloLambda$1@6b32098이런 의외의 문자열.
    총괄: 어법 작용역의 이념을 바탕으로 lambda 표현식은 그 상하문의 국부 변수를 덮을 수 없다.
    4. 변수 캡처
    Java7에서 컴파일러는 내부 클래스에서 인용하는 외부 변수 (즉 포획된 변수) 에 대한 요구가 매우 엄격합니다. 포획된 변수가final로 선언되지 않으면final로 컴파일 오류가 발생합니다.그러나 Java8에서 이 제한을 완화한 C는 lambda 표현식과 내부 클래스에 대해 유효한 읽기 전용에 부합되는 국부 변수를 포획할 수 있습니다. 만약에 국부 변수가 초기화된 후에 수정된 적이 없다면 유효한 읽기 전용입니다.
    
    Runnable getRunnable(String name){
      String hello = "hello";
      return () -> System.out.println(hello+","+name);
    }
    
    this의 인용과this를 통해 한정되지 않은 필드에 대한 인용과 한정되지 않은 방법의 호출은 본질적으로final 국부 변수를 사용하는 데 속한다.이런 인용을 포함하는lambda 표현식은this 실례를 포획한 것과 같다.다른 경우,lambda 대상은this에 대한 응용을 보류하지 않습니다.
    이 특성은 메모리 관리에 매우 좋다. 자바에서 비정적 내부 클래스가 외부 클래스의 실례를 기본적으로 가지고 있다는 것을 알아야 한다. 이것은 메모리 유출을 초래할 수 있다.lambda 표현식에서 외부 클래스 구성원을 포획하지 않으면 외부 클래스 실례에 대한 인용을 보류하지 않습니다.
    그러나 자바8은 포획 변수에 대한 문법적 제한을 완화했지만 포획 변수를 수정하려는 행위는 금지되었다. 예를 들어 다음과 같은 예는 불법이다.
    
    int sum = 0;
    list.forEach(i -> {sum += i;});
    
    왜 이런 행위를 금지해야 합니까?왜냐하면 이런 람다 표현식은 레이스 상태를 일으키기 쉬워요.
    lambda 표현식이 포획 변수를 수정하는 것을 지원하지 않는 또 다른 이유는 우리가 같은 효과를 실현하기 위해 더 좋은 방법을 사용할 수 있기 때문이다. 규정 (condition) 을 사용하는 것이다.java.util.stream 패키지는 Java8의 Stream API에 대해 다음 장에 설명합니다.
    5. 방법 인용
    lambda 표현식은 익명 방법을 정의하고 함수식 인터페이스로 사용할 수 있도록 합니다.Java8은 기존 방법에서 동일한 특성을 구현할 수 있습니다.
    방법 인용과lambda표현식은 같은 특성을 가지고 있다. (그들은 모두 목표 유형이 필요하고 함수식 인터페이스로 전환되는 실례가 필요하다.) 그러나 우리는 방법 인용에 방법체를 제공할 필요가 없다. 우리는 방법명을 통해 기존의 방법을 직접 인용할 수 있다.
    다음 코드를 예로 들면, userName에 따라 정렬해야 한다고 가정합니다
    
    class User{
    
      private String userName;
    
      public String getUserName() {
        return userName;
      }
      ...
    }
    
    List<User> users = new ArrayList<>();
    Comparator<User> comparator = Comparator.comparing(u -> u.getUserName());
    Collections.sort(users, comparator);
    
    
    우리는 방법으로 위의 lambda 표현식을 인용하여 바꿀 수 있다
    
    Comparator<User> comparator = Comparator.comparing(User::getUserName);
    여기 User::get UserName은 lambda 표현식의 약자로 간주됩니다.비록 방법 인용이 반드시 코드를 더욱 치밀하게 만드는 것은 아니지만, 그것은 더욱 명확한 의미를 가지고 있다. 만약 우리가 호출하고자 하는 방법이 하나의 이름을 가지고 있다면, 우리는 방법명을 통해 그것을 호출할 수 있다.
    메소드 참조에는 다음과 같은 여러 가지 구문이 있습니다.
  • 정적 방법 참조: ClassName:::methodName
  • 인스턴스의 인스턴스 방법 참조: instanceReference::methodName
  • 클래스 초과의 실례 방법 참조:super:::methodName
  • 형식의 인스턴스 방법 참조:ClassName:::methodName
  • 구조 방법 인용: Class:::new
  • 수조 구조 방법 참조: TypeName[]:new
  • 읽어주셔서 감사합니다. 여러분에게 도움이 되었으면 좋겠습니다. 본 사이트에 대한 지지에 감사드립니다!

    좋은 웹페이지 즐겨찾기