Java 프로그래밍에서 lambda 표현식을 사용하는 기발한 솜씨

13483 단어 Javalambda
Lambda 표현식을 사용하는 이유
먼저 몇 가지 예를 살펴보겠습니다.
첫 번째 예는 하나의 독립된 라인에서 어떤 임무를 수행하는 것이다. 우리는 일반적으로 이렇게 실현한다.

class Worker implements Runnable {
  public void run() {
    for (int i = 0; i < 100; i++)
      doWork();
  }
  ...
}

Worker w = new Worker();
new Thread(w).start();

두 번째 예, 사용자 정의 문자열 비교 방법 (문자열 길이를 통해) 은 일반적으로 다음과 같다.

class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
세 번째 예는 JavaFX에서 button에 callback을 추가합니다.

button.setOnAction(new EventHandler<ActionEvent>() {
  public void handle(ActionEvent event) {
    System.out.println("Thanks for clicking!");
  }
});
이러한 예는 코드 블록을 정의하여 특정한 대상이나 방법에 전달한 다음에 실행된다는 공통점이 있다.람다 시계에서
다식 이전에 자바는 코드 블록을 직접 전달하는 것을 허락하지 않는다. 자바는 대상을 대상으로 하기 때문에 반드시 하나의 대상을 전달해야 한다.
실행된 코드 블록은 대상에 봉인됩니다.
Lambda 표현식 구문
위의 두 번째 예에서 LengthComparator를 Lambda 표현식으로 다음과 같이 표시합니다.

(String first, String second) -> Integer.compare(first.length(), 
  second.length());
-> 앞은 매개변수 목록이고 뒤는 표현식 문체입니다.
표현식 문체가 한 줄에 그치지 않으면 일반 함수와 같이 문체를 {}에 씁니다.

(String first, String second) -> {
  if (first.length() > second.length()) {
    return 1;
  } else if (first.length() == second.length()) {
    return 0;
  } else {
    return -1;
  }
};
만약 매개 변수가 없다면, () 를 가져가야 한다. 예를 들어 위의 첫 번째 예는 다음과 같다.

() -> {
  for (int i = 0; i < 1000; i ++) {
    doWork();
  }
}
매개변수 유형을 컨텍스트에서 자동으로 추정할 수 있는 경우에는 생략할 수 있습니다.

Comparator<String> comp
  = (first, second) // Same as (String first, String second)
  -> Integer.compare(first.length(), second.length());
매개변수가 하나이고 유형을 자동으로 추정할 수 있는 경우 괄호()도 생략할 수 있습니다.

// Instead of (event) -> or (ActionEvent event) ->
eventHandler<ActionEvent> listener = 
  event -> System.out.println("Thanks for clicking!");
lambda 표현식의 반환 값의 유형은 자동으로 추정되기 때문에 설명할 필요가 없습니다.lambda 표현식, 일부 조건 지점 중
값이 반환되고 다른 분기에는 값이 반환되지 않으므로 다음과 같이 허용되지 않습니다.

(x) -> {
  if (x >= 0) {
    return 1;
  }
}
또한,expression lambda와statement lambda의 차이점은 expression lambda가 필요하지 않다는 것이다
return 키워드를 쓰면 Java runtime는 표현식의 결과를 반환값으로 되돌려줍니다. statement lambda는
{}에 쓰인 표현식은 다음과 같은 return 키워드를 사용해야 합니다.

// expression lambda
Comparator<String> comp1 = 
  (first, second) -> Integer.compare(first.length(), second.length());

// statement lambda
Comparator<String> comp2 = (first, second) -> 
  { return Integer.compare(first.length(), second.length());};

Functional Interface
만약 인터페이스 (인터페이스) 가 추상적인 방법 (abstract method) 하나만 있다면
Runnable, Comparator 등의 Functional Interface
Functional Interface 객체가 필요한 모든 곳에서 lambda 표현식을 사용할 수 있습니다.

Arrays.sort(words, 
  (first, second) -> Integer.compare(first.length(), second.length()));
여기서sort()의 두 번째 매개 변수는 Comparator 대상입니다. Comparator는
Functional Interface, 이 대상의 compare () 방법을 호출하기 위해 lambda 표현식에 직접 전송할 수 있습니다
이 lambda 표현식의 문장체를 실행합니다.
만약 lambda 표현식의 문장이 이상을 던지면 대응하는 Functional Interface의 추상적인 방법은 던져야 한다
이 이상이 발생했습니다. 그렇지 않으면 lambda 표현식에서 이상을 현시적으로 포착해야 합니다.

Runnable r = () -> {
  System.out.println("------");
  try {
    Thread.sleep(10);
  } catch (InterruptedException e) {
    // catch exception
  }
};

Callable<String> c = () -> {
  System.out.println("--------");
  Thread.sleep(10);
  return "";
};

Method Reference
만약lambda표현식의 매개 변수를 매개 변수로 전달하면 그들의 실행 효과가 같다면 이lambda표현식
다음과 같은 두 가지 방법으로 Method Reference 표현을 사용할 수 있습니다.

(x) -> System.out.println(x)
System.out::println
그중에서도 시스템.out::println은 Method Reference라고 합니다.
Method Reference는 다음과 같은 세 가지 유형으로 구성됩니다.
  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod
  • 앞의 두 가지 방식에 대응하는 lambda 표현식의 매개 변수와 method의 매개 변수는 일치한다. 예를 들어
    
    System.out::println
    (x) -> System.out.println(x)
    
    Math::pow 
    (x, y) -> Math.pow(x, y)
    
    
    세 번째 방식에 대응하는 lambda 표현식 문장체에서 첫 번째 파라미터를 대상으로 method를 호출하여 다른 파라미터를
    method의 매개 변수로서, 예를 들면:
    
    String::compareToIgnoreCase
    (s1, s2) -> s1.compareToIgnoreCase(s2)
    1.5 Constructor Reference
    
    Constructor Reference는 Method Reference와 유사하며 특수한 method:new에 불과하며 구체적으로 호출되는 구조 함수는 상하문 환경에 의해 결정됩니다. 예를 들어
    
    List<String> labels = ...;
    Stream<Button> stream = labels.stream().map(Button::new);
    
    Button::new는 (x) -> Button(x)과 동일하므로 호출된 구조 함수는: Button(x);
    개별 객체를 만드는 것 외에도 다음 두 가지 방법과 같은 값으로 객체 배열을 만들 수 있습니다.
    
    int[]::new 
    (x) -> new int[x]
    
    
    변수 작용역
    lambd 표현식은 현재 역할 영역에서 사용할 수 있는 변수를 포획합니다. 예를 들어 다음과 같습니다.
    
    public void repeatMessage(String text, int count) {
      Runnable r = () -> {
        for (int i = 0; i < count; i ++) {
          System.out.println(text);
          Thread.yield();
        }
      };
      new Thread(r).start();
    }
    
    그러나 이 변수들은 반드시 변할 수 없어야 한다. 왜?다음 예를 참조하십시오.
    
    int matches = 0;
    for (Path p : files)
      new Thread(() -> { if (p has some property) matches++; }).start(); 
      // Illegal to mutate matches
    
    변수가 lambda 표현식에서 안전하지 않기 때문에 내부 클래스의 요구와 일치하고 내부 클래스에서만 인용할 수 있습니다
    외부에서 정의한final 변수;
    lambda 표현식의 작용역은 코드 블록의 작용역과 같기 때문에lambd 표현식의 매개 변수 이름이나 변수 이름은
    다음과 같은 로컬 변수와 충돌할 수 있습니다.
    
    Path first = Paths.get("/usr/bin");
    Comparator<String> comp = (first, second) -> Integer.compare(first.length(),
       second.length()); // Error: Variable first already defined
    
    만약 lambda 표현식에서this 변수를 인용한다면, 이 lambda 표현식을 만드는 방법의this 변수를 인용합니다. 예를 들어
    
    public class Application() {
      public void doWork() {
        Runnable runner = () -> {
          ...;
          System.out.println(this.toString());
          ...
        };
      }
    }
    
    그래서 여기 있는this.toString () 은 Runnable 대신 Application 객체의 toString () 를 호출합니다.
    대상의
    Default Method
    인터페이스에는 추상적인 방법만 있을 수 있다. 만약에 기존의 인터페이스에 하나의 방법이 추가된다면 이 인터페이스의 모든 실현 클래스는 이 방법을 실현해야 한다.
    Java 8에서 Default Method라는 개념을 도입하여 인터페이스에 default 방법을 추가하여 기존의 연결을 파괴하지 않는다
    인터페이스 규칙, 인터페이스 구현 클래스는 이 default 방법을 다시 쓰거나 직접 계승할 수 있습니다. 예를 들어 다음과 같습니다.
    
    interface Person {
      long getId();
      default String getName() { return "John Q. Public"; }
    }
    
    Java는 다중 계승을 허용합니다. 만약에 하나의 클래스의 부류에 정의된 방법과 인터페이스에 정의된default 방법이 완전히 같다면, 또는
    한 종류의 두 인터페이스에서 완전히 같은 방법을 정의했는데 이런 충돌을 어떻게 처리합니까?처리 규칙은 다음과 같습니다.
    만약 부류와 인터페이스의 방법이 충돌한다면: 부류의 방법을 기준으로 인터페이스의 방법은 무시된다.
    만약 두 인터페이스의 default 방법이 충돌한다면 이 방법을 다시 써서 충돌을 해결해야 한다.
    Static Method
    Java8 이전에 인터페이스에서 static 변수만 정의할 수 있습니다. Java8을 시작하면 인터페이스에 static 방법을 추가할 수 있습니다. 예를 들어
    Comparator 인터페이스에는 다음과 같은 일련의 comparingXXX static 방법이 추가되었습니다.
    
    public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> 
      keyExtractor) {
      Objects.requireNonNull(keyExtractor);
      return (Comparator<T> & Serializable)
       (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), 
           keyExtractor.applyAsInt(c2));
    }
    
    이 static 방법을 사용하면 다음 두 가지 방식도 같은 가격입니다.
    1、
    
    Arrays.sort(cities, (first, second) -> Integer.compare(first.length(), 
      second.length()));
    
    2、
    
    Arrays.sort(cities, Comparator.comparingInt(String::length));
    
    따라서 앞으로 우리는 자신의 인터페이스를 설계할 때 단독 도구 종류(예를 들어 Collections/Collection)를 정의할 필요가 없다.
    인터페이스에서static 방법을 사용하면 됩니다.
    익명 내부 클래스
    자바 세계에서 익명 내부 클래스는 응용 프로그램에서 한 번만 실행할 수 있는 동작을 실현할 수 있다.예를 들어 Android 응용 프로그램에서 단추를 누르면 이벤트 처리가 됩니다.클릭 이벤트를 처리하기 위해 독립된 클래스를 만들 필요가 없습니다. 익명 내부 클래스로 이 작업을 완성할 수 있습니다.
    
    Button button = (Button) findViewById(R.id.button1);
    button.setOnClickListener(new OnClickListener() {
     
      @Override
      public void onClick(View view) {
        Toast.makeText(MainActivity.this, "Button Clicked", Toast.LENGTH_SHORT).show();
      }
     
    });
    
    Lambda 예
    1.Runnable Lambda
    다음은 Runnable의 예입니다.
    
    public void runnableTest() {
        System.out.println("=== RunnableTest ===");
        //   Runnable
        Runnable r1 = new Runnable() {
          @Override
          public void run() {
            System.out.println("Hello world one!");
          }
        };
        // Lambda Runnable
        Runnable r2 = () -> System.out.println("Hello world two!");
        //   run  
        r1.run();
        r2.run();
      }
    
    
      public void runnableTest() {
        System.out.println("=== RunnableTest ===");
        //   Runnable
        Runnable r1 = new Runnable() {
          @Override
          public void run() {
            System.out.println("Hello world one!");
          }
        };
     
        // Lambda Runnable
        Runnable r2 = () -> System.out.println("Hello world two!");
     
        //   run  
        r1.run();
        r2.run();
      }
    
    이 두 가지 실현 방식은 모두 매개 변수도 반환 값도 없다.Runnable lambda 표현식은 코드 블록을 사용하여 5행 코드를 하나의 문장으로 간소화합니다.
    2.Comparator Lambda
    Java에서 Comparator 인터페이스는 컬렉션을 정렬하는 데 사용됩니다.다음 예에서 ArrayList에는 Person 객체가 포함되어 있으며 Person 객체의surName에 따라 정렬됩니다.다음은 Person 클래스에 포함된fields:
    
    public class Person {
      private String givenName;
      private String surName;
      private int age;
      private Gender gender;
      private String eMail;
      private String phone;
      private String address;
    }
    
    
    public class Person {
      private String givenName;
      private String surName;
      private int age;
      private Gender gender;
      private String eMail;
      private String phone;
      private String address;
    }
    
    다음은 Comparator 인터페이스를 각각 익명 내부 클래스와 Lambda 표현식으로 구현하는 방법입니다.
    
    public class ComparatorTest {
      public static void main(String[] args) {
        List<Person> personList = Person.createShortList();
        //  
        Collections.sort(personList, new Comparator<Person>() {
          public int compare(Person p1, Person p2) {
            return p1.getSurName().compareTo(p2.getSurName());
          }
        });
        System.out.println("=== Sorted Asc SurName ===");
        for (Person p : personList) {
          p.printName();
        }
        //   Lambda  
        //  
        System.out.println("=== Sorted Asc SurName ===");
        Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
        for (Person p : personList) {
          p.printName();
        }
        //  
        System.out.println("=== Sorted Desc SurName ===");
        Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
        for (Person p : personList) {
          p.printName();
        }
      }
    }
    
    
    public class ComparatorTest {
      public static void main(String[] args) {
        List<Person> personList = Person.createShortList();
     
        //  
        Collections.sort(personList, new Comparator<Person>() {
          public int compare(Person p1, Person p2) {
            return p1.getSurName().compareTo(p2.getSurName());
          }
        });
     
        System.out.println("=== Sorted Asc SurName ===");
        for (Person p : personList) {
          p.printName();
        }
     
        //   Lambda  
     
        //  
        System.out.println("=== Sorted Asc SurName ===");
        Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
        for (Person p : personList) {
          p.printName();
        }
     
        //  
        System.out.println("=== Sorted Desc SurName ===");
        Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
        for (Person p : personList) {
          p.printName();
        }
      }
    }
    
    익명 내부 클래스는 Lambda 표현식을 통해 이루어질 수 있습니다.첫 번째 Lambda 표현식은 매개변수의 유형을 Person으로 정의합니다.두 번째 Lambda 표현식은 이 유형의 정의를 생략했다.Lambda 표현식은 형식 밀어내기를 지원합니다. 상하문을 통해 필요한 형식을 밀어낼 수 있다면 형식 정의를 생략할 수 있습니다.여기에서 우리는 Lambda 표현식을 일반적인 정의를 사용하는Comparator에 사용하기 때문에, 컴파일러는 이 두 개의 매개 변수 유형을 Person으로 밀어낼 수 있다.

    좋은 웹페이지 즐겨찾기