ممکن است متوجه وجود Default Methodها در Functional Interfaceهای پکیج java.util.function شده باشید. این متدها برای امکان ترکیب و زنجیرهای کردن Lambda Expressionها اضافه شدهاند.
چرا چنین کاری انجام میدهید؟ فقط برای کمک به نوشتن کد سادهتر و خواناتر.
---فرض کنید نیاز به پردازش لیستی از رشتهها دارید و فقط رشتههای non-null، non-empty و کوتاهتر از ۵ کاراکتر را نگه دارید. سه آزمایش روی رشتهی دادهشده دارید:
هر یک از این آزمایشها بهراحتی با Predicate سادهی یکخطی نوشته میشود. همچنین میتوان این سه آزمایش را در یک Predicate واحد ترکیب کرد:
Predicate<String> p = s -> (s != null) && !s.isEmpty() && s.length() < 5;
اما JDK به شما اجازه میدهد این کد را به این شکل بنویسید:
Predicate<String> nonNull = s -> s != null;
Predicate<String> nonEmpty = s -> !s.isEmpty();
Predicate<String> shorterThan5 = s -> s.length() < 5;
Predicate<String> p = nonNull.and(nonEmpty).and(shorterThan5);
مخفی کردن پیچیدگی فنی و آشکار کردن قصد کد، همان چیزی است که ترکیب Lambda Expressionها دربارهی آن است.
در سطح API این کد چگونه پیادهسازی شده؟ بدون ورود عمیق در جزئیات، آنچه میبینید:
and() یک متد استPredicate<T> فراخوانی میشود: پس متد instance استPredicate<T> دیگری به عنوان آرگومان میگیردPredicate<T> برمیگرداندچون فقط یک متد abstract در Functional Interface مجاز است، این متد and() باید Default Method باشد. Interface Predicate<T> متد and() دارد و نیازی نیست خودتان آن را بنویسید.
به علاوه، متد or() و negate() هم وجود دارد.
با استفاده از اینها، مثال قبلی را میتوانید به این شکل بنویسید:
Predicate<String> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNullOrEmpty = isNull.or(isEmpty);
Predicate<String> isNotNullNorEmpty = isNullOrEmpty.negate();
Predicate<String> shorterThan5 = s -> s.length() < 5;
Predicate<String> p = isNotNullNorEmpty.and(shorterThan5);
---
بیانپذیری را میتوان با استفاده از Factory Methodهای تعریفشده در Functional Interfaceها یک قدم جلوتر برد. دو تا از آنها در Interface Predicate<T> وجود دارد.
در مثال زیر، Predicate isEqualToDuke رشتهی کاراکتر را آزمایش میکند. آزمایش وقتی true است که رشته برابر «Duke» باشد:
Predicate<String> isEqualToDuke = Predicate.isEqual("Duke");
دومین Factory Method Predicate دادهشده را Negate میکند:
Predicate<Collection<String>> isEmpty = Collection::isEmpty;
Predicate<Collection<String>> isNotEmpty = Predicate.not(isEmpty);
---
Interface Consumer<T> هم متدی برای زنجیرهای کردن Consumerها دارد:
Logger logger = Logger.getLogger("MyApplicationLogger");
Consumer<String> log = message -> logger.info(message);
Consumer<String> print = message -> IO.println(message);
Consumer<String> logAndPrint = log.andThen(print);
در این مثال، logAndPrint Consumerای است که ابتدا پیام را به Consumer log و سپس به Consumer print ارسال میکند.
تفاوت بین زنجیرهای کردن و ترکیب کمی ظریف است. نتیجهی هر دو عملیات در واقع یکسان است. تفاوت در نحوهی نوشتن آنهاست.
فرض کنید دو Function f1 و f2 دارید. میتوانید آنها را با فراخوانی f1.andThen(f2) زنجیرهای کنید. اعمال Function حاصل، شی را ابتدا به f1 و نتیجه را به f2 ارسال میکند.
Interface Function متد Default دومی هم دارد: f2.compose(f1). به این شکل نوشته شده، Function حاصل ابتدا شی را از طریق Function f1 پردازش و نتیجه را به f2 ارسال میکند.
آنچه باید درک کنید این است که برای به دست آوردن Function حاصل یکسان، باید andThen() را روی f1 یا compose() را روی f2 فراخوانی کنید.
میتوانید Functionهایی از نوعهای مختلف را زنجیرهای یا ترکیب کنید. محدودیتهای واضحی وجود دارد: نوع نتیجهی تولیدشده توسط f1 باید با نوع مصرفشده توسط f2 سازگار باشد.
Interface Function<T, R> همچنین Factory Methodای برای ایجاد Identity Function دارد، به نام identity().
ایجاد Identity Function با الگوی سادهی زیر:
Function<String, String> id = Function.identity();
این الگو برای هر نوع معتبری قابل اعمال است.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/