زبان جاوا در سال ۱۹۹۰ به عنوان یک زبان برنامه نویسی شئ گرا طراحی شد، زمانی که برنامه نویسی شئ گرا روش اصلی مورد استفاده برای تولید نرم افزار محصوب میشد. بسیار قبل از اینکه برنامه نویسی شئ گرا وجود داشته باشد زبان های برنامه نوسی تابعی همچون `List` و `Scheme` وجود داشتند، ولی مزایای استفاده از آنها بیرون از محیط های اکادمیک چندان مورد توجه واقع نشد. در سال های اخیر اهمین استفاده از برنامه نویسی تابعی به دلیل مناسب بودن برای برنامه نویسی همروند و واکنش گرا افزایش پیدا کرده است. این به معنی بد بودن شئ گرایی نیست. در عوض استراتژی پیروزی مخلوط کردن برنامه نویسی شئ گرا و تابعی است، حتی اگر شما علاقه ای به برنامه نویسی همروند نداشته باشید استفاده از این استراتژی منطقی خواهد بود. بعنوان مثال اگر زبان نحو مناسبی برای عبارات تابعی داشته باشد کتابخانه های کالکشن می توانند بسیار قدرتمند تر حالت اولیه خود عمل کنند.

بهبود و گسترش اصلی جاوا ۸ در افزودن ساختار های برنامه نویسی تابعی به بنیان های برنامه نویسی شئ گرا بوده است. در این نوشته نحو پایه ای را معرفی کرده و نحوه استفاده از آن را در برخی زمینه های مهم بیان می کنیم. نکات اصلی عبارتند از :

  • یک عبارت لامدا یک بلوک کد با چند پارامتر است.
  • از عبارات لامدا هنگامی استفاده کنید که می خواهید یک تکه کد بعدا در زمانی مشخص اجرا شود.
  • عبارات لامدا می توانند به اینترفس های تابعی تبدیل شوند.
  • عبارات لامدا می توانند از متغییر های `final` موجود در حوزه تعریف خود استفاده کنند.
  • وجود اشاره گر های متود و سازنده که به متود ها و سازنده ها اشاره می کنند بدون اینکه آنها را فراخوانی کنند.
  • امکان افزودن متود های پیش فرض و استاتیک به اینترفیس های برای ایجاد پیاده سازی منسجم .
  • شما می بایست تداخل ها موجود میان متود های پیش فرض موجود در اینترفیس ها را برطرف کنید. (درصورت وجود تداخل)

چرا لامدا؟

عبارت لامدا یک بلوک کد است که شما می توانید آن را به جاهای مختلف ارسال کنید، پس این کد ها می توانند بعد تنها یکبار یا چندید بار اجرا شوند. پیش از اینکه به نحوه ایجاد عبارات بپردازیم اجازه دهید سفری به گذشته داشته باشیم و ببینیم برای ایجاد این نوع ساختار ها قبلا چگونه می بایست عمل می کردیم. وقتی قرار بود کاری در نخی جداگانه اجرا شود آن را درون متود `run` یک `Runnable` می نوشتید، مانند این:

``` class Worker implements Runnable { public void run() { for (int i = 0; i < 1000; i++) doWork(); } ... } ```

سپس وقتی لازم بود این کد را اجرا کنید، می باییت کی نمونه جدید از کلاس `Worker` می ساختید، آنگاه میتوانید آنرا به مخزن نخ اضافه کنید یا بصورت ساده آن را به عنوان یک نخ اجرا کنید:

``` Worker w = new Worker(); new Thread(w).start(); ```

نکته اصلی اینجاست که متود `run` می بایستی حاوی کدی باشد که می خواهید درون نخی دیگر اجرا شود.

فرض کنید می خواهید رشته هایی را با استفاده از مقایسه کننده مرتب کنید. اگر بخواهید رشته ها را بر حسب طول آنها و نه برحسب ترتیب الفبایی پیش فرض آنها مرتب کنید، در این صورت می توانید شئی از کلاسComparator را به متود sort ارسال کنید.

class LengthComparator implements Comparator<String> {
     public int compare(String first, String second) {
        return Integer.compare(first.length(), second.length());
     }
  }
  
Arrays.sort(strings, new LengthComparator());

متود `sort` متود `compare` را بصورت متداوم اجرا میکند و در صورتی که المنتی خارج از ترتیب باشد آن را جابجا می کند تا زمانی که کل آرایه مرتب شود. شما تکه کدی به متود `sort` ارسال می کنید که کار مقایسه را انجام می دهد و کد شما با باقی منطق مرتب سازی ادغام می شود. (چیزی که شاید شما اهمیتی به پیاده سازی دوباره آن نمی دهید.) توجه داشته باشید که فراخوانی تابع `Integer.compare(x, y)` مقدار صفر برمی گرداند اگر `x=y` مقداری منفی اگر `x < y` و مقداری مثبت اگر `x > y` باشد. این تابع استاتیک در جاوا ۷ اضافه شده است. شما نباید مقدار `x - y` را برای مقایسه `x` و `y` حساب کنید زیرا این عملیات برای عملوند های بزرگ غیر هم علامت ممکن است با سرریز مواجه شود.

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

``` button.setOnAction(new EventHandler() { public void handle(ActionEvent event) { System.out.println("Thanks for clicking!"); } }); ```

چیزی که در اینجا مهم است تک کدی است که درون متود `handle` نوشته می شود، این همان کدی است که هنگام فشرده شدن دکمه اجرا می شود.

از آنجایی که پس از جاوا 8 `JavaFX` به عنوان جانشین `Swing` جهت ایجاد واسط کاربری قرار گرفته است، در اینجا مثال های با استفاده از `JavaFX` نوشته شده اند. جزئیات در اینجا حائز اهمیت نیست چیزی که مهم است این که در تمامی رابط های کاربری خواه `Swing` باشد، خواه `JavaFX` یا اندروید شما کدی به دکمه می دهید تا آن هنگام فشرده شدن دکمه اجرا شود. همانطور که مشاهه می کنید در هر سه نمونه ذکر شده در بالا تکه کدی جهت اجرا به مکانی ارسال میشود، به مخزن نخ، به متود `sort` یا به یک دکمه. کد ارسالی در زمانی در آینده اجرا خواهد شد.

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

در زبان های دیگر امکان ارسال مستقیم بلوک های کد وجود دارد. طراحان جاوا مدت طولانی ای در مقابل اضافه کردن این ویژگی مقابله می کردند. پس از همه توانمندی بزرگ جاوا در در سادگی و انسجام و استحام آن است. یک زبان می تواند بطور غیر قابل تحملی شلوغ شود اگر شامل تمامی راهها برای دستیابی به کدی کوتاه تر باشد. اگر چه در دیگر زبان ها تنها سادگی در ایجاد نخ جدید یا ثبت واکنش برای دکمه خاص نیست، بخش های بسیاری از `API` آنها ساده تر، سازگارتر و قدرتمند تر است. جاوا میتواند `API` های مشابهی را ایجاد کند که شئ هایی از کلاس هایی دارای توابع خاص را بپذیرند ولی اینگونه `API` ها چندان خوشایند نخواهند بود.

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

نحو عبارات لامدا

مثال های قبل را بیاد بیاورید، برای تشخصی کوچک یا بزرگ بودن یک رشته از نظر طول با دیگری کد زیر را می بایست ارسال کنیم:

``` Integer.compare(first.length(), second.length()) ```

متغییر های `first` و `second` چه هستند؟ در جاوا تمامی متغییر ها باید نوعی ثابت داشته باشند، پس میبایست نوع متغییر ها هم مشخص شود:

``` (String first, String second) -> Integer.compare(first.length(), second.length()) ```

این اولین فرم لامدا ایست که شما دیده اید، عبارات لامدا شبیه این تنها بلوکی از کدها هستند که شما می توانید متغییر های ورودی آن را مشخص کنید.

چرا این نام؟ سال هاپیش، قبل از اینکه کامپیوتر های بسیاری وجود داشته باشد، آلونزو کرانچ منطقدان تلاش های برای فرمالیزه کردن توابع ریاضی انجام تا بصورت موثرتری قابل محاسبه باشند. (توابعی که همه از وجود آنها اطلاع داشند ولی کسی از نحوه محاسبه آنها با آگاهی نداشت) وی از حرف لامدا `(λ)` برای مشخص کردم پارامتر ها استفاده کرد. اگر آلونزو از `API` جاوا آگاهی داشت اینگونه می نوشت:

``` λfirst.λsecond.Integer.compare(first.length(), second.length()) ```

چرا حرف لامدا `(λ)` ؟ در حقیقت کتاب مباد ریاضیات از نماد `^` برای نمایش متغییر های آزاد استفاده کرده بود که با الهام از این کتاب کرانچ حرف لامدای بزرگ را برای نمایش پارامتر ها مورد استفاده قرار داد. ولی در انتها وی از حرف لاندای کوچک بجای لاندای بزرگ استفاده کرد. از آن زمان بعد به عباراتی که با متغییر هایی به عنوان پارامتر نوشته می شوند نام “عبارات لامدا” اطلاق می شود.

تا کنون تنها یک حالت از عبارات لامدا در جاوا را مشاهده کرده اید: `(پارامتر ها) (فلش ->) (یک عبارات)`. اگر کد مورد نظر چیزی فراتر یک مقایسه ساده بوده و تنها در یک عبارت نگنجد، عبارت لامدا را دقیقا شبیه به حالت قبل بنویسید و کد خود را درون آکولاد `{}` محصور کنید و مقدار بازگشتی با واژه `return` مشخص کنید. بعنوان مثال:

``` (String first, String second) -> { if (first.length() < second.length()) return -1; else if (first.length() > second.length()) return 1; else return 0; } ```

اگر عبارت لامدا پارامتر ورودی نداشت تنها پارانتز های مخصوص پارامتر را خالی بگذارید، دقیقا همانند متود های بدون پارامتر:

``` () -> { for (int i = 0; i < 1000; i++) doWork(); } ```

اگر نوع پارامتر های عبارت لامدا از کد نوشته شده قابل استنباط باشد میتوان از نوشتن صریح نوع آنها چشم پوشی کنید.

``` Comparator comp = (first, second) // Same as (String first, String second) -> Integer.compare(first.length(), second.length()); ```

در اینجا کامپایلر براحتی میتواند تشخیص دهد که نوع ورودی های عبارت لامدا یعنی `first` و`second` از کلاس `String` است، زیرا این عبارت به متغییری از نوع `Comparator` رشته اختصاص داده میشود. (پیشتر نگاهی نزدیکتری به تخصیص ها خواهیم داشت) اگر عبارت لامدا مورد نظر شما تنها دارای یک پارمتر ورودی باشد میتوانید حتی پرانتز های مخشص کننده ورودی ها را نیز جا بیاندازید.

``` EventHandler listener = event -> System.out.println("Thanks for clicking!"); // Instead of (event) -> or (ActionEvent event) -> ```

همچینین میتواند `annotation` ها و `final mdifier` را به پارامترهای عبارات لامدا اضافه کنید همانند آنچه در پارامتر متود ها مورد استفاده قرار میگرفت.

``` (final String name) -> ... (@NonNull String name) -> ... ```

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

``` (String first, String second) -> Integer.compare(first.length(), second.length()) ```

میتواند در مواردی استفاده شود که مقدار مورد انتظار از نوع `int` باشد.

توجه داشته باشید که این مقدار بازگشتی را در تمامی پرش های کد درون بلاک مشخص کنید، در غیر این صورت عبارت لامدای شما نامعتبر خواهد بود، برای مثال (int x) -> { if (x >= 0) return 1; } عبارتی نا معتبر است.