کد نویسی با Lambda
در سری مقالات آموزش جاوا, میخواهیم با استفاده از مفاهیمی که در مقاله قبل بررسی شد, کد نویسی با Lambda را آغاز کنیم.
همانطور که در مقاله قبل اشاره شد, میتوان یک متد (تابع) را به یک متغیر تبدیل کرد و آنرا به عنوان behavior به متد های دیگر ارسال کرد. در این تبدیل اجزایی از متد مانند: نام متد, نوع داده ای که بر میگرداند و سطح دسترسی متد حذف میشود. کد زیر یک نمونه کد برای یک متد است:
public void print() { System.out.println("Hello zero to hero"); }
و با مراحل ذکر شده به چنین کدی تبدیل میشود:
methodAsVariable = () -> System.out.println("Hello zero to hero");
اما این کد تنها یک شبه کد است و قابل استفاده در محیط کد نویسی جاوا نیست. زیرا نوع متغیر methodAsVariable مشخص نشده است. برای رفع این مشکل نیاز است تا یک interface ایجاد کنیم و نوع این متغیر را برابر این interface قرار دهیم. این interface باید شامل یک متد با تمام ویژگی های متدی که قرار است با استفاده از lambda expression به یک متغیر تبدیل شود را داشته باشد. این ویژگی شامل نوع داده ای که بر میگرداند، تعداد و نوع متغیر هایی ورودی است. این interface ها را functional interface گویند.
public static void main(String[] args) { LambdaInterface methodAsVariable = () -> System.out.println("Hello zero to hero"); } interface LambdaInterface{ public void myPrinter(); }
توجه داشته باشید که interface نمیتواند بیش از یک متد داشته باشد. زیرا Compiler دچار سردرگمی میشود و نمیتواند تشخیص دهد که از کدام متد باید استفاده کند. نامی که برای متد درون interface انتخاب میشود اهمیتی ندارد و اساس تعریف متد مهم است.
حال میتوان به خوبی این روش را با روش شیگرایی مقایسه کرد. در مقاله قبل چنین کدی برای پیاده سازی چاپ یک پیام نوشته شد:
public class ZeroToHero { public void print(ZeroToHeroInterface messaging) { messaging.message(); } public static void main(String[] args) { ZeroToHero zeroToHero=new ZeroToHero(); ZeroToHeroMessage zeroToHeroMessage = new ZeroToHeroMessage(); zeroToHero.print(zeroToHeroMessage); } interface ZeroToHeroInterface { public void message(); } class ZeroToHeroMessage implements SimpleServlet.ZeroToHeroInterface { @Override public void message() { System.out.println("Hello zero to hero"); } } }
و یا در حالت خلاصه تر و با حذف کلاس پیاده ساز interface به این حالت تبدیل میشود:
public class ZeroToHero { public void print(ZeroToHeroInterface messaging) { messaging.message(); } public static void main(String[] args) { ZeroToHero zeroToHero=new ZeroToHero(); ZeroToHeroInterface zeroToHeroMessage=new ZeroToHeroInterface() { @Override public void message() { System.out.println("Hello zero to hero"); } }; zeroToHero.print(zeroToHeroMessage); } interface ZeroToHeroInterface { public void message(); } }
در اینجا ما تنها به یک متد نیاز داشتیم و مجبور به پیاده سازی یک کلاس شدیم. اما با استفاده از روش lambda میتوان کد را به این صورت خلاصه کرد:
public class ZeroToHero { public void print(ZeroToHeroInterface messaging) { messaging.message(); } public static void main(String[] args) { ZeroToHero zeroToHero=new ZeroToHero(); ZeroToHeroInterface zeroToHeroMessage = () -> System.out.println("Hello zero to hero"); zeroToHero.print(zeroToHeroMessage); } interface ZeroToHeroInterface { public void message(); } }
این مثال به خوبی مزیت functional programming بر شیگرایی را نمایش میدهد. همچنین میتوان کد بالا را خلاصه تر کرد:
public class ZeroToHero { public void print(ZeroToHeroInterface messaging) { messaging.message(); } public static void main(String[] args) { ZeroToHero zeroToHero=new ZeroToHero(); zeroToHero.print(() -> System.out.println("Hello zero to hero")); } interface ZeroToHeroInterface { public void message(); } }
در اینجا بدون نیاز به ایجاد یک متغیر, رفتار متد را به متد print ارجاع دادهایم. Compiler خود تشخیص میدهد که متد print به چه نوع ورودی نیاز دارد و متناسب با آن نوع متغیر lambda را تعیین میکند. میتوان حتی نوع متغیر هایی که به lambda ارسال میشود را حذف کرد :
public class ZeroToHero { public void print(ZeroToHeroInterface messaging,String message) { messaging.message(message); } public static void main(String[] args) { ZeroToHero zeroToHero=new ZeroToHero(); zeroToHero.print((s) -> System.out.println(s), "Zero to Hero"); } interface ZeroToHeroInterface { public void message(String message); } }
یکی از مواردی که در پیاده سازی با استفاده از شی گرایی ممکن است آزار دهنده باشد استفاده از Runnable ها است. در روش شی گرایی نیاز است تا به این صورت یک Thread را پیاده سازی کنید:
Thread oopThread=new Thread(new Runnable() { @Override public void run() { System.out.println("Zero to Hero OOP Thread"); } }); oopThread.run();
اما با استفاده از lambda میتوان دردسر کدنویسی آن را کاهش داد:
Thread lambdaThread = new Thread(() -> System.out.println("Zero to Hero Lambda Thread")); lambdaThread.run();
همانطور که در تعریف functional interface اشاره شد, این interface تنها یک متد میتواند داشته باشد. اما گاهی ممکن است تشخیص این interface ها از باقی interface ها مشکل باشد. برای این منظور از functional interface annotation استفاده میشود. وقتی یک interface این annotation را داشته باشد, اگر متد دومی درون آن تعریف شود, خطایی را برای برنامه نویس نمایش میدهد:
@FunctionalInterface public interface ZeroToHeroInterface { public void message(); }
در مقاله بعدی به تفضیل پیرامون functional interface ها صحبت خواهیم کرد.
سری مقالات آموزش Lambda همچنان ادامه دارد.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.