محمد حسین جلیلی
محمد حسین جلیلی
خواندن ۱۰ دقیقه·۲ سال پیش

امکانات اضافه شده در جاوا 8

1 امکانات جدید در زبان (language) جاوا

1-1قابلیت Lambdas و رابط کاربردی

قابلیت Lambdas بزرگترین تغییر در جاوا 8 است. این قابلیت به ما اجازه میدهد تا توابع را بصورت یک آرگومان متود (با ارسال به یک تابع) یا کد را بصورت داده ببینیم. در زیر مثالهای استفاده از این قابلیت را میبینیم.

مثال 1:

Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).forEach( e -> System.out.println( e ) );

مثال 2:

Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).forEach( ( String e ) -> System.out.println( e ) );

مثال 3:

Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).forEach( e -> {
System.out.print( e );
System.out.print( e );
} );

مثال 4:

String separator = &quot,"
Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).forEach(
( String e ) -> System.out.print( e + separator ) );

مثال 5:

final String separator = &quot,"
Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).forEach(
( String e ) -> System.out.print( e + separator ) );

مثال 6:

Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );

مثال 7:

Arrays.asList( &quota&quot, &quotb&quot, &quotd&quot ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );

1-2 تغییر Interface پیشفرض یا متودهای static

جاوا 8 در extend کردن از interfaceدو مفهوم جدید تعریف کرده است: متودهای default و static. متودهای default امکان اضافه کردن متودهای جدید در interface های موجود را بدون شکستن سازگاری باینری با کدهای نوشته شده برای ورژنهای قدیمی تر آن interfaceها را می دهد.

تفاوت بین متودهای default و متودهای abstractاین است که متودهای abstract نیازمند پیاده سازی شدن هستند اما متودهای default نه. در ادامه مثالی در این رابطه را میبینیم:

private interface Defaulable {
// Interfaces now allow default methods, the implementer may or
// may not implement (override) them.
default String notRequired() {
return &quotDefault implementation"
}
}
private static class DefaultableImpl implements Defaulable {
}
private static class OverridableImpl implements Defaulable {
@Override
public String notRequired() {
return &quotOverridden implementation"
}
}

رابط Defaulable یک متود defaultبه نام notRequired با استفاده از کلمه کلیدی default تعریف میکنیم. کلاس DefaultableImplرابط Defaulable را بدون overrideکردن متود default پیاده سازی میکند. اما کلاس OverridableImpl متود defaultرا override میکند.

امکان جذاب دیگر در جاوا 8 این است که در interfaceمیتوان متودهای static را تعریف کرد. مثال زیر را ببنید:

private interface DefaulableFactory {
// Interfaces now allow static methods
static Defaulable create( Supplier< Defaulable > supplier ) {
return supplier.get();
}
}

حالا در متود main زیر نحوه استفاده از آنرا میبینیم.

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() );
}

و خروجی console:

Default implementation
Overridden implementation

1-3بررسی Reference های متود

این قابلیت برای اشاره مستقیم به متودها یا سازندهای کلاسها یا آبجکتهای(نمونه های) جاوا استفاده میشود. Reference های متود با پیوستگی به Lambdas نمای زبان را فشرده و خلاصه میکند.

در مثال زیر ابتدا کلاس کار را تعریف میکنیم و سپس نحوه استفاده از این قابلیت را روی آن نمایش میدهیم.

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( &quotCollided &quot + car.toString() );
}
public void follow( final Car another ) {
System.out.println( &quotFollowing the &quot + another.toString() );
}
public void repair() {
System.out.println( &quotRepaired &quot + this.toString() );
}
}

نوع اول reference های متود، referenceسازنده بصورت Class::new یا برای داده های عمومی Class<T>::newاست. دقت کنید که سازنده آرگومانی ندارد.

final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );

نوع دوم referenceهای متود، reference متود staticبصورت Class::static_methodاست. دقت کنید که متود دقیقا یک پارامتر از نوع Carدریافت میکند.

cars.forEach( Car::collide );

نوع سوم referenceهای متود، reference به متود یک آبجکت مشخص شده با نوع بصورت Class::method است. دقت کنید که توسط متود پارامتری دریافت نمیشود.

cars.forEach( Car::repair );

در نهایت نوع چهارم refrenceبه متود نمونه کلاس خاص نمونه بصورت instance::method است.

دقت کنید که متود دقیقا یک پارامتر از نوع Car می پذیرد.

final Car police = Car.create( Car::new );
cars.forEach( police::follow );

1-4 جایگزینی annotationها

از جاوا 5 پشتیبانی از annotationها معرفی شد، این امکان بسیار مشهور شد و بصورت گسترده استفاده شد. به هر حال یکی از محدودیت های استفاده از annotationدر واقع این است که نمیتوان بیش از یکی را در یک مکان تعریف کرد. جاوا 8 این قانون را شکسته و جایگزینی annotationها را معرفی کرده است. این امکان اجازه میدهد تا annotationچندین بار در همان مکان تعریف شده تکرار شود.

جایگزینی annotationها با استفاده از @Repeatable annotationانجام میشود. در ادامه مثالی از آنرا میبینیم:

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( &quotfilter1&quot )
@Filter( &quotfilter2&quot )
public interface Filterable {
}
public static void main(String[] args) {
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() );
}
}
}

خروجی برنامه بشکل زیر است:

filter1
filter2

2 امکانات جدید در کامپایلر جاوا

2-1 نام پارامترها

برای سالها توسعه دهندگان جاوا راههای متفاوتی را برای نگهداشتن نام پارامترهای متود در بایت کد جاوا و آنها را در زمان اجرا ایجاد میکردند (برای مثال Paranamer library). در نهایت جاوا 8 به این نیاز پاسخ داد و امکانی با استفاده از Reflection API و متود 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( &quotmain&quot, String[].class );
for( final Parameter parameter: method.getParameters() ) {
System.out.println( &quotParameter: &quot + parameter.getName() );
}
}
}

اگر این کلاس را بدون استفاده از آرگومان –parametersکامپایل و اجرا کنید، خروجی زیر را خواهید دید:

Parameter: arg0

اما با آرگومان –parametersخواهید دید:

Parameter: args

3 امکانات جدید در کتابخانه های جاوا

3-1 بررسی Stream ها

به تازگی Stream API(java.util.stream) به جاوا اضافه شده است. Stream API پردازشهای روی collectionsها را بصورت قابل توجه ای ساده میکند (البته تنها به collectionsمحدود نمیشود).

اینجانب یک بررسی ساده روی سورس کدهای مربوط به Stream ها انجام دادم. دیدم که Stream یک interface است و در نتیجه برای آن کدی نوشته نشده است و در عوض تعداد زیادی از کلاسها این interface را implement کرده اند. در نتیجه هر کلاس یا Collection با استفاده از متودهای درون خود اینکار را انجام داده است. پس Stream در صورتی که بصورت parallel استفاده نشود، هیچ تاثیر منفی یا مثبتی روی کارایی و مصرف حافظه کدها ندارد و تنها کد نویسی را راحت میکند.

با یک مثال ساده کار را شروع میکنیم.

public class Streams {
private enum Status {
OPEN, CLOSED
};
private static final class Task {
private final Status status;
private final Integer points;
Task( final Status status, final Integer points ) {
this.status = status;
this.points = points;
}
public Integer getPoints() {
return points;
}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format( &quot[%s, %d]&quot, status, points );
}
}
}

اکنون از کلاس taskیک مجموعه میسازیم.

حالا می خواهیم با استفاده از Stream مجموعه pointهای مربوط به taskهای در حالت OPEN را محاسبه کنیم.

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println( &quotTotal points: &quot + totalPointsOfOpenTasks );

و خروجی بصورت زیر است:

Total points: 18

حال می خواهیم مجموع pointهای تمام task ها را بصورت موازی (parallel) محاسبه کنیم.

// Calculate total points of all tasks
final double totalPoints = tasks
.stream()
.parallel()
.map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce( 0, Integer::sum );
System.out.println( &quotTotal points (all tasks): &quot + totalPoints );

و خروجی بصورت زیر است:

Total points: 26

حال میخواهیم taskها را به توجه به OPENیا CLOSE بودنشان دسته بندی کنیم.

// Group tasks by their status
final Map< Status, List< Task > > map = tasks
.stream()
.collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );

و خروجی بصورت زیر است:

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

حال میخواهیم درصد وزنی هر taskرا نسبت به کل taskها بر روی pointهای آن محاسبه کنیم.

// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks
.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
.mapToObj( percentage -> percentage + &quot%&quot ) // Stream< String>
.collect( Collectors.toList() ); // List< String >
System.out.println( result );

و خروجی بصورت زیر است:

[19%, 50%, 30%]

در ادامه مثالی می آوریم که از Stream API در collectionsاستفاده نمیشود و در کار با فایل استفاده کرده ایم.

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
lines.( () -> System.out.println(&quotDone!&quot) ).forEach( System.out::println );
}

کد بالا تمام خطهای فایل را در خروجی چاپ میکند و در انتها عبارت "Done!" را چاپ میکند.

3-2 موتور جاوا اسکریپت Nashorn

جاوا 8 امکان توسعه و اجرای برنامه های جاوا اسکریپ در JVM را از طریق موتور جاوا اسکریپت Nashorn فراهم کرده است. موتور جاوا اسکریپت Nashorn تنها پیاده سازی دیگری از javas.script.ScriptEngine است و همان مجموعه قوانین پیروی میکند که اجازه قابلیت همکاری جاوا و جاوا اسکریپت را میدهد. در اینجا یک مثال کوچک را میبینیم.

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( &quotJavaScript&quot );
System.out.println( engine.getClass().getName() );
System.out.println( &quotResult:&quot + engine.eval( &quotfunction f() { return 1; }; f() + 1;&quot ) );

و خروجی بصورت زیر است:

jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2

3-3 بررسی Base64

سرانجام پشتیبانی از کدگزاری Base64در کتابخانه های استاندارد جاوا قرار گرفت. در ادامه با مثالی نحوه استفاده از آنرا نمایش میدهیم.

package com.javacodegeeks.java8.base64;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64s {
public static void main(String[] args) {
final String text = &quotBase64 finally in Java 8!"
final String encoded = Base64
.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println( encoded );
final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( decoded );
}
}

و خروجی بصورت زیر است:

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!

همچنین انواع دیگر از کدگزاری مخصوص URLو MIME نیز توسط همین کلاس پشتیبانی میشود.

3-4 آرایه های موازی

جاوا 8 تعداد بسیار زیادی از متودها را برای پردازش موازی آرایه ها اضافه کرده است. مسلما یکی از مهمترین آنها parallelSort()است که میتواند به میزان قابل توجه ای از طریق مرتب سازی در ماشینهای چند هسته ای سرعت را بالا ببرد. مثال کوچکی که در ادامه می آید متودهای جدید خانواده parallelXxxرا در عمل اثبات میکند.

package com.javacodegeeks.java8.parallel.arrays;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
public class ParallelArrays {
public static void main( String[] args ) {
long[] arrayOfLong = new long [ 20000 ];
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + &quot &quot ) );
System.out.println();
Arrays.parallelSort( arrayOfLong );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + &quot &quot ) );
System.out.println();
}
}

از طریق کد کوچکی با استفاده از متود parallelSetAllاستفاده میکند تا آرایه ای را با 20000 مقدار تصادفی پر کند. سپس با استفاده از parallelSortآنها را مرتب میکند و سپس ده مقدار کوچکتر آنرا چاپ میکند. خروجی مشابه زیر است:

Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

4 مراجع

[1] http://www.javacodegeeks.com/2014/05/java-8-features-tutorial.html.

[2] http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html

[3] http://docs.oracle.com/javase/tutorial/

[4]http://blog.arungupta.me/2014/03/wildfly8-jdk8-netbeans8-javaee7-excellent-combo-enterprise-java/

[5] http://winterbe.com/posts/2014/03/16/java-8-tutorial/


https://teaching.iranmodares.com/teaching-index.php?metaword=%D9%85%D8%AD%D9%85%D8%AF+%D8%AD%D8%B3%DB%8C%D9%86+%D8%AC%D9%84%DB%8C%D9%84%DB%8C
system outجاواout printlnjava8
بعضی وقتا زمانی که در یک جای تاریک هستید فکر می‌کنید دفن شده‌اید اما در واقع شما کاشته شده‌اید
شاید از این پست‌ها خوشتان بیاید