لم يقدم 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
: Reader
static 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!