لم يقدم Java 11 أي ميزات مبتكرة ، ولكنه يحتوي على العديد من الأحجار الكريمة التي ربما لم تسمع بها بعد. هل نظرت بالفعل إلى الأحدث في String و " Optional و " Collection وغيرها من المناهج؟ إذا لم يكن الأمر كذلك ، فأنت وصلت إلى العنوان: اليوم سننظر إلى 11 جواهر مخفية من Java 11!
اكتب الاستدلال لمعلمات لامدا
عند كتابة تعبير lambda ، يمكنك الاختيار بين تحديد الأنواع بوضوح وتخطيها:
 Function<String, String> append = string -> string + " "; Function<String, String> append = (String s) -> s + " "; 
قدم Java 10 var ، لكن لا يمكن استخدامه في lambdas:
 
في Java 11 ، من الممكن بالفعل. لكن لماذا؟ لا يبدو أن var أعطى أكثر من مجرد تمريرة كتابة. على الرغم من أن هذا هو الحال ، فإن استخدام var له ميزتان صغيرتان:
- يجعل استخدام varأكثر عالمية عن طريق إزالة الاستثناء للقاعدة
- يسمح لك بإضافة التعليقات التوضيحية إلى نوع المعلمة دون اللجوء إلى استخدام اسمه الكامل
فيما يلي مثال للحالة الثانية:
 List<EnterpriseGradeType<With, Generics>> types = ; types .stream()  
على الرغم من أن الخلط بين الأنواع المشتقة والصريحة والضمنية في تعبيرات lambda من النموذج (var type, String option, index) -> ... يمكن تنفيذه ، ولكن ( في إطار JEP-323 ) لم يتم تنفيذ هذا العمل. لذلك ، من الضروري اختيار واحد من الطرق الثلاثة والالتزام بها لجميع معايير التعبير lambda. قد تكون الحاجة إلى تحديد var لجميع المعلمات من أجل إضافة تعليق توضيحي لأحدها مزعجة بعض الشيء ، لكن يمكن تحملها عمومًا.
دفق معالجة السلاسل مع 'String::lines'
حصلت على سلسلة متعددة الخطوط؟ تريد أن تفعل شيئا مع كل سطر منه؟ ثم String::lines هو الخيار الصحيح:
 var multiline = "\r\n\r\n\r\n"; multiline .lines()  
لاحظ أن السطر الأصلي يستخدم محددات المسمار \r\n وعلى الرغم من أنني على نظام Linux ، إلا أن lines() لا تزال تعمل على كسرها. ويعزى ذلك إلى حقيقة أنه على الرغم من نظام التشغيل الحالي ، تفسر هذه الطريقة \r و \n و \r\n كفواصل أسطر - حتى إذا كانت مختلطة على نفس السطر.
لا يحتوي دفق الخطوط مطلقًا على فواصل الأسطر نفسها. يمكن أن تكون الخطوط فارغة ( "\n\n \n\n" ، التي تحتوي على 5 خطوط) ، ولكن يتم تجاهل السطر الأخير من السطر الأصلي إذا كان فارغًا ( "\n\n" ؛ سطرين). (ملاحظة من المترجم: من المريح بالنسبة لهم أن يكون لديهم line ، ولكن لديهم string ، ولدينا كل منهما.)
على عكس split("\R") ، تعتبر lines() كسولة ، وأقتبس ، "توفر أداء أفضل [...] من خلال البحث بشكل أسرع عن فواصل الأسطر الجديدة". (إذا أراد شخص ما تقديم ملف قياسي على JMH للتحقق منه ، فأعلمني بذلك). كما أنه يعكس بشكل أفضل خوارزمية المعالجة ويستخدم بنية بيانات أكثر ملاءمة (دفق بدلاً من الصفيف).
إزالة مسافة بيضاء باستخدام 'String::strip' ، إلخ.
في البداية ، كان لدى String طريقة تقليدية لإزالة المسافة البيضاء ، والتي كانت تعتبر كل شيء برموز حتى U+0020 . نعم ، BACKSPACE ( U+0008) هي مسافة بيضاء مثل BELL ( U+0007 ) ، ولكن لم يعد LINE SEPARATOR ( U+2028 ) على هذا النحو.
أدخلت Java 11 طريقة strip ، الذي يحتوي منهجها على مزيد من الفروق الدقيقة. يستخدم Character::isWhitespace من Java 5 لتحديد ما يجب إزالته بالضبط. من وثائقها ، من الواضح أن هذا:
- SPACE SEPARATOR،- SPACE SEPARATOR- LINE SEPARATOR،- PARAGRAPH SEPARATOR، ولكن ليس مساحة لا تنفصم
- HORIZONTAL TABULATION(- U+0009) ،- LINE FEED- VERTICAL TABULATION(- U+000B) ،- VERTICAL TABULATION(- U+000B) ،- FORM FEED(- U+000C) ،- CARRIAGE RETURN(- U+000D)
- FILE SEPARATOR(- U+001C) ،- GROUP SEPARATOR(- U+001D) ،- RECORD SEPARATOR(- U+001E) ،- UNIT SEPARATOR- U+001E(- U+001F)
مع نفس المنطق ، هناك طريقتان للتنظيف ، stripLeading وقطاع stripTailing ، stripTailing بالضبط بما هو متوقع منها.
وأخيرًا ، إذا كنت بحاجة فقط إلى معرفة ما إذا كان الخط فارغًا بعد إزالة المسافة البيضاء ، فليست هناك حاجة لحذفها فعليًا - ما isBlank سوى استخدام isBlank :
 " ".isBlank();  
تكرار السلاسل مع 'String::repeat'
قبض على الفكرة:
الخطوة 1: مراقبة الساعة على JDK

الخطوة 2: البحث عن الأسئلة المتعلقة بـ StackOverflow

الخطوة 3: الوصول إلى إجابة جديدة بناءً على التغييرات المستقبلية

الخطوة 4: ؟؟؟
الخطوة 4: الربح

كما يمكنك أن تتخيل ، لدى String طريقة repeat(int) . إنه يعمل تمامًا بما يتماشى مع التوقعات ، ولا يوجد الكثير للمناقشة.
إنشاء مسارات باستخدام 'Path::of'
تعجبني واجهة برمجة تطبيقات Path ، ولكن تحويل المسارات بين طرق العرض المختلفة (مثل Path File URL و URI و String ) لا يزال مزعجًا. أصبحت هذه النقطة أقل إرباكًا في Java 11 عن طريق نسخ Paths::get الطرق في Path::of الطرق:
 Path tmp = Path.of("/home/nipa", "tmp"); Path codefx = Path.of(URI.create("http://codefx.org")); 
يمكن اعتبارها متعارف عليها ، لأن كلا Paths::get القديمين Paths::get طرق تستخدم خيارات جديدة.
قراءة وكتابة الملفات باستخدام 'Files::readString' و 'Files::writeString'
إذا كنت بحاجة إلى القراءة من ملف كبير ، فإنني عادةً ما أستخدم Files::lines للحصول على دفق كسول من خطوطه. وبالمثل ، لكتابة قدر كبير من البيانات التي قد لا يتم تخزينها في الذاكرة بأكملها ، يمكنني استخدام Files::write Iterable<String> كـ Iterable<String> .
ولكن ماذا عن الحالة البسيطة عندما أرغب في معالجة محتويات الملف كسطر واحد؟ هذا ليس ملائمًا للغاية ، لأن Files::readAllBytes والمتغيرات المناسبة من Files::write تعمل على صفائف البايت.
ثم يظهر Java 11 ، مضيفًا readString و writeString إلى Files :
 String haiku = Files.readString(Path.of("haiku.txt")); String modified = modify(haiku); Files.writeString(Path.of("haiku-mod.txt"), modified); 
واضح وسهل الاستخدام. إذا لزم الأمر ، يمكنك تمرير readString إلى readString ، وفي writeString أيضًا صفيف OpenOptions .
إفراغ الإدخال / الإخراج باستخدام 'Reader::nullReader' ، إلخ.
هل تحتاج إلى OutputStream لا يكتب في أي مكان؟ أو InputStream فارغة؟ ماذا عن Reader Writer الذي لا يفعل شيئًا؟ جافا 11 لديه كل شيء:
 InputStream input = InputStream.nullInputStream(); OutputStream output = OutputStream.nullOutputStream(); Reader reader = Reader.nullReader(); Writer writer = Writer.nullWriter(); 
(ملاحظة المترجم: في commons-io هذه الفئات منذ عام 2014 تقريبًا)
ومع ذلك ، أنا مندهش - هل هو حقًا أفضل بادئة؟ لا أحب كيف يتم استخدامه ليعني "الغياب المتعمد" ... ربما سيكون من الأفضل استخدام noOp ؟ (ملاحظة المترجم: على الأرجح تم اختيار هذه البادئة بسبب الاستخدام الشائع لـ /dev/null .)
{ } ~> [ ] مع 'Collection::toArray'
كيف يمكنك تحويل المجموعات إلى صفائف؟
 
الخيار الأول ، objects ، يفقد جميع المعلومات حول الأنواع ، لذلك فهو في حالة طيران. ماذا عن البقية؟ كلاهما ضخم ، لكن الأول أقصر. ينشئ الأخير مجموعة من الحجم المطلوب ، بحيث يبدو أكثر إنتاجية (أي ، "يبدو أكثر إنتاجية" ، انظر المصداقية ). ولكن هل هو حقا أكثر إنتاجية؟ لا ، على العكس ، إنه أبطأ (في الوقت الحالي).
ولكن لماذا يجب أن أهتم بهذا؟ ليس هناك طريقة أفضل للقيام بذلك؟ في Java 11 هناك:
 String[] strings_fun = list.toArray(String[]::new); 
Collection::toArray متغير جديد من Collection::toArray ، يقبل IntFunction<T[]> ، أي دالة تستقبل حجم الصفيف وتقوم بإرجاع صفيف بالحجم المطلوب. يمكن التعبير عنها لفترة وجيزة كمرجع إلى مُنشئ النموذج T[]::new (من أجل T معروف جيدًا).
حقيقة مثيرة للاهتمام ، التنفيذ الافتراضي لـ Collection#toArray(IntFunction<T[]>) يمرر دائمًا 0 إلى منشئ الصفيف. في البداية ، قررت أن هذا الحل يعتمد على أفضل أداء للصفائف ذات الطول الصفري ، لكن الآن أعتقد أن السبب قد يكون بالنسبة لبعض المجموعات ، فإن حساب الحجم يمكن أن يكون عملية مكلفة للغاية ويجب عدم استخدام هذا النهج في التنفيذ الافتراضي Collection . ومع ذلك ، يمكن أن تقوم تطبيقات المجموعة المحددة ، مثل ArrayList ، بتغيير هذا الأسلوب ، لكنها لا تتغير في Java 11. لا يستحق كل هذا العناء ، أعتقد.
تحقق من عدم وجود 'Optional::isEmpty'
مع الاستخدام الوفير لـ Optional ، خاصة في المشروعات الكبيرة ، حيث تواجه غالبًا نهجًا غير Optional ، يجب عليك في كثير من الأحيان التحقق مما إذا كانت له قيمة. هناك طريقة Optional::isPresent لهذا الغرض. ولكن في كثير من الأحيان تحتاج إلى معرفة العكس - أن Optional فارغ. لا مشكلة ، فقط استخدم !opt.isPresent() ، أليس كذلك؟
بالطبع ، يمكن أن يكون الأمر كذلك ، لكن من الأسهل دائمًا فهم المنطق if حالته غير مقلوبة. وفي بعض الأحيان ، تنبثق ميزة " Optional في نهاية سلسلة طويلة من المكالمات ، وإذا كنت بحاجة إلى التحقق من ذلك دون مقابل ، فعليك المراهنة ! في البداية
 public boolean needsToCompleteAddress(User user) { return !getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isPresent(); } 
في هذه الحالة ، تخطي ذلك ! سهل جدا بدءًا من Java 11 ، هناك خيار أفضل:
 public boolean needsToCompleteAddress(User user) { return getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isEmpty(); } 
عكس المسند مع 'Predicate::not'
الحديث عن عكس ... واجهة Predicate لها طريقة negate : تقوم بإرجاع دالة تقييم جديدة تقوم بإجراء الفحص نفسه ، لكنها تحول النتيجة. لسوء الحظ ، نادراً ما أتمكن من استخدامه ...
 
المشكلة هي أنه نادراً ما يمكنني الوصول إلى حالة Predicate . في أغلب الأحيان ، أرغب في الحصول على مثل هذا المثال من خلال ارتباط لطريقة (وعكسها) ، ولكن لكي يعمل ذلك ، يجب أن يعرف المترجم ما يجب إحضاره إلى الطريقة - دون ذلك ، لا يمكنه فعل أي شيء. وهذا هو بالضبط ما يحدث إذا استخدمت (String::isBlank).negate() : لم يعد المحول البرمجي يعرف ما يجب أن يكون عليه String::isBlank في هذا الأمر String::isBlank . طبقة محددة بشكل صحيح بإصلاح هذا ، ولكن بأي ثمن؟
رغم أن هناك حل بسيط. لا تستخدم negate مثيل negate ، ولكن استخدم الأسلوب الثابت الجديد Predicate.not(Predicate<T>) من Java 11:
 Stream .of("a", "b", "", "c")  
بالفعل أفضل!
التعبيرات العادية 'Pattern::asMatchPredicate' مع 'Pattern::asMatchPredicate'
هل هناك تعبير منتظم؟ تحتاج إلى تصفية البيانات على ذلك؟ ماذا عن هذا:
 Pattern nonWordCharacter = Pattern.compile("\\W"); Stream .of("Metallica", "Motörhead") .filter(nonWordCharacter.asPredicate()) .forEach(System.out::println); 
كنت سعيدًا جدًا بالعثور على هذه الطريقة! تجدر الإشارة إلى أن هذه طريقة من Java 8. عفوًا ، فاتني ذلك. أضاف Java 11 طريقة أخرى مماثلة: Pattern::asMatchPredicate . ما هو الفرق؟
- يتحقق asPredicateالسلسلة أو جزء منها مع النموذج (يعمل مثلs -> this.matcher(s).find())
- يتحقق asMatchPredicateمن تطابق السلسلة بالكامل مع النمط (يعمل مثلs -> this.matcher(s).matches())
على سبيل المثال ، لدينا تعبير منتظم يتحقق من أرقام الهواتف ، لكنه لا يحتوي على ^ و $ لتتبع بداية ونهاية الخط. ثم لن تعمل التعليمات البرمجية التالية كما قد تتوقع:
 prospectivePhoneNumbers .stream() .filter(phoneNumberPatter.asPredicate()) .forEach(this::robocall); 
هل لاحظت خطأ؟ سيتم تصفية سطر مثل " -152 ? +1-202-456-1414" ، لأنه يحتوي على رقم هاتف صالح. من ناحية أخرى ، لن يسمح Pattern::asMatchPredicate بذلك ، لأن السلسلة بأكملها لن تتطابق مع النموذج.
اختبار الذات
إليك نظرة عامة على جميع اللؤلؤات الإحدى عشرة - هل ما زلت تتذكر ما تفعله كل طريقة؟ إذا كان الأمر كذلك ، لقد نجحت في الاختبار.
- في String:
 - Stream<String> lines()
- String strip()
- String stripLeading()
- String stripTrailing()
- boolean isBlank()
- String repeat(int)
 
- في Path:
 - static Path of(String, String...)
- static Path of(URI)
 
- في Files:
 - String readString(Path) throws IOException
- Path writeString(Path, CharSequence, OpenOption...) throws IOException
- Path writeString(Path, CharSequence, Charset, OpenOption...) throws IOException
 
- في static InputStream nullInputStream():static InputStream nullInputStream()
- في static OutputStream nullOutputStream():static OutputStream nullOutputStream()
- في Reader:Readerstatic Reader nullReader()
- في Writer:static Writer nullWriter()
- في Collection:T[] toArray(IntFunction<T[]>)
- في Optional: المنطقةboolean isEmpty()
- في Predicate:static Predicate<T> not(Predicate<T>)
- في Pattern:Predicate<String> asMatchPredicate()
استمتع مع Java 11!