قسمت اول ترجمه مقاله ویژگی های Java 8 – The ULTIMATE Guide . بخش دوم اینجاست (لینک ممکن است تغییر کند). توجه سردبیر : این مقاله در حالی منتشر شد که جاوا 8 در دسترس عموم بود و همه نشانهها حاکی از آن است که این در واقع یک نسخه اصلی است. در اینجا ما به وفور راهنماهای Java Code Geeks مانند بازی با جاوا 8 - Lambdas و Concurrency ، Java 8 Date and Time API Guide: LocalDateTime و Abstract Class در مقابل رابط در عصر جاوا 8 را ارائه کرده ایم . ما همچنین به 15 آموزش جاوا 8 از منابع دیگر پیوند می دهیم . البته ما به برخی از جنبه های منفی مانند Dark Side Java 8 نگاه می کنیم . بنابراین، وقت آن است که تمام ویژگی های اصلی جاوا 8 را در یک مکان برای راحتی خود جمع آوری کنید. لذت ببرید!
1. معرفی
بدون شک، انتشار جاوا 8 بزرگترین رویداد از زمان جاوا 5 (که مدت ها پیش، در سال 2004 منتشر شد) است. بسیاری از ویژگیهای جدید را هم در زبان، کامپایلر، کتابخانهها، ابزارها و JVM (ماشین مجازی جاوا) به جاوا آورد. در این آموزش، ما قصد داریم نگاهی به این تغییرات بیندازیم و موارد استفاده مختلف را با مثالهای واقعی نشان دهیم. این راهنما از چندین بخش تشکیل شده است که هر یک به جنبه خاصی از پلتفرم می پردازد:- زبان
- کامپایلر
- کتابخانه ها
- ابزار
- محیط زمان اجرا (JVM)
2. ویژگی های جدید در جاوا 8
در هر صورت، جاوا 8 یک نسخه اصلی است. می توان گفت که به دلیل پیاده سازی ویژگی هایی که هر توسعه دهنده جاوا به دنبال آن بود، زمان زیادی طول کشید. در این بخش قصد داریم به بیشتر آنها بپردازیم.2.1. لامبدا و رابط های کاربردی
Lambdas (همچنین به عنوان روش های خصوصی یا ناشناس شناخته می شود) بزرگترین و مورد انتظارترین تغییر زبان در کل نسخه جاوا 8 است. آنها به ما اجازه می دهند تا عملکرد را به عنوان آرگومان متد (با اعلام یک تابع در اطراف آن) مشخص کنیم، یا کد را به عنوان داده مشخص کنیم. : مفاهیمی که هر توسعه دهنده عملکردی با آن آشناست برنامه نویسی _ بسیاری از زبانهای پلتفرم JVM (Groovy، Scala ، ...) از روز اول دارای لامبدا بودند، اما توسعهدهندگان جاوا چارهای جز نمایش لامبدا از طریق کلاسهای ناشناس نداشتند. بحث در مورد طراحی لامبدا زمان و تلاش زیادی را از مردم گرفت. اما در نهایت سازش هایی پیدا شد که منجر به ظهور طرح های مختصر جدیدی شد. در سادهترین شکل، یک لامبدا را میتوان بهعنوان فهرستی از پارامترها، نماد –> و بدنه جدا شده با کاما نشان داد. مثلا: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
دسترسی یا عدم دسترسی به فیلد مؤثر می کند). به عنوان مثال، این 2 قطعه معادل هستند:
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
دو نمونه عالی از رابط های کاربردی. در عمل، رابط های کاربردی بسیار شکننده هستند: اگر کسی حتی یک روش دیگر را به تعریف رابط اضافه کند، دیگر کاربردی نخواهد بود و فرآیند کامپایل کامل نخواهد شد. برای جلوگیری از این شکنندگی و تعریف واضح هدف یک رابط به عنوان کاربردی، یک حاشیه نویسی ویژه در جاوا 8 اضافه شد @FunctionalInterface
(همه رابط های موجود در کتابخانه جاوا حاشیه نویسی @FunctionalInterface را دریافت کردند). بیایید به این تعریف ساده از یک رابط کاربردی نگاه کنیم:
@FunctionalInterface
public interface Functional {
void method();
}
یک چیز وجود دارد که باید در نظر داشت: روش های پیش فرض و روش های استاتیک اصل رابط کاربری را نقض نمی کنند و می توان آنها را اعلام کرد:
@FunctionalInterface
public interface FunctionalDefaultMethods {
void method();
default void defaultMethod() {
}
}
Lambdas محبوب ترین ویژگی جاوا 8 است. آنها تمام پتانسیل را دارند که توسعه دهندگان بیشتری را به این پلتفرم فوق العاده جذب کنند و از ویژگی های جاوا خالص پشتیبانی هوشمند کنند. برای اطلاعات بیشتر، لطفاً به اسناد رسمی مراجعه کنید .
2.2. رابط های پیش فرض و روش های استاتیک
جاوا 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
، پیاده سازی پیش فرض را لغو می کند و خود را ارائه می دهد. یکی دیگر از ویژگیهای جالب معرفی شده در جاوا 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.util.Collection
: stream()
, , , ... اگرچه قدرتمند هستند، اما روشهای پیشفرض باید با احتیاط مورد استفاده قرار گیرند: قبل از اعلام یک روش پیشفرض parallelStream()
، باید دو بار در مورد اینکه آیا این واقعاً ضروری است فکر کنید زیرا ممکن است منجر شود. برای گردآوری ابهامات و خطاها در سلسله مراتب پیچیده. برای اطلاعات دقیق تر، لطفا به مستندات مراجعه کنید . forEach()
removeIf()
2.3. روش های مرجع
متدهای مرجع، سینتکس مفیدی را برای ارجاع به روشهای موجود یا سازنده کلاسها یا اشیاء جاوا (نمونهها) پیادهسازی میکنند. همراه با عبارات لامبدا، روش های مرجع ساختارهای زبان را فشرده و مختصر می کنند و آن را مبتنی بر الگو می کنند. در زیر یک کلاس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 );
اجرای همه این نمونه ها به عنوان برنامه های جاوا خروجی کنسول زیر را تولید می کند (مرجع کلاس 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. حاشیه نویسی های تکراری
از زمانی که جاوا 5 پشتیبانی از حاشیه نویسی را معرفی کرد ، این ویژگی بسیار محبوب و بسیار مورد استفاده قرار گرفت. با این حال، یکی از محدودیتهای استفاده از حاشیهنویسی این بود که یک حاشیهنویسی را نمیتوان بیش از یک بار در یک مکان اعلام کرد. جاوا 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
، اما کامپایلر جاوا سعی می کند حضور آنها را از توسعه دهندگان پنهان کند. بنابراین، رابط Filterable
حاوی حاشیه نویسی است Filter
که دو بار اعلام شده است (بدون ذکر Filters
). Reflection API همچنین روش جدیدی را getAnnotationsByType()
برای برگرداندن حاشیه نویسی های تکراری از نوع خاصی ارائه می دهد (به یاد داشته باشید که Filterable. class
.getAnnotation( Filters. class
) یک نمونه تزریق شده توسط کامپایلر را برمی گرداند Filters
). خروجی برنامه به شکل زیر خواهد بود:
filter1
filter2
برای اطلاعات بیشتر، لطفاً به اسناد رسمی مراجعه کنید .
2.5. استنتاج نوع بهبود یافته
کامپایلر جاوا 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()
به طور خودکار تعیین می شود و نیازی به ارائه صریح نیست. در جاوا 7، همان مثال کامپایل نمی شود و باید به صورت <NOBR>Value بازنویسی شود.<String>defaultValue()</NOBR>.
2.6. پشتیبانی از حاشیه نویسی گسترده
جاوا 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
همچنین برای شناسایی انواع حاشیه نویسی جدید در جاوا دستخوش تغییرات جزئی شده است.
3. ویژگی های جدید در کامپایلر جاوا
3.1. نام پارامترها
در طول زمان، توسعه دهندگان جاوا راه های مختلفی برای ذخیره نام پارامترهای روش در بایت کد جاوا ابداع کرده اند تا در زمان اجرا در دسترس قرار گیرند (به عنوان مثال، کتابخانه Paranamer ). در نهایت، جاوا 8 این تابع دشوار را در زبان (با استفاده از API و متد ReflectionParameter.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. ابزارهای جدید جاوا
جاوا 8 با مجموعه جدیدی از ابزارهای خط فرمان عرضه می شود. در این بخش به جالب ترین آنها خواهیم پرداخت.4.1. موتور ناشورن: jjs
jjs یک موتور Nashorn مستقل است که مبتنی بر خط فرمان است. لیستی از فایل های کد منبع جاوا اسکریپت را می گیرد و آنها را اجرا می کند. به عنوان مثال، اجازه دهید یک فایل func.js با محتوای زیر ایجاد کنیم:function f() {
return 1;
};
print( f() + 1 );
برای اجرای این فایل، اجازه دهید آن را به عنوان آرگومان به jjs ارسال کنیم :
jjs func.js
خروجی کنسول به صورت زیر خواهد بود:
2
برای جزئیات بیشتر به مستندات مراجعه کنید .
4.2. تحلیلگر وابستگی کلاس: jdeps
jdeps یک ابزار خط فرمان واقعا عالی است. وابستگی های سطح بسته یا کلاس را برای کلاس های جاوا نشان می دهد. این یک فایل .class ، پوشه یا فایل JAR را به عنوان ورودی می پذیرد. به طور پیش فرض ، jdeps وابستگی ها را به خروجی استاندارد (کنسول) خروجی می دهد. به عنوان مثال، اجازه دهید به گزارش وابستگی کتابخانه محبوب Spring Framework نگاه کنیم . برای کوتاه نگه داشتن مثال، اجازه دهید فقط به وابستگی های فایل JAR نگاه کنیمorg.springframework.core-3.0.5.RELEASE.jar
.
jdeps org.springframework.core-3.0.5.RELEASE.jar
این دستور مقدار زیادی خروجی می دهد، بنابراین ما فقط بخشی از خروجی را تجزیه و تحلیل می کنیم. وابستگی ها بر اساس بسته ها گروه بندی می شوند. اگر وابستگی وجود نداشته باشد، not found نمایش داده می شود .
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
برای اطلاعات بیشتر، لطفاً به اسناد رسمی مراجعه کنید .
GO TO FULL VERSION