5 ویژگی در جاوا 8 که نحوه نوشتن کد را تغییر داد-قسمت اول

توسط منصوری جاوا, آموزش با بدون نظر

ج

جاوا 8 ویژگی های زیادی به هر دو سطح JVM و زبان اضاف کرده است. بیشتر این ویژگی ها بهبود در زیر ساخت در سطح کامپایلر ،JVM و help-system است، که برای فعال کردن آنها نیاز به انجام کاری جز نصب JDK8 نیست.در این مقاله 5 ویژگی که دانستن آنها در نحوه نوشتن کدها موثر است را معرفی می کنیم:

I . Lambda expressions

جاوا یک زبان شی گرا است. بدون در نظر گرفتن انواع داده اولیه، همه چیز در جاوا شی است. هیچ راهی برای تعریف یک متد در جاوا نیست. برای مثال هیچ راهی برای ارسال یک متد به عنوان آرگومان یا برگرداندن بدنه متد وجود ندارد. با توجه به این محدودیت جاوا 8 در سطح زبان یک ویژگی جدید به نام عبارت لامبدا افزود.

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

 numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

برای چاپ هر آیتم به حلقه زیر نیاز است:

for (int number : numbers) {
	System.out.println(number);
}
				

این کاریست که ما برای پیمایش collection ها انجام می دهیم و مثل این می ماند که برای تک تک آیتم ها باید بگوییم که چه کاری باید انجام شود به این صورت:

آیا آیتمی در لیست وجود دارد؟

بله مقدار 1 وجود دارد

مقدار 1 را چاپ کن

آیا آیتمی در لیست وجود دارد؟

بله مقدار 2 وجود دارد

مقدار 2 را چاپ کن

.

.

.

آیا آیتمی در لیست وجود دارد؟

خیر آیتمی وجود ندارد

از حلقه خارج شو!

به این کار پیمایش خارجی می گویند که آیتم ها را تک تک از collection می خواند. دلیلی که بهتر است که به جای پیمایش خارجی از پیمایش داخلی استفاده کنیم یعنی به این صورت: "آیتم ها را از لیست بخوان و چاپ کن"، این است که کامپایلر می تواند از موازی سازی یا راه های دیگر برای بهینه کردن پردازش استفاده کند. با استفاده از عبارت لامبدا می توان از پیمایش داخلی استفاده کرد:

numbers.forEach((Integer value) -> System.out.println(value));

از syntax زیر برای نوشتن Lambda expressions استفاده می شود:

(argument) -> (body)

برای مثال:

(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }

ساختار Lambda expressions:

1. می تواند هیچ، یک یا بیشتر از یک پارامتر بگیرد.

2. نوع پارامتر می تواند به صورت مستقیم در عبارت آورده شود و یا از متن استنباط شود: (int a) مانند (a) است.

3. پارامتر ها در پرانتز قرار می گیرند و با کاما جدا می شوند: (int a, int b) یا (String a, int b, float c)

4. پرانتز خالی برای نشان دادن مجموعه خالی از پارامتر ها به کار برده می شود.

5. اگر فقط یک پارامتر بود و نوع آن هم در متن مشخص شده بود نیازی به استفاده از پرانتز نیست.

6. بدنه Lambda expressions می تواند هیچ ، یک یا بیشتر از یک statement داشته باشد.

7. اگر یک statement در بدنه بود نیازی به براکت نیست و نوع بازگشتی تابع با نوع بدنه یکسان است.

مثال:

1. n -> n % 2 != 0;

Given a number n returns a boolean indicating if it is odd.

2. (char c) -> c == 'y';

Given a character c returns a boolean indicating if it is equal to ‘y’.

3. (x, y) -> x + y;

Given two numbers x and y returns another number with their summation.

4. (int a, int b) -> a * a + b * b;

Given two integers a and b returns another integer with the sum of their squares.

5. () -> 42

Given no parameters returns the integer 42.

6. () -> { return 3.14 };

Given no parameters returns the double 3.14.

7. (String s) -> { System.out.println(s); };

Given a string s prints the string to the main output and returns void.

8. () -> { System.out.println("Hello World!"); };

Give no parameters prints Hello World! to the main output and returns void.

Functional Interface:

در جاوا Functional Interface یک interface با یک abstract method تنها است که به آنها SAM (Single Abstract Method) هم گفته می شود. قبل از جاوا 8، Functional Interface های متعددی وجود داشت:

public interface Comparator<T> {
    int compare(T o1, T o2);
}

public interface Callable<V> {
    V call() throws Exception;
}

public interface ActionListener extends EventListener {
    public void actionPerformed(ActionEvent e);
}

public interface Runnable {
    public void run();
}
                

در جاوا 8 هم Functional Interface های جدیدی تعریف شده است که معروف ترین ها آن ها در زیر آمده است:

public interface Predicate<T> {
    boolean test(T t);
}

public interface Function<T,R> {
    R apply(T t);
}

public interface BinaryOperator<T> {
    T apply(T left, T right);
}

public interface Consumer<T> {
    void accept(T t);
}

public interface Supplier<T> {
    T get();
}
                

در جاوا 8 می توان از این توابع در عبارت لامبدا استفاده کرد، برای مثال شما نیاز به تابعی دارید که جمع مقادیر ورودی را بر گرداند:

public int sumAll(List<Integer> numbers) {
    int total = 0;
    for (int number : numbers) {
        total += number;
    }
    return total;
}
                

بار دیگر نیاز پیدا می کنید که تابعی داشته باشید که فقط جمع اعداد زوج را برگرداند، با گذاشتن یک شرط این کار را انجام می دهید:

public int sumAllEven(List<Integer> numbers) {
    int total = 0;
    for (int number : numbers) {
        if (number % 2 == 0) {
            total += number;
        }
    }
    return total;
}
                

این بار به جمعی متفاوت نیاز پیدا می کنید، دوباره کد را کپی کرده و آن را تغییر می دهید. اما در جاوا 8 با استفاده از Functional Interface و عبارت لامبدا می توانید برای تمام نیاز های بالا فقط یک تابع بنویسید به این صورت که نوع action که نیاز دارید را به تابع ارسال کنید:

public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
    int total = 0;
    for (int number : numbers) {
        if (p.test(number)) {
            total += number;
        }
    }
    return total;
}
                

این تابع تنها در صورتی مقادیر را با هم جمع می کند که predicate بر قرار باشد(predicate یکی از Functional Interface هاست). فراخوانی تابع به این صورت است:

sumAll(numbers, n -> true);
sumAll(numbers, n -> n % 2 == 0);
sumAll(numbers, n -> n > 3);
                

مثال دیگر که نشان می دهد چگونه می توان از Functional Interface و عبارت لامبدا در جاوا برای کپسوله سازی و جلوگیری از تکرار استفاده کرد:

فرض کنید منبعی دارید که می خواهید از آن استفاده کنید .

public class Resource {
	public Resource() {
		System.out.println("Opening resource");
	}

	public void operate() {
		System.out.println("Operating on resource");
	}

	public void dispose() {
		System.out.println("Disposing resource");
	}
}
                

پس از استفاده از آن برای جلوگیری از اتلاف حافظه باید آن را آزاد کنید:

Resource resource = new Resource();
resource.operate();
resource.dispose();
                

اما برای اینکه مطمئن شوید که دستور dispose اجرا می شود باید از try/finally استفاده کنید:

Resource resource = new Resource();
	try {
		resource.operate();
	} finally {
		resource.dispose();
	}
                

این کار دو مشکل دارد: 1 – هرجا که بخواهید از منبع استفاده کنید باید دستورات بالا را بنویسید 2- ممکن است فراموش کنید که try/finally بنویسید یا منبع را آزاد کنید.

برای حل مشکلات می توان به این صورت عمل کرد:

public static void withResource(Consumer<Resource> consumer) {
    Resource resource = new Resource();
    try {
        consumer.accept(resource);
    } finally {
        resource.dispose();
    }
}
                

در این صورت می توانیم تنها با دستور زیر از منبع استفاده کنیم و تضمین می شود که منبع آزاد می شود:

withResource(resource -> resource.operate());
                



مثال های بیشتر:
www.javacodegeeks.com
javapapers.com
منابع :
Java 8 Lambda Expressions Tutorial with Examples
Why We Need Lambda Expressions in Java - Part 1
Why We Need Lambda Expressions in Java - Part 2
Java Lambda Expressions Basics