JavaRush /مدونة جافا /Random-AR /ميزات Java 8 – الدليل النهائي (الجزء الأول)
0xFF
مستوى
Донецк

ميزات Java 8 – الدليل النهائي (الجزء الأول)

نشرت في المجموعة
الجزء الأول من ترجمة المقال مميزات Java 8 – الدليل النهائي . الجزء الثاني هنا (قد يتغير الرابط). ميزات Java 8 - الدليل الشامل (الجزء الأول) - 1 ملاحظة المحرر : تم نشر هذه المقالة بينما كان Java 8 متاحًا للجمهور وكل المؤشرات تشير إلى أن هذا إصدار رئيسي بالفعل. لقد قدمنا ​​هنا أدلة Java Code Geeks بوفرة مثل اللعب مع Java 8 – Lambdas and Concurrency ودليل Java 8 Date and Time API: LocalDateTime و Abstract Class vs. Interface في Java 8 Era . نقوم أيضًا بالربط بـ 15 برنامجًا تعليميًا لـ Java 8 يجب قراءتها من مصادر أخرى . بالطبع ننظر إلى بعض الجوانب السلبية، مثل الجانب المظلم لـ Java 8 . لذا، فقد حان الوقت لتجميع كافة الميزات الرئيسية لـ Java 8 في مكان واحد من أجل راحتك. يتمتع!

1 المقدمة

لا شك أن إصدار Java 8 هو الحدث الأعظم منذ إصدار Java 5 (الذي تم إصداره منذ فترة طويلة، في عام 2004). لقد جلبت العديد من الميزات الجديدة إلى Java سواء في اللغة أو المترجم أو المكتبات أو الأدوات وJVM (جهاز Java الظاهري). في هذا البرنامج التعليمي، سنلقي نظرة على هذه التغييرات ونوضح حالات الاستخدام المختلفة باستخدام أمثلة من الحياة الواقعية. يتكون الدليل من عدة أجزاء يتناول كل منها جانبًا محددًا من المنصة:
  • لغة
  • مترجم
  • المكتبات
  • أدوات
  • بيئة وقت التشغيل (JVM)

2. ميزات جديدة في Java 8

على أية حال، يعد Java 8 إصدارًا رئيسيًا. يمكننا القول أن الأمر استغرق وقتًا طويلاً بسبب تنفيذ الميزات التي كان يبحث عنها كل مطور Java. في هذا القسم سنقوم بتغطية معظمهم.

2.1. Lambdas والواجهات الوظيفية

Lambdas (المعروفة أيضًا بالطرق الخاصة أو المجهولة) هي التغيير اللغوي الأكبر والأكثر توقعًا في إصدار Java 8 بأكمله. فهي تسمح لنا بتحديد الوظيفة كوسيطة طريقة (من خلال الإعلان عن وظيفة حولها)، أو تحديد التعليمات البرمجية كبيانات : المفاهيم التي يعرفها كل مطور وظيفي . العديد من اللغات على منصة JVM (Groovy، Scala ، ...) تحتوي على lambdas منذ اليوم الأول، لكن لم يكن أمام مطوري Java خيار سوى تمثيل lambdas من خلال فئات مجهولة. استغرقت مناقشة تصميم لامدا الكثير من الوقت والجهد من الجمهور. ولكن في نهاية المطاف تم التوصل إلى حلول وسط، مما أدى إلى ظهور تصاميم موجزة جديدة. في أبسط أشكالها، يمكن تمثيل لامدا كقائمة من المعلمات مفصولة بفواصل، ورمز -> ، وجسم. على سبيل المثال:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) )
لاحظ أن نوع الوسيطة e يتم تحديده بواسطة المترجم. بالإضافة إلى ذلك، يمكنك تحديد نوع المعلمة بشكل صريح عن طريق وضع المعلمة بين قوسين. على سبيل المثال:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
إذا كان جسم لامدا أكثر تعقيدًا، فيمكن لفه بأقواس مجعدة، على غرار تعريف الوظيفة العادية في Java. على سبيل المثال:
Arrays.asList( "a", "b", "d" ).forEach( e -< {
    System.out.print( e );
    System.out.print( e );
} );
يمكن أن تشير لامدا إلى أعضاء الفصل والمتغيرات المحلية (مما يجعل المكالمة فعالة ضمنيًا بغض النظر عما إذا finalتم الوصول إلى الحقل أم لا). على سبيل المثال، هذين المقتطفين متكافئان:
String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
و:
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
يمكن لـ Lambdas إرجاع قيمة. سيتم تحديد نوع الإرجاع بواسطة المترجم. لا يلزم الإعلان returnإذا كان جسم لامدا يتكون من سطر واحد. مقتطفا الكود أدناه متكافئان:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
و:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
لقد فكر مطورو اللغة لفترة طويلة في كيفية جعل الوظائف الحالية متوافقة مع لامدا. ونتيجة لذلك، ظهر مفهوم الواجهة الوظيفية. الواجهة الوظيفية هي واجهة ذات طريقة واحدة فقط. ونتيجة لذلك، يمكن تحويله ضمنيًا إلى تعبير لامدا. java.lang.Runnableواثنين java.util.concurrent.Callableمن الأمثلة الرائعة للواجهات الوظيفية. من الناحية العملية، تكون الواجهات الوظيفية هشة للغاية: إذا أضاف شخص ما طريقة أخرى إلى تعريف الواجهة، فلن تكون فعالة ولن تكتمل عملية التجميع. لتجنب هذه الهشاشة وتحديد غرض الواجهة بشكل واضح على أنها وظيفية، تمت إضافة تعليق توضيحي خاص في Java 8 @FunctionalInterface(جميع الواجهات الموجودة في مكتبة Java تلقت التعليق التوضيحيFunctionalInterface). دعونا نلقي نظرة على هذا التعريف البسيط للواجهة الوظيفية:
@FunctionalInterface
public interface Functional {
    void method();
}
هناك شيء واحد يجب أخذه في الاعتبار: الأساليب الافتراضية والأساليب الثابتة لا تنتهك مبدأ الواجهة الوظيفية ويمكن إعلانها:
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Lambdas هي الميزة الأكثر شيوعًا في Java 8. ولديها كل الإمكانيات لجذب المزيد من المطورين إلى هذه المنصة الرائعة وتوفير دعم ذكي للميزات في Java الخالصة. لمزيد من المعلومات التفصيلية، يرجى الرجوع إلى الوثائق الرسمية .

2.2. الواجهات الافتراضية والأساليب الثابتة

قام Java 8 بتوسيع تعريف الواجهات بمفهومين جديدين: الطريقة الافتراضية والطريقة الثابتة. تجعل الطرق الافتراضية الواجهات مشابهة إلى حد ما للسمات، ولكنها تخدم غرضًا مختلفًا قليلاً. إنها تسمح لك بإضافة أساليب جديدة إلى الواجهات الموجودة دون كسر التوافق مع الإصدارات السابقة للإصدارات المكتوبة مسبقًا من هذه الواجهات. الفرق بين الطرق الافتراضية والأساليب المجردة هو أنه يجب تنفيذ الأساليب المجردة بينما لا يتم تنفيذ الطرق الافتراضية. بدلاً من ذلك، يجب أن توفر كل واجهة ما يسمى بالتطبيق الافتراضي، وسيستقبله جميع المتحدرين افتراضيًا (مع القدرة على تجاوز هذا التنفيذ الافتراضي إذا لزم الأمر). دعونا نلقي نظرة على المثال أدناه.
private interface Defaulable {
    // Интерфейсы теперь разрешают методы по умолчанию,
    // клиент может реализовывать  (переопределять)
    // or не реализовывать его
    default String notRequired() {
        return "Default implementation";
    }
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}
تعلن الواجهة Defaulableعن طريقة افتراضية notRequired()باستخدام كلمة أساسية defaultكجزء من تعريف الطريقة. تقوم إحدى الفئات DefaultableImplبتنفيذ هذه الواجهة مع ترك الطريقة الافتراضية كما هي. فئة أخرى، OverridableImpl، تتجاوز التنفيذ الافتراضي وتوفر تطبيقًا خاصًا بها. ميزة أخرى مثيرة للاهتمام تم تقديمها في Java 8 هي أن الواجهات يمكنها الإعلان عن (وتقديم تطبيقات) للطرق الثابتة. هنا مثال:
private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier<Defaulable> supplier ) {
        return supplier.get();
    }
}
يجمع جزء صغير من التعليمات البرمجية بين الطريقة الافتراضية والطريقة الثابتة من المثال أعلاه:
public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );

    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}
يبدو إخراج وحدة التحكم لهذا البرنامج كما يلي:
Default implementation
Overridden implementation
يعد تنفيذ الطريقة الافتراضية في JVM فعالاً للغاية ويتم دعم استدعاء الطريقة من خلال تعليمات الرمز الثانوي. سمحت الأساليب الافتراضية لواجهات Java الحالية بالتطور دون كسر عملية التجميع. الأمثلة الجيدة هي الطرق العديدة المضافة إلى الواجهة java.util.Collection: stream(), parallelStream(), forEach(), removeIf(), ... على الرغم من كونها قوية، إلا أنه يجب استخدام الطرق الافتراضية بحذر: قبل الإعلان عن طريقة افتراضية، يجب أن تفكر مرتين حول ما إذا كان هذا ضروريًا حقًا لأن هذا قد يؤدي إلى لتجميع الغموض والأخطاء في التسلسلات الهرمية المعقدة. لمزيد من المعلومات التفصيلية، يرجى الرجوع إلى الوثائق .

2.3. الطرق المرجعية

تطبق الأساليب المرجعية بناء جملة مفيدًا للإشارة إلى الأساليب الموجودة أو مُنشئات فئات أو كائنات Java (المثيلات). جنبًا إلى جنب مع تعبيرات لامدا، تجعل الأساليب المرجعية بنيات اللغة مدمجة وموجزة، مما يجعلها قائمة على القالب. فيما يلي فئة كمثال Carلتعريفات الطرق المختلفة، دعنا نسلط الضوء على الأنواع الأربعة المدعومة من الطرق المرجعية:
public static class Car {
    public static Car create( final Supplier<Car> supplier ) {
        return supplier.get();
    }

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {
        System.out.println( "Repaired " + this.toString() );
    }
}
الطريقة المرجعية الأولى هي إشارة إلى المُنشئ باستخدام بناء الجملة Class::newأو بديل للأدوية العامة Class< T >::new. لاحظ أن المنشئ ليس لديه أي وسائط.
final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
الخيار الثاني هو إشارة إلى طريقة ثابتة مع بناء الجملة Class::static_method. لاحظ أن الطريقة تأخذ معلمة واحدة بالضبط من النوع Car.
cars.forEach( Car::collide );
النوع الثالث هو إشارة إلى طريقة مثيل كائن عشوائي من نوع معين مع بناء الجملة Class::method. لاحظ أنه لا يتم قبول أية وسائط بواسطة الطريقة.
cars.forEach( Car::repair );
والنوع الأخير والرابع هو إشارة إلى طريقة مثيل لفئة معينة مع بناء الجملة instance::method. يرجى ملاحظة أن الطريقة تقبل معلمة واحدة فقط من النوع Car.
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
يؤدي تشغيل كل هذه الأمثلة كبرامج Java إلى إنتاج مخرجات وحدة التحكم التالية ( Carقد يختلف مرجع الفئة):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
لمزيد من المعلومات التفصيلية وتفاصيل الطرق المرجعية، يرجى الرجوع إلى الوثائق الرسمية .

2.4. التعليقات التوضيحية المكررة

منذ أن قدم Java 5 دعمًا للتعليقات التوضيحية ، أصبحت هذه الميزة شائعة جدًا ومستخدمة على نطاق واسع جدًا. ومع ذلك، كان أحد القيود المفروضة على استخدام التعليقات التوضيحية هو حقيقة أنه لا يمكن الإعلان عن نفس التعليق التوضيحي أكثر من مرة في نفس المكان. يكسر Java 8 هذه القاعدة ويقدم تعليقات توضيحية مكررة. يسمح هذا بتكرار نفس التعليقات التوضيحية عدة مرات في الموقع الذي تم الإعلان عنها فيه. يجب أن تضيف التعليقات التوضيحية المكررة نفسها باستخدام التعليقات التوضيحية @Repeatable. في الواقع، هذا ليس تغييرًا كبيرًا في اللغة بقدر ما هو خدعة مترجم، بينما تظل التقنية كما هي. دعونا نلقي نظرة على مثال بسيط:
package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }

    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };

    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {
    }

    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}
كما نرى، Filterتم إضافة تعليق توضيحي للفصل باستخدام @Repeatable( Filters. class). Filtersهو ببساطة مالك التعليقات التوضيحية Filter، لكن مترجم Java يحاول إخفاء وجودها عن المطورين. Filterableوبالتالي، تحتوي الواجهة على تعليقات توضيحية Filterتم الإعلان عنها مرتين (بدون ذكر Filters). توفر واجهة برمجة التطبيقات Reflection أيضًا طريقة جديدة getAnnotationsByType()لإرجاع التعليقات التوضيحية المكررة من نوع ما (تذكر أن Filterable. class.getAnnotation( Filters. class) سيعيد مثيلًا تم حقنه بواسطة برنامج التحويل البرمجي Filters). سيبدو إخراج البرنامج كما يلي:
filter1
filter2
لمزيد من المعلومات التفصيلية، يرجى الرجوع إلى الوثائق الرسمية .

2.5. تحسين استنتاج النوع

تلقى مترجم Java 8 العديد من تحسينات استدلال النوع. في كثير من الحالات، يمكن تعريف معلمات النوع الصريحة بواسطة المترجم، مما يجعل التعليمات البرمجية أكثر نظافة. دعونا نلقي نظرة على مثال واحد:
package com.javacodegeeks.java8.type.inference;

public class Value<T> {
    public static<T> T defaultValue() {
        return null;
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
وهنا الاستخدام مع النوع Value<String>:
package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value<String> value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}
يتم تحديد معلمة النوع Value.defaultValue()تلقائيًا ولا يلزم تقديمها بشكل صريح. في Java 7، لن يتم تجميع نفس المثال ويجب إعادة كتابته كـ <NOBR>Value.<String>defaultValue()</NOBR>.

2.6. دعم الشرح الموسع

يعمل Java 8 على توسيع السياق حيث يمكن استخدام التعليقات التوضيحية. في الوقت الحاضر، يمكن لأي شيء تقريبًا أن يحتوي على تعليق توضيحي: المتغيرات المحلية، والأنواع العامة، والفئات الفائقة والواجهات المنفذة، وحتى استثناءات الأساليب. بعض الأمثلة معروضة أدناه:
package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {
    }

    public static class Holder<@NonEmpty T> extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder<String> holder = new @NonEmpty Holder<String>();
        @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>();
    }
}
ElementType.TYPE_USEونوعين ElementType.TYPE_PARAMETERجديدين من العناصر لوصف سياق التعليقات التوضيحية ذات الصلة. Annotation Processing APIخضع أيضًا لتغييرات طفيفة للتعرف على أنواع التعليقات التوضيحية الجديدة في Java.

3. ميزات جديدة في مترجم جافا

3.1. أسماء المعلمات

على مدار الوقت، ابتكر مطورو Java مجموعة متنوعة من الطرق لتخزين أسماء معلمات الطريقة في Java bytecode لإتاحتها في وقت التشغيل (على سبيل المثال، مكتبة Paranamer ). أخيرًا، يقوم Java 8 بإنشاء هذه الوظيفة الصعبة في اللغة (باستخدام واجهة برمجة تطبيقات الانعكاس والطريقة Parameter.getName()) والرمز الثانوي (باستخدام وسيطة المترجم الجديدة javac:) –parameters.
package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
    public static void main(String[] args) throws Exception {
        Method method = ParameterNames.class.getMethod( "main", String[].class );
        for( final Parameter parameter: method.getParameters() ) {
            System.out.println( "Parameter: " + parameter.getName() );
        }
    }
}
إذا قمت بتجميع هذه الفئة دون استخدام وسيطة –parametersثم قمت بتشغيل البرنامج، فسترى شيئًا مثل هذا:
Parameter: arg0
مع تمرير المعلمة –parametersإلى المترجم، سيكون مخرجات البرنامج مختلفة (سيظهر الاسم الفعلي للمعلمة):
Parameter: args
بالنسبة لمستخدمي Maven المتقدمين، يمكن إضافة الوسيطة –parameters إلى التجميع باستخدام القسم maven-compiler-plugin:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
    <compilerArgument>-parameters</compilerArgument>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
</plugin>
للتحقق من توفر أسماء المعلمات، توجد isNamePresent()طريقة ملائمة يوفرها الفصل Parameter.

4. أدوات جافا جديدة

يأتي Java 8 مزودًا بمجموعة جديدة من أدوات سطر الأوامر. في هذا القسم سوف ننظر إلى الأكثر إثارة للاهتمام منهم.

4.1. محرك ناشورن: jjs

jjs هو محرك Nashorn مستقل يعتمد على سطر الأوامر. يأخذ قائمة بملفات التعليمات البرمجية المصدر لـ JavaScript ويقوم بتشغيلها. على سبيل المثال، لنقم بإنشاء ملف func.js بالمحتوى التالي:
function f() {
     return 1;
};

print( f() + 1 );
لتشغيل هذا الملف، دعنا نمرره كوسيطة إلى jjs :
jjs func.js
سيكون إخراج وحدة التحكم مثل هذا:
2
لمزيد من التفاصيل راجع الوثائق .

4.2. محلل تبعية الفئة: jdeps

jdeps هي أداة سطر أوامر رائعة حقًا. يُظهر تبعيات مستوى الحزمة أو الفصل لفئات Java. يقبل ملف .class أو مجلد أو ملف JAR كمدخل. بشكل افتراضي ، يقوم jdeps بإخراج التبعيات إلى الإخراج القياسي (وحدة التحكم). على سبيل المثال، دعونا نلقي نظرة على تقرير التبعية الخاص بمكتبة Spring Framework الشهيرة . لإبقاء المثال قصيرًا، دعنا نلقي نظرة على تبعيات ملف JAR فقط org.springframework.core-3.0.5.RELEASE.jar.
jdeps org.springframework.core-3.0.5.RELEASE.jar
ينتج هذا الأمر الكثير، لذا سنقوم بتحليل جزء فقط من المخرجات. يتم تجميع التبعيات حسب الحزم. إذا لم تكن هناك تبعيات، فسيتم عرض لم يتم العثور عليها .
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io
      -> java.lang
      -> java.lang.annotation
      -> java.lang.ref
      -> java.lang.reflect
      -> java.util
      -> java.util.concurrent
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang
      -> java.lang.annotation
      -> java.lang.reflect
      -> java.util
لمزيد من المعلومات التفصيلية، يرجى الرجوع إلى الوثائق الرسمية .
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION