diff --git a/docs/java-features/jshell.md b/docs/java-features/jshell.md index e3b012e..9328191 100644 --- a/docs/java-features/jshell.md +++ b/docs/java-features/jshell.md @@ -3,3 +3,267 @@ sidebar_position: 1 --- # ابزار Jshell + +### مقدمه + +برنامه‌نویسانی که تازه می‌خواهند به محیط جاوا ملحق شوند، برای یاد گرفتن امکانات و محدودیت‌های زبان باید بارها آزمون و خطا کنند. مراحل این آزمون و خطا در زبان جاوا معمولا شامل مراحل زیر است: + +- نوشتن یک کد جدید +- ذخیره کردن آن +- کامپایل با کمک کامپایلر جاوا +- اجرا با کمک ماشین مجازی جاوا + +همچنین کدی که نوشته می‌شود باید دارای شرایط خاصی باشد. مثلا حتما باید `public static void main` نوشته شده باشد و نتیجه‌ی هر عبارت نیز داخل خروجی استاندارد چاپ شود تا بتوان نتیجه را دید. + +برای نمونه برای تست کردن یک جمع ساده باید چندین خط کد بنویسیم و کامپایل و اجرا کنیم: + +```java +import java.util.*; +Class Main { + public static void main(String[] args) { + int a = 1; + System.out.print(a); + int b = a + 1 ; + System.out.print(b); + } +} + +``` + + + +البته این روند تکرارشونده فقط مربوط به یادگیری یک زبان برنامه‌نویسی جدید نیست، بلکه در [برنامه‌نویسی اکتشافی یا همان Exploratory programming](https://en.wikipedia.org/wiki/Exploratory_programming) نیز اتفاق می‌افتد. برنامه نویسی اکتشافی زمانی‌است که تعریف دقیقی از کاری که قرار است انجام دهیم نداریم یا با کتاب‌خانه یا دامنه‌ی مساله به خوبی آشنا نیستیم، بنابراین دوست داریم به شکل تعاملی برنامه بنویسیم و بازخورد بگیرید و برنامه خود را عوض کنیم. + + + +### معرفی اولیه + +یکی از امکانات جدید جاوا که در جاوای نسخه‌ی ۹ اضافه شده‌است، قابلیت JShell است. این قابلیت که با کد [JEP 222](https://openjdk.org/jeps/222) شناخته می‌شود، یک پوسته‌ی نرم‌افزاری (shell) برای زبان جاوا است. برنامه‌نویسان پایتون و لیسپ (و بسیاری از زبان‌های دیگر) از قدیم با پوسته‌های‌ زبان‌های برنامه‌نویسی آشنا هستند. پوسته به برنامه‌نویس کمک می‌کند بدون اینکه یک برنامه کامل را بنویسد و سپس اجرا کند، در یک محیط تعاملی برنامه‌ی خود را خط به خط نوشته و کدها همانجا اجرا شوند. + +به این نوع از پوسته‌ها، پوسته‌های «حلقه‌ی خواندن - محاسبه - چاپ» نیز می‌گویند، یعنی پوسته‌ی نرم‌افزاری یک دستور از ورودی می‌خواند، سپس آن را اجرا می‌کند و نتیجه‌ی عبارت را چاپ می‌کند. در نهایت مجددا منتظر ورودی برنامه‌نویس می‌ماند تا یک خط دیگر وارد کند. در انگلیسی به آن REPL می‌گویند که مخفف read-evaluate-print loop است. + +اولین پوسته‌ی نرم‌افزاری مربوط به زبان لیسپ بوده که در سال ۱۹۶۴ اختراع گردیده است. پس از آن داشتن پوسته بین زبان‌های برنامه‌نویسی مفسری رایج شده است مثلا پایتون و جاوااسکریپت و روبی دارای پوسته هستند. + +یکی از دلایلی که این امکان کمتر بین زبان‌های قابل کامپایل رایج است، این است که یکی از مراحل این حلقه، مرحله‌ی eval است. این تابع که معمولا در زبان‌های برنامه‌نویسی مفسری وجود دارد یک رشته به عنوان آرگومان می‌گیرد و به عنوان کد همان زبان اجرا می‌کند. برای مثال در [پایتون](https://pythongeeks.org/python-eval-function/) یا [جاوااسکریپت](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) + +اما در زبان‌های کامپایلری، این شیوه باید کمی تغییر کند اما غیرممکن نیست. همانطور که مشاهده می‌کنید جاوا و اسکالا نیز به این قابلیت مجهز هستند. + + + +### اولین دیدار با JShell + +ابزار JShell به همراه JDK در سیستم شما نصب خواهد بود. برای اجرای آن کافی است دستور `jshell` را در محیط متنی سیستم‌عامل خود اجرا کنید. در صورتی که جاوای نسخه‌ی ۹ و یا بالاتر به شکل صحیح در سیستم شما نصب شده باشد این دستور با موفقیت اجرا می‌شود و محیط تعاملی آن را مشاهده می‌کنید. + +محیط اجرا چیزی شبیه به زیر خواهد بود: + +```java +| Welcome to JShell -- Version 18.0.2 +| For an introduction type: /help intro + +jshell> System.out.println("Hello Javacup") +Hello Javacup + +jshell> +``` + +همانطور که می‌بینید لزومی به نوشتن مقدمات همیشگی مثل تعریف کلاس یا تابع `main` نیست و به شکل مختصری می‌توان شروع به نوشتن برنامه کرد. حتی در اینجا نیازی به گذاشتن نقطه‌ویرگول هم نیست. (البته در ساختارهای پیچیده‌تر مثل تابع لازم است.) + + + +### قابلیت‌های اولیه + +کارهای اولیه‌ای که می‌توان انجام داد، تعریف متغیر، اجرای دستورات ساده، تعریف تابع و کلاس و خروج است. + +```java +jshell> int a = 1 +a ==> 1 + +jshell> a +a ==> 1 + +jshell> a+1 +$4 ==> 2 +``` + +در این مثال یک تعریف و مقدردهی متغیر را می‌بینیم. همچنین با نوشتن یک عبارت می‌توان نتیجه‌ی آن را بدون چاپ کردن مشاهده کرد. + +```java +jshell> int f(int a){ + ...> return a+5; + ...> } +| created method f(int) + +jshell> f(3) +$6 ==> 8 +``` + + + +برای ساختار‌های پیچیده‌تر مثل تابع امکان نوشتن دستورمان در چند خط را نیز داریم. در این مثال پس از خط اول JShell متوجه شد که ساختار وارد شده کامل نیست، بنابراین امکان نوشتن خطوط بعدی را به ما داد. در نهایت با بستن کروشه متوجه شد که ساختار کامل شده و یک تابع در نظر گرفت. + +```java +jshell> class Student { + ...> int age; + ...> String name; + ...> public Student(String name, int age){ + ...> this.age = age; + ...> this.name = name; + ...> } + ...> } +| created class Student + +jshell> new Student("Ali Alavi", 20); +$8 ==> Student@1b604f19 +``` + +تعریف کلاس نیز تفاوت خاصی ندارد و می‌توان به راحتی انجام داد. + + + +```java +jshell> void f(){ return 1; } +| Error: +| incompatible types: unexpected return value +| void f(){ return 1; } +| ^ +``` + +در صورتی که تکه کد نوشته‌شده‌ی ما خطای زمان کامپایل داشته باشد در همان موقع به ما گفته می‌شود. برای درست کردن آن می‌توان از تاریخچه‌ی دستورات کمک گرفت. با کمک کلید‌های بالا و پایین روی کیبورد می‌توان بین دستورات قبلی جا به جا شد. + + + +برای خروج می‌توان از کلید‌های ctrl+D کمک گرفت و یا دستور `exit/` را تایپ کرد. + +```java +jshell> /exit +| Goodbye +``` + + + +### دستورات کنترلی + +اکثر دستوراتی که تا الان دیدیم همه در تعریف زبان جاوا می‌گنجیدند و می‌توان آن‌ها را با پیش‌نیاز‌های لازم در یک سورس‌کد جاوا نیز نوشت. برای مثال با داشتن یک کلاس و یک تابع `main` می‌توان `int a = 1` را کامپایل کرد. هدف JShell نیز ارائه‌ی یک زبان جدید نیست. + +اما آخرین دستوری که دیدیم یا همان `exit/` در جاوا وجود ندارد، بلکه یکی از دستورات کنترلی مختص کنترل کردن رفتار این نرم‌افزار است. این دسته از دستورات با `/` شروع می‌شوند. + +در زیر لیست مختصری از این دستورات را می‌بینیم: + +- `help/`: راهنمای دستورات کنترلی دیگر. برای راهنمایی دقیق‌تر یک دستور می‌توانید از `help list/` استفاده کنید. +- `list/`: لیستی از دستورات (تاریخچه)‌ی تایپ‌شده. +- ‍`drop/`: حذف کردن یک مورد از بین متغیرها و توابع و تایپ‌های موجود. مثلا یک تابع تعریف‌شده به این شکل حذف می‌شود. +- `vars/`: لیستی از متغیرهای تعریف شده تا اینجای برنامه به همراه مقدارشان. +- `methods/`: لیستی از توابع تعریف‌شده تا کنون. +- `types/`: لیستی از تایپ‌های جدید (کلاس‌ها و اینترفیس‌ها) که تا کنون تعریف شده‌اند. + + + +### ذخیره و بارگذاری اسکریپت‌ها + +زمانی که یک‌سری دستور وارد کرده‌ایم، می‌توان این مرحله‌ی فعلی را با کمک دستور `save/` ذخیره کرد. مثلا نوشت: `save ./state.jsh/`. در این صورت یک فایل متنی از نوع java shell ساخته می‌شود که شامل همه دستوراتی است که تا الان اجرا شده‌اند (البته به جز متغیرها و توابع و کلاس‌های حذف شده با دستور `drop/`) + +در کنار این دستور، می‌توان یک فایل `jsh` را با jshell باز کرد. راه اول این است که موقع باز کردن jshell به آن آرگومان دهیم مثلا بنویسیم: + +```bash +jshell ./state.jsh +``` + +و یا اینکه بعد از باز شدن با دستور `open/` فایل مورد نظر را باز کنیم. + +همچنین می‌توانیم یک فایل javaی معمولی را داخل jshell لود کنیم، مثلا یک کلاس که می‌خواهیم با متد‌های آن به شکل تعاملی کار کنیم و بیشتر آشنا شویم. + +یکسری اسکریپت پیش‌فرض هم وجود دارد که می‌تواند لود کردن آن‌ها کمک‌کننده باشد. + +- PRINTING: در این اسکریپت شکل کوتاه‌تر توابع چاپ نوشته شده است و با این وجود می‌توان به جای `System.out.println` نوشت `println` که برای محیط تعاملی ساده‌تر و جذاب تر است. همین موضوع برای `print` و `printf` نیز صادق است. +- JAVASE: با اجرای این اسکریپت، تمام کلاس‌های موجود در java SE لود می‌شوند و می‌توان از آن‌ها استفاده کرد. +- DEFAULT: در حالت پیش‌فرض این اسکریپت اجرا می‌شود که چند `import` مناسب و کاربردی دارد. مثلا `ArrayList` از `java.util‍` یا `Socket` از `java.net` به شکل پیش‌فرض در دسترس هستند. + + + +### تاریخچه + +چند راه برای استفاده از تاریخچه وجود دارد: + +- با دستور `history/` می‌توان دستوراتی که تا اینجا زدیم (هم دستورات جاوایی و هم کنترلی) را مشاهده کرد. +- با زدن کلید‌های بالا و پایین روی کیبورد، دستورات گذشته را مشاهده و ادیت کرد. +- با زدن کلید‌های ctrl+r در تاریخچه جست‌وجو کرد. +- هر دستور دارای یک id یکتا است. می‌توان با داشتن آن، دستور را بارگذاری کرد. (با مراجعه به `list/` می‌توان id دستورات را دید) +- داشتن id معمولا راهکاری عملی نیست، اما می‌توان به شکل نسبی به دستور مورد نظر رسید. مثلا با ``1-/` به دستور آخر آخر رسید. + + + +### نکات حرفه‌ای تر + +- می‌دانیم این ابزار متوجه می‌شویم که هر عبارتی را که حساب کنیم چاپ می‌کند، برای چاپ کردن نیز باید متد `toString` آن را محاسبه کرد. +- مثل ابزارهای توسعه‌ی نرم‌افزار با کمک tab می‌توان نام متد یا متغیر را کامل کرد. البته این امکان بسیار ساده‌تر از نرم‌افزارهای توسعه‌ی نرم‌افزار است. +- چند کلید میانبر وجود دارد که می‌توانید آن‌ها را از `help shortcuts/` مطالعه کنید. برای مثال می‌توان با یک میانبر، عبارت فعلی را داخل یک متغیر ریخت. +- یک‌سری حالت بازخورددهی وجود دارد که می‌توان آن‌ها را تنظیم کرد که بیشتر از حد معمولا یا کمتر از حد معمولا بازخورد دهد. مثلا: + + + +```java +jshell> /set feedback verbose +| Feedback mode: verbose +​ +jshell> void f(int a){ System.out.println(a);} +| created method f(int) +​ +jshell> f(1); +1 + +jshell> void f(int a){ System.out.println(a+1);} +| modified method f(int) +| update overwrote method f(int) + +jshell> /set feedback concise +jshell> int a = 2; +jshell> a++ +$8 ==> 2 +jshell> a +a ==> 3 +``` + + + + + +در این حالت خروجی بسیار مختصرتر خواهد بود و فقط نتیجه‌ی اجرای دستوراتی که مقدار برگشتی دارند چاپ می‌شود. + +- در سیستم‌عامل های یونیکسی با دستور `man jshell` می‌توانید توضیحات مفیدی درمورد این ابزار بخوانید در همه‌ی سیستم‌عامل‌ها نیز می‌توانید [نسخه‌ی آنلاین](https://docs.oracle.com/en/java/javase/14/docs/specs/man/jshell.html) را بخوانید. +- با تعریف دوباره‌ی یک متغیر می‌توانید تایپ آن را عوض کنید اما در صورتی که آن را مجدد تعریف نکنید نمی‌توانید مقداری با تایپ متفاوت در آن بریزید. + + +```java +jshell> int a = 1; +a ==> 1 +| created variable a : int + +jshell> a = "javacup" +| Error: +| incompatible types: java.lang.String cannot be converted to int +| a = "javacup" +| ^-------^ + +jshell> String a = "javacup" +a ==> "javacup" +| replaced variable a : String +| update overwrote variable a : int +``` + + + +- به شکل پیش‌فرض امکان استفاده از ورودی استاندارد مثلا با کلاس `Scanner` فراهم نیست چون اجرای کدها در یک ماشین مجازی دیگر انجام می‌شود اما با کمک آرگومان `--execution local` این مشکل حل می‌شود. + +```java +jshell --execution local Hello.java +``` + + + +## منابع + +- تاریخچه‌ی REPL در ویکیپدیا: [+](https://en.wikipedia.org/wiki/Read–eval–print_loop) +- صفحه‌ی JEP 222: [+](https://openjdk.org/jeps/222) + +- یک آموزش خوب:‌[+](https://cr.openjdk.java.net/~rfield/tutorial/JShellTutorial.html) +- یک آموزش خوب دیگر [+](https://dev.java/learn/jshell-tool/)