From 225034164c5d0b4975dba98d5931996752eebe75 Mon Sep 17 00:00:00 2001 From: Roozbeh Sharifnasab Date: Wed, 17 Aug 2022 10:39:24 +0430 Subject: [PATCH 1/3] feat: add records --- docs/java-features/records.md | 227 ++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/docs/java-features/records.md b/docs/java-features/records.md index 64c3ac9..a06fbe5 100644 --- a/docs/java-features/records.md +++ b/docs/java-features/records.md @@ -3,3 +3,230 @@ sidebar_position: 6 --- # رکوردها (Records) + + + +- [مقدمه](https://docs.asta.ir/display/JavaCup/Records#Records-مقدمه) +- [نحوه استفاده](https://docs.asta.ir/display/JavaCup/Records#Records-نحوهاستفاده) +- [قواعد](https://docs.asta.ir/display/JavaCup/Records#Records-قواعد) + - [Reflection API](https://docs.asta.ir/display/JavaCup/Records#Records-ReflectionAPI) + + + + + +## مقدمه + +گاهی لازم می‌شود کلاس‌هایی داشته باشیم که فقط حامل داده هستند و قرار نیست رفتاری داشته باشند. مثلا کلاس‌هایی که برای پارامترهای وروردی کوئری‌های دیتابیسی استفاده می‌کنیم. به جای نوشتن ایجاد یک کلاس به روش معمول و نوشتن فیلدها و سازنده و ... می‌توانیم از این پس از کلاس‌های Record استفاده کنیم. + +کلاس‌های Record، نوع جدیدی از کلاس‌ها در زبان جاوا هستند. این کلاس‌ها، کلاس‌های حامل داده‌های تغییرناپذیر هستند و انتقال داده بین کلاس‌ها و ماژول‌های مختلف را تسهیل می‌کنند. + +قبل از جاوا ۱۶ و بدون استفاده از کلاس رکورد، کلاس Person را برای نگهداری داده‌‌های تغییر ناپدیر به شکل زیر تعریف می‌شود. + +```java +public class Person { + private final String name; + private final String gender; + private final int age; + + public Person(String name, String gender, int age) { + this.name = name; + this.gender = gender; + this.age = age; + } + + public String getName() { + return name; + } + + public String getGender() { + return gender; + } + + public int getAge() { + return age; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Person person = (Person) o; + return age == person.age && + Objects.equals(name, person.name) && + Objects.equals(gender, person.gender); + } + + @Override + public int hashCode() { + return Objects.hash(name, gender, age); + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", gender='" + gender + '\'' + + ", age=" + age + + '}'; + } +} +``` + + + +اما با کمک ویژگی جدید Record، می‌توان Person را در یک خط و به راحتی تعریف کرد: + +```java +public record Person(String name, String gender, int age) {} +``` + + + +## نحوه استفاده + +```java +RecordDeclaration: +{ClassModifier} `record` TypeIdentifier [TypeParameters] RecordHeader [SuperInterfaces] RecordBody +``` + + + +با تعریف یک رکورد به شکل بالا، موارد زیر به صورت خودکار توسط کامپایلر ایجاد می‌شود: + +- به ازای هر کامپوننتی که به صورت پارامتر در تعریف رکورد ذکر شده، این دو مورد ایجاد می‌شود: + - یک فیلد private final با همان اسم و نوع + - یک متد public برای دسترسی به مقدار آن کامپوننت. نام متد برابر با نام کامپوننت است. در مثال بالا، دو متد ()name و ()address را خواهیم داشت. +- یک سازنده public که امضایش دقیقا مشابه هدر رکورد است. +- پیاده‌سازی متدهای equals و hashCode به این صورت که اگر دو کلاس رکورد با نوع یکسان و مقادیر یکسان برای فیلدهایشان داشته باشیم، آن دو رکورد با هم برابر هستند. +- پیاده‌سازی متد toString به طوری که نشان‌دهنده همه فیلدهای رکورد به همراه مقادیرشان است. + +## قواعد + +هنگام تعریف و استفاده از کلاس رکورد باید در نظر داشت این کلاس‌ها در مقایسه با یک کلاس عادی، ملاحظات و تفاوت‌هایی دارند که باید در نظر گرفته شود. + +- کلاس رکورد از هیچ کلاسی نمی‌تواند ارث‌بری کند و عبارت extends نمی‌تواند در تعریف رکورد ظاهر شود. +- کلاس رکورد به صورت ضمنی final است و نمی‌تواند abstract باشد. +- همانطور که قبلا هم گفته شد، فیلدهای رکورد، final و تغییرناپذیر هستند. +- داخل کلاس رکورد نمی‌توان فیلد یا بلاک‌های مقداردهی غیراستاتیک تعریف کرد. + +```java +record Rectangle(double length, double width) { + + // Field declarations must be static: + BiFunction diagonal; + + // Instance initializers are not allowed in records: + { + diagonal = (x, y) -> Math.sqrt(x*x + y*y); + } +} +``` + +اما فیلد، متد و بلاک مقداردهی استاتیک می‌توان تعریف کرد و از این نظر هیچ فرقی با کلاس عادی ندارد. + +```java +record Rectangle(double length, double width) { + + // Static field + static double goldenRatio; + + // Static initializer + static { + goldenRatio = (1 + Math.sqrt(5)) / 2; + } + + // Static method + public static Rectangle createGoldenRectangle(double width) { + return new Rectangle(width, width * goldenRatio); + } +} +``` + +- اگر متدهای accessor را بخواهیم داخل رکورد پیاده‌سازی کنیم، باید حتما دقت کنیم که نوع بازگشتی متد مطابق با نوع فیلد در هدر رکورد باشد و identifier هم public باشد. + + + +```java +public record Rectangle(int width, int length) { + private int width() { + return 4; + } + // error: invalid accessor method in record Rectangle + // private int width() { + // ^ + // (accessor method must be public) + + public double width() { + return 4.0; + } + // error: invalid accessor method in record Rectangle + // public double width() { + // ^ + //(return type of accessor method width() must match the type of record component width) +} +``` + +در مورد بازنویسی متدهای hashCode و equals نیز باید این ملاحظات را در نظر گرفت. + + + +علاوه بر محدودیت‌هایی که در بالا ذکر شد، کلاس ریکورد مانند یک کلاس عادی کار می‌کند: + +۱- نمونه‌های کلاس رکورد با استفاده از کلیدواژه new ساخته می‌شوند. + +۲- کلاس رکورد می‌تواند به شکل سطح بالا (‌‌top level) یا به شکل تودرتو تعریف شود و حتی می‌تواند جنریک باشد. + +۳- یک کلاس رکورد می‌تواند متدهای استاتیک، فیلدها و مقداردهی‌های اولیه داشته باشد. + +۴- در کلاس رکورد می‌توان روی متدهای غیراستاتیک نیز تعریف کرد. + +5- کلاس رکورد می‌تواند یک یا چند رابط (interface) را پیاده‌سازی کند. اما یک کلاس رکورد نمی‌تواند یک از یک کلاس ارث‌بری کند چون دارای state می‌شود و فراتر از چیزی است که در بالا توضیح داده شده است. مانند باقی کلاس‌‌ها، می‌توان از یک اینترفیس برای توصیف رفتار چند رکورد استفاده کرد. این رفتار ممکن است مستقل از دامنه‌ی مساله (مثل واسط Compareable) باشند یا مختص دامنه (*domain-specific*) باشد که در این صورت رکوردها می‌توانند بخشی از سلسله مراتب **sealed** باشند. + +6- یک کلاس رکورد می‌تواند تایپ های تودرتو از جمله رکورد های تودرتو را اعلان کند. اگر یک کلاس رکورد خودش تودرتو باشد، به طور ضمنی استاتیک است. به این شکل از به وجود آمدن state قابل تغییر جلوگیری می‌شود. + +7-می‌توان برای کلاس‌های رکورد یا اجزای آن ممکن است حاشیه‌‌نویسی (annotation) قرار داد. + +8- نمونه‌های کلاس رکورد می‌توانند سریالایز و یا دی‌سریالایز شوند. با این حال، این پروسه با استفاده از متدهای writeObject, readObject, readObjectNoData, writeExternal, readExternal، نمی‌تواند شخصی‌سازی شود. مولفه‌های یک کلاس رکورد، serialization را کنترل می‌کنند‌، درحالی که سازنده‌های متعارف یک کلاس رکورد deserialization را کنترل می‌کنند. + + + + + +### **Reflection API** + +دو متد پابلیک به java.lang.Class اضافه شده است: + +```java + RecordComponent[] getRecordComponents() : +``` + + + + + +یک آرایه از شی های java.lang.reflect.RecordComponent برمی‌گرداند. عناصر این آرایه به همان ترتیبی که در تعریف رکورد آمده‌اند، می‌آیند. اطلاعات اضافی مثل نام، حاشیه‌نویسی و توابع دسترسی (accessor) را می‌توان از هر عنصر آرایه استخراج کرد. + +```java + boolean isRecord(): +``` + + + +این متد اگر کلاس داده شده از نوع رکورد باشد، true برمیگرداند.(چیزی شبیه به isEnum) + + + + + +# منابع + +https://openjdk.java.net/jeps/395 + +https://docs.oracle.com/en/java/javase/17/language/records.html + +https://weakreference.medium.com/java-16-records-f16c2ecb4b05 + +https://www.logicbig.com/tutorials/core-java-tutorial/java-16-changes/intro-to-java-records.html From 3dd4256bccdec8f225763d4c8a56af7f2eb9d8f8 Mon Sep 17 00:00:00 2001 From: Roozbeh Sharifnasab Date: Sat, 22 Oct 2022 19:04:29 +0330 Subject: [PATCH 2/3] Update records.md --- docs/java-features/records.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/java-features/records.md b/docs/java-features/records.md index a06fbe5..5f6665c 100644 --- a/docs/java-features/records.md +++ b/docs/java-features/records.md @@ -5,19 +5,17 @@ sidebar_position: 6 # رکوردها (Records) - - [مقدمه](https://docs.asta.ir/display/JavaCup/Records#Records-مقدمه) - [نحوه استفاده](https://docs.asta.ir/display/JavaCup/Records#Records-نحوهاستفاده) - [قواعد](https://docs.asta.ir/display/JavaCup/Records#Records-قواعد) - - [Reflection API](https://docs.asta.ir/display/JavaCup/Records#Records-ReflectionAPI) - +- [Reflection API](https://docs.asta.ir/display/JavaCup/Records#Records-ReflectionAPI) ## مقدمه -گاهی لازم می‌شود کلاس‌هایی داشته باشیم که فقط حامل داده هستند و قرار نیست رفتاری داشته باشند. مثلا کلاس‌هایی که برای پارامترهای وروردی کوئری‌های دیتابیسی استفاده می‌کنیم. به جای نوشتن ایجاد یک کلاس به روش معمول و نوشتن فیلدها و سازنده و ... می‌توانیم از این پس از کلاس‌های Record استفاده کنیم. +گاهی لازم می‌شود کلاس‌هایی داشته باشیم که فقط حامل داده هستند و قرار نیست رفتاری داشته باشند. مثلا کلاس‌هایی که برای پارامترهای وروردی کوئری‌های دیتابیسی استفاده می‌کنیم. به جای نوشتن و ایجاد یک کلاس به روش معمول و نوشتن فیلدها و سازنده و ... می‌توانیم از این پس از کلاس‌های Record استفاده کنیم. کلاس‌های Record، نوع جدیدی از کلاس‌ها در زبان جاوا هستند. این کلاس‌ها، کلاس‌های حامل داده‌های تغییرناپذیر هستند و انتقال داده بین کلاس‌ها و ماژول‌های مختلف را تسهیل می‌کنند. From a44033e2255b77864ffc57f5c557253112e1c73f Mon Sep 17 00:00:00 2001 From: MHZ Date: Mon, 24 Oct 2022 16:55:43 +0330 Subject: [PATCH 3/3] Update records.md --- docs/java-features/records.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/java-features/records.md b/docs/java-features/records.md index 5f6665c..1b8a262 100644 --- a/docs/java-features/records.md +++ b/docs/java-features/records.md @@ -15,11 +15,11 @@ sidebar_position: 6 ## مقدمه -گاهی لازم می‌شود کلاس‌هایی داشته باشیم که فقط حامل داده هستند و قرار نیست رفتاری داشته باشند. مثلا کلاس‌هایی که برای پارامترهای وروردی کوئری‌های دیتابیسی استفاده می‌کنیم. به جای نوشتن و ایجاد یک کلاس به روش معمول و نوشتن فیلدها و سازنده و ... می‌توانیم از این پس از کلاس‌های Record استفاده کنیم. +گاهی لازم می‌شود کلاس‌هایی داشته باشیم که فقط حامل داده هستند و قرار نیست رفتاری داشته باشند. مثلا کلاس‌هایی که برای پارامترهای وروردیِ کوئری‌های دیتابیسی استفاده می‌کنیم. به جای نوشتن و ایجاد یک کلاس به روش معمول و نوشتن فیلدها و سازنده و ... می‌توانیم از این پس از کلاس‌های Record استفاده کنیم. -کلاس‌های Record، نوع جدیدی از کلاس‌ها در زبان جاوا هستند. این کلاس‌ها، کلاس‌های حامل داده‌های تغییرناپذیر هستند و انتقال داده بین کلاس‌ها و ماژول‌های مختلف را تسهیل می‌کنند. +کلاس‌های Record، نوع جدیدی از کلاس‌ها در زبان جاوا هستند. این کلاس‌ها، حامل داده‌های تغییرناپذیر بوده و انتقال داده بین کلاس‌ها و ماژول‌های مختلف را تسهیل می‌کنند. -قبل از جاوا ۱۶ و بدون استفاده از کلاس رکورد، کلاس Person را برای نگهداری داده‌‌های تغییر ناپدیر به شکل زیر تعریف می‌شود. +در نسخه‌های قبل از جاوا ۱۶ و بدون استفاده از کلاس رکورد، کلاس Person برای نگهداری داده‌‌های تغییرناپدیر به شکل زیر تعریف می‌شود: ```java public class Person { @@ -75,7 +75,7 @@ public class Person { -اما با کمک ویژگی جدید Record، می‌توان Person را در یک خط و به راحتی تعریف کرد: +اما با کمک ویژگی جدید Record، می‌توان Person را به راحتی و در یک خط به شکل زیر تعریف کرد: ```java public record Person(String name, String gender, int age) {} @@ -123,7 +123,7 @@ record Rectangle(double length, double width) { } ``` -اما فیلد، متد و بلاک مقداردهی استاتیک می‌توان تعریف کرد و از این نظر هیچ فرقی با کلاس عادی ندارد. +البته فیلد، متد و بلاک مقداردهی استاتیک می‌توان تعریف کرد و از این نظر هیچ فرقی با کلاس عادی ندارد. ```java record Rectangle(double length, double width) { @@ -171,23 +171,23 @@ public record Rectangle(int width, int length) { -علاوه بر محدودیت‌هایی که در بالا ذکر شد، کلاس ریکورد مانند یک کلاس عادی کار می‌کند: +علاوه بر محدودیت‌هایی که در بالا ذکر شد، کلاس رکورد مانند یک کلاس عادی کار می‌کند: ۱- نمونه‌های کلاس رکورد با استفاده از کلیدواژه new ساخته می‌شوند. -۲- کلاس رکورد می‌تواند به شکل سطح بالا (‌‌top level) یا به شکل تودرتو تعریف شود و حتی می‌تواند جنریک باشد. +۲- کلاس رکورد می‌تواند به شکل سطح بالا (‌‌top level) یا به شکل تودرتو تعریف شود و حتی می‌تواند generic باشد. -۳- یک کلاس رکورد می‌تواند متدهای استاتیک، فیلدها و مقداردهی‌های اولیه داشته باشد. +۳- کلاس رکورد می‌تواند متدهای استاتیک، فیلدها و مقداردهی‌های اولیه داشته باشد. ۴- در کلاس رکورد می‌توان روی متدهای غیراستاتیک نیز تعریف کرد. -5- کلاس رکورد می‌تواند یک یا چند رابط (interface) را پیاده‌سازی کند. اما یک کلاس رکورد نمی‌تواند یک از یک کلاس ارث‌بری کند چون دارای state می‌شود و فراتر از چیزی است که در بالا توضیح داده شده است. مانند باقی کلاس‌‌ها، می‌توان از یک اینترفیس برای توصیف رفتار چند رکورد استفاده کرد. این رفتار ممکن است مستقل از دامنه‌ی مساله (مثل واسط Compareable) باشند یا مختص دامنه (*domain-specific*) باشد که در این صورت رکوردها می‌توانند بخشی از سلسله مراتب **sealed** باشند. +5- کلاس رکورد می‌تواند یک یا چند اینترفیس را پیاده‌سازی کند. اما نمی‌تواند از یک کلاس ارث‌بری کند چون دارای state می‌شود و فراتر از چیزی است که در بالا توضیح داده شده است. مانند باقی کلاس‌‌ها، می‌توان از یک اینترفیس برای توصیف رفتار چند رکورد استفاده کرد. این رفتار ممکن است مستقل از دامنه‌ مساله (مثل واسط Compareable) باشند یا مختص دامنه (*domain-specific*) باشد که در این صورت رکوردها می‌توانند بخشی از سلسله مراتب **sealed** باشند. -6- یک کلاس رکورد می‌تواند تایپ های تودرتو از جمله رکورد های تودرتو را اعلان کند. اگر یک کلاس رکورد خودش تودرتو باشد، به طور ضمنی استاتیک است. به این شکل از به وجود آمدن state قابل تغییر جلوگیری می‌شود. +6- یک کلاس رکورد می‌تواند تایپ‌های تودرتو از جمله رکوردهای تودرتو را اعلان کند. اگر یک کلاس رکورد خودش تودرتو باشد، به طور ضمنی استاتیک است. به این شکل از به وجود آمدن state قابل تغییر جلوگیری می‌شود. -7-می‌توان برای کلاس‌های رکورد یا اجزای آن ممکن است حاشیه‌‌نویسی (annotation) قرار داد. +7-می‌توان برای کلاس‌های رکورد یا اجزای آن حاشیه‌‌نویسی (annotation) قرار داد. -8- نمونه‌های کلاس رکورد می‌توانند سریالایز و یا دی‌سریالایز شوند. با این حال، این پروسه با استفاده از متدهای writeObject, readObject, readObjectNoData, writeExternal, readExternal، نمی‌تواند شخصی‌سازی شود. مولفه‌های یک کلاس رکورد، serialization را کنترل می‌کنند‌، درحالی که سازنده‌های متعارف یک کلاس رکورد deserialization را کنترل می‌کنند. +8- نمونه‌های کلاس رکورد می‌توانند سریالایز و یا دی‌سریالایز شوند. با این حال، این پروسه با استفاده از متدهای writeObject, readObject, readObjectNoData, writeExternal, readExternal، نمی‌تواند شخصی‌سازی شود. مولفه‌های یک کلاس رکورد، serialization را کنترل می‌کنند‌، درحالی که سازنده‌های متعارف یک کلاس رکورد deserialization را کنترل می‌کنند. @@ -205,7 +205,7 @@ public record Rectangle(int width, int length) { -یک آرایه از شی های java.lang.reflect.RecordComponent برمی‌گرداند. عناصر این آرایه به همان ترتیبی که در تعریف رکورد آمده‌اند، می‌آیند. اطلاعات اضافی مثل نام، حاشیه‌نویسی و توابع دسترسی (accessor) را می‌توان از هر عنصر آرایه استخراج کرد. +یک آرایه از شی‌های java.lang.reflect.RecordComponent بر می‌گرداند. عناصر این آرایه به همان ترتیبی که در تعریف رکورد آمده‌اند، می‌آیند. اطلاعات اضافی مثل نام، حاشیه‌نویسی و توابع دسترسی (accessor) را می‌توان از هر عنصر آرایه استخراج کرد. ```java boolean isRecord(): @@ -213,7 +213,7 @@ public record Rectangle(int width, int length) { -این متد اگر کلاس داده شده از نوع رکورد باشد، true برمیگرداند.(چیزی شبیه به isEnum) +این متد اگر کلاس داده‌شده از نوع رکورد باشد، true بر می‌گرداند (چیزی شبیه به isEnum).