Java 함수 프로그래밍(5): 패키지 닫기

문법 작용역과 패키지 사용하기
많은 개발자들이 이런 오해를 가지고 있는데 람다 표현식을 사용하면 코드의 군더더기를 초래하고 코드의 질을 떨어뜨릴 수 있다고 생각한다.반대로 코드가 아무리 복잡해져도 우리는 코드의 간결성을 위해 코드의 질에 대해 어떠한 타협도 하지 않을 것이다. 다음은 우리가 볼 수 있다.
앞의 예에서 우리는 이미 lambda 표현식을 다시 사용할 수 있다.그러나 만약 또 다른 알파벳과 일치한다면 코드의 불필요한 문제는 곧 다시 시작될 것이다.우리는 먼저 이 문제를 한층 더 분석한 후에 다시 어법 작용역과 패키지로 그것을 해결하자.
lambda 표현식이 가져온 군더더기
우리는 친구에서 N이나 B로 시작하는 알파벳을 필터해 냈다.위의 예를 계속 사용하면 우리가 쓴 코드는 다음과 같을 수 있다.

final Predicate<String> startsWithN = name -> name.startsWith("N");
final Predicate<String> startsWithB = name -> name.startsWith("B");
final long countFriendsStartN =
friends.stream()
.filter(startsWithN).count();
final long countFriendsStartB =
friends.stream()
.filter(startsWithB).count();
첫 번째predicate는 이름이 N으로 시작하는지 판단하고, 두 번째는 B로 시작하는지 판단한다.우리는 이 두 가지 실례를 각각 두 번의 filter 방법으로 호출한다.이렇게 하면 보기에는 매우 합리적이지만, 두 predicate는 군더더기가 생겼는데, 그것들은 단지 그 검사의 자모가 다를 뿐이다.어떻게 하면 이런 쓸데없는 것을 피할 수 있는지 봅시다.
문법 작용역을 사용하여 군더더기를 피하다
첫 번째 방안은 알파벳을 함수의 매개 변수로 추출하고 이 함수를 filter 방법에 전달할 수 있다.이것은 좋은 방법이지만, 필터는 어떤 함수도 받아들이는 것이 아니다.이것은 단지 하나의 매개 변수만 받아들이는 함수입니다. 그 매개 변수는 집합 중의 요소입니다. boolean 값을 되돌려줍니다. 이것은 Predicate가 들어오기를 원합니다.
우리는 매개 변수가 전달될 때까지 이 알파벳을 먼저 캐시할 수 있는 곳이 있기를 바란다.다음은 이런 함수를 새로 만듭니다.

public static Predicate<String> checkIfStartsWith(final String letter) {
return name -> name.startsWith(letter);
}
우리는 String 매개 변수를 수신하고Predicate 대상을 되돌려주는 정적 함수 check IfStarts With를 정의했습니다. 이것은 다음에 사용할 수 있도록 filter 방법에 전달할 수 있습니다.앞에서 본 고급 함수처럼 함수를 매개 변수로 하는 것이 아니라, 이 방법은 하나의 함수로 되돌아온다.그러나 그것도 하나의 고급 함수이다. 이것은 우리가 12페이지의 진화에서 언급한 것이지 변혁에서 언급한 것이 아니다.
checkIfStartsWith 방법으로 되돌아오는 Predicate 대상은 다른 lambda 표현식과 약간 다르다.return name -> name.startsWith (letter) 문장에서name이 무엇인지 잘 알고 있습니다. 이것은 lambda 표현식에 전달된 매개 변수입니다.그런데 변수 레터는 도대체 뭐예요?이것은 이 익명 함수의 영역 바깥쪽에 있습니다. 자바는 이 lambda 표현식을 정의하는 영역을 찾았고 이 변수letter를 발견했습니다.이것은 바로 어법 작용역이라고 한다.어법 작용역은 매우 유용한 것이다. 이것은 우리가 다른 상하문에서 사용할 수 있도록 한 용역에 변수를 캐시할 수 있게 한다.이 lambda 표현식은 정의 영역의 변수를 사용하기 때문에, 이런 상황을 폐쇄라고도 부른다.문법 작용역에 대한 접근 제한은 다음 31페이지의 문법 작용역에 어떤 제한이 있는지 볼 수 있습니까?
어법 작용역에 어떤 제한이 있습니까?
lambda 표현식에서, 우리는 정의 영역에 있는final 형식이나 실제로final 형식의 로컬 변수에만 접근할 수 있습니다.
lambda 표현식은 곧 호출될 수도 있고, 호출이 지연되거나 서로 다른 라인에서 호출될 수도 있습니다.경쟁 충돌을 피하기 위해서, 우리가 방문한 정의 영역의 로컬 변수는 초기화되면 수정할 수 없습니다.모든 수정 작업은 컴파일 이상을 초래할 수 있습니다.
final로 표시한 후에 이 문제를 해결했지만 자바는 우리에게 반드시 이렇게 표시해야 한다고 강요하지 않았다.사실 자바는 두 가지를 본다.하나는 이 변수를 정의하는 방법에서 초기화를 끝내고 lambda 표현식을 정의하기 전에 접근해야 한다는 것입니다.둘째, 이러한 변수의 값은 수정할 수 없다. 즉, 그것들은 사실적으로final 유형이다. 비록 이렇게 표시되지 않았지만.
무상태의lambda표현식은 운행시 상량이며, 로컬 변수를 사용한lambda표현은 추가 계산 비용이 있습니다.
filter 방법을 호출할 때 우리는 check IfStarts With 방법으로 되돌아오는 lambda 표현식을 사용할 수 있습니다. 이렇게:

final long countFriendsStartN =
friends.stream() .filter(checkIfStartsWith("N")).count();
final long countFriendsStartB = friends.stream()
.filter(checkIfStartsWith("B")).count();
filter 방법을 호출하기 전에, 우리는 먼저 check IfStarts With () 방법을 호출하여 원하는 알파벳을 포함시켰다.이 호출은 곧 lambda 표현식을 되돌려주고 필터 방법에 전달합니다.
고급 함수 (여기는 check IfStarts With) 를 만들고 어법 작용역을 사용함으로써 코드의 군더더기를 제거하는 데 성공했습니다.우리는 더 이상 반복해서name이 어떤 자모로 시작되었는지 판단할 필요가 없다.
역할 영역 재구성
앞의 예에서 우리는 static 방법을 사용했지만, 우리는 static 방법으로 변수를 캐시하는 것을 원하지 않는다. 이렇게 해서 우리의 코드를 혼란스럽게 한다.가장 좋은 것은 이 함수의 작용역을 그것을 사용하는 곳으로 축소하는 것이다.우리는 Function 인터페이스로 이것을 실현할 수 있다.

final Function<String, Predicate<String>> startsWithLetter = (String letter) -> {
Predicate<String> checkStarts = (String name) -> name.startsWith(letter);
return checkStarts; };
이 lambda 표현식은 원래의static 방법을 대체합니다. 함수에 넣을 수 있습니다. 사용하기 전에 정의하면 됩니다.startWithLetter 변수는 인삼은 String이고 인삼은 Predicate의 Function을 참조합니다.
static 방법을 사용하는 것보다 이 버전은 훨씬 간단하지만, 우리는 그것을 계속 재구성하여 더욱 간결하게 할 수 있다.실제적인 측면에서 볼 때 이 함수는 앞의static 방법과 같다.그것들은 모두 String을 받아서 Predicate로 돌아갑니다.Predicate를 명확하게 설명하지 않기 위해서, 우리는lamdba 표현식으로 전부 교체합니다.

final Function<String, Predicate<String>> startsWithLetter = (String letter) -> (String name) -> name.startsWith(letter);
우리는 그 엉망진창인 것들을 해치웠지만, 자바 컴파일러는 상하문에 따라 유형을 유도할 수 있도록 유형 설명을 삭제할 수 있다.개선된 버전을 살펴보겠습니다.

final Function<String, Predicate<String>> startsWithLetter =
letter -> name -> name.startsWith(letter);
이런 간결한 문법에 적응하려면 시간을 좀 들여야 한다.만약 그것이 너의 눈을 멀게 한다면 먼저 다른 곳을 보아라.우리는 이미 코드의 재구성을 완성하였으며, 지금은 그것으로 원래의 checkIfStartsWith () 방법을 대체할 수 있다. 이와 같다.

final long countFriendsStartN = friends.stream()
.filter(startsWithLetter.apply("N")).count();
final long countFriendsStartB = friends.stream()
.filter(startsWithLetter.apply("B")).count();
이 절에서 우리는 고급 함수를 썼다.우리는 함수를 다른 함수에 전달하면 함수에서 함수를 만드는 방법과 함수를 통해 함수를 되돌리는 방법을 보았다.이 예들은 모두 람다 표현식이 가져온 간결성과 중용성을 보여 준다.
이 절에서 우리는 Function과Predicate의 역할을 충분히 발휘했지만, 그 두 가지가 도대체 어떤 차이가 있는지 살펴보자.Predicate는 T 형식의 매개 변수를 받아들여 boolean 값을 되돌려 그에 대응하는 판단 조건의 진위를 나타냅니다.우리가 조건 판단을 해야 할 때, 우리는Predicateg를 사용하여 완성할 수 있다.필터와 같은 요소를 선별하는 방법은 모두 Predicate를 매개 변수로 받아들인다.Funciton은 함수를 대표합니다. 함수는 T 형식의 변수이고 R 형식의 결과를 되돌려줍니다.이것은 boolean만 되돌릴 수 있는 Predicate보다 더 통용됩니다.입력을 하나의 출력으로 바꾸면 우리는 모두 Function을 사용할 수 있기 때문에 맵이 Function을 매개 변수로 사용하는 것도 이치에 맞는 일이다.
집합에서 요소를 선택하는 것은 매우 간단하다는 것을 알 수 있다.다음은 집합에서 하나의 원소만 고르는 방법을 소개할 것이다.

좋은 웹페이지 즐겨찾기