JavaRush /בלוג Java /Random-HE /תכונות Java 8 - המדריך האולטימטיבי (חלק 1)
0xFF
רָמָה
Донецк

תכונות Java 8 - המדריך האולטימטיבי (חלק 1)

פורסם בקבוצה
החלק הראשון של התרגום של המאמר Java 8 Features – The ULTIMATE Guide . החלק השני נמצא כאן (ייתכן שהקישור ישתנה). תכונות Java 8 - המדריך האולטימטיבי (חלק 1) - 1 הערת העורך : מאמר זה פורסם בזמן ש-Java 8 היה זמין לציבור וכל הסימנים מצביעים על כך שזו אכן גרסה מרכזית. כאן סיפקנו מדריכי Java Code Geeks בשפע כמו Playing with Java 8 – Lambdas and Concurrency , Java 8 Date and Time API Guide: LocalDateTime ומחלקה מופשטת לעומת ממשק בעידן Java 8 . אנו גם מקשרים ל -15 מדריכים שחייבים לקרוא ב-Java 8 ממקורות אחרים . כמובן שאנחנו מסתכלים על כמה מהחסרונות, כמו הצד האפל של Java 8 . אז, הגיע הזמן לאסוף את כל התכונות העיקריות של Java 8 במקום אחד לנוחיותך. תהנה!

1. הקדמה

ללא ספק, השחרור של Java 8 הוא האירוע הגדול ביותר מאז Java 5 (ששוחרר לפני די הרבה זמן, ב-2004). זה הביא תכונות חדשות רבות ל-Java הן בשפה, מהדר, ספריות, כלים ו-JVM (Java Virtual Machine). במדריך זה, אנו הולכים להסתכל על השינויים הללו ולהדגים מקרי שימוש שונים עם דוגמאות מהחיים האמיתיים. המדריך מורכב ממספר חלקים, שכל אחד מהם מתייחס להיבט ספציפי של הפלטפורמה:
  • שפה
  • מַהְדֵר
  • ספריות
  • כלים
  • סביבת זמן ריצה (JVM)

2. תכונות חדשות ב-Java 8

בכל מקרה, Java 8 היא מהדורה מרכזית. אנו יכולים לומר שזה לקח כל כך הרבה זמן בגלל הטמעת התכונות שכל מפתח ג'אווה חיפש. בחלק זה אנו הולכים לכסות את רובם.

2.1. למבדות וממשקים פונקציונליים

למבדות (הידועות גם כשיטות פרטיות או אנונימיות) הן השינוי השפה הגדול והצפוי ביותר בכל מהדורת Java 8. הן מאפשרות לנו לציין פונקציונליות כארגומנט מתודה (על ידי הכרזה על פונקציה סביבו), או לציין קוד כנתונים : מושגים שכל מפתח פונקציונליות מכיר. תכנות _ לשפות רבות בפלטפורמת JVM (Groovy, Scala , ...) היו למבדות מהיום הראשון, אבל למפתחי Java לא הייתה ברירה אלא לייצג את למבדות באמצעות שיעורים אנונימיים. הדיון בעיצוב הלמבדות לקח הרבה זמן ומאמץ מהציבור. אבל בסופו של דבר נמצאו פשרות, שהובילו להופעתם של עיצובים תמציתיים חדשים. בצורתה הפשוטה ביותר, למבדה ניתן לייצג כרשימה מופרדת בפסיקים של פרמטרים, סמל –> וגוף. לדוגמה:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) )
שימו לב שסוג הארגומנט e נקבע על ידי המהדר. בנוסף, ניתן לציין במפורש את סוג הפרמטר על ידי גלישת הפרמטר בסוגריים. לדוגמה:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
במקרה שגוף הלמבדה מורכב יותר, ניתן לעטוף אותו בפלטה מתולתלת, בדומה להגדרת פונקציה רגילה בג'אווה. לדוגמה:
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 ) );
למבדות יכולות להחזיר ערך. סוג ההחזר ייקבע על ידי המהדר. אין צורך בהצהרה 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() {
    }
}
למבדות הן התכונה הפופולרית ביותר של Java 8. יש להן את כל הפוטנציאל למשוך מפתחים נוספים לפלטפורמה הנפלאה הזו ולספק תמיכה חכמה לתכונות בג'אווה טהורה. למידע מפורט יותר, עיין בתיעוד הרשמי .

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 הוא יעיל מאוד והפעלת השיטה נתמכת על ידי הוראות bytecode. שיטות ברירת המחדל אפשרו לממשקי 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( מסננים. class). Filtersהוא פשוט הבעלים של הערות Filter, אבל מהדר Java מנסה להסתיר את נוכחותם ממפתחים. לפיכך, הממשק Filterableמכיל הערות Filterהמוצהרות פעמיים (בלי להזכיר Filters). ה-Reflection API מספק גם שיטה חדשה 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. תכונות חדשות במהדר Java

3.1. שמות פרמטרים

לאורך כל הזמן, מפתחי Java המציאו מגוון דרכים לאחסן שמות פרמטרים של שיטה ב-Java bytecode כדי להפוך אותם לזמינים בזמן ריצה (לדוגמה, ספריית Paranamer ). לבסוף, Java 8 יוצר את הפונקציה הקשה הזו בשפה (באמצעות ה-Reflection API ושיטה Parameter.getName()) וב-bytecode (באמצעות ארגומנט המהדר החדש 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. מנוע Nashorn: 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