کلاس ها و رابط های انتزاعی در جاوا اهداف اساسی متفاوتی را دنبال می کنند. تفاوت بین این عناصر زبان جاوا و نحوه استفاده از آنها در برنامه های خود را بیاموزید.
- ماهیت یک رابط در جاوا
- زمان استفاده از رابط ها در جاوا
- نسخ یک روش رابط در جاوا
- متغیرهای ثابت در جاوا
- روش های پیش فرض در جاوا
- ماهیت یک کلاس انتزاعی در جاوا
- روش های انتزاعی در کلاس های انتزاعی
- زمان استفاده از کلاس های انتزاعی در جاوا
- تفاوتهای بین کلاسهای انتزاعی و رابطها در جاوا
- در چالش کد جاوا شرکت کنید!
کلاسها و رابطهای انتزاعی در کد جاوا و حتی در خود کیت توسعه جاوا (JDK) فراوان هستند. هر عنصر کد یک هدف اساسی دارد:
- اینترفیس ها نوعی قرارداد کد هستند که باید توسط یک کلاس مشخص اجرا شوند.
- کلاس های انتزاعی مشابه کلاس های معمولی هستند، با این تفاوت که می توانند شامل روش های انتزاعی باشند که متدهای بدون بدنه هستند. کلاس های انتزاعی را نمی توان نمونه سازی کرد.
بسیاری از توسعه دهندگان معتقدند که رابط ها و کلاس های انتزاعی مشابه هستند، اما در واقع کاملاً متفاوت هستند. بیایید تفاوت های اصلی بین آنها را بررسی کنیم.
ماهیت یک رابط در جاوا
در قلب، یک رابط یک قرارداد است، بنابراین به یک پیاده سازی بستگی دارد تا به هدف خود عمل کند. یک رابط هرگز نمی تواند حالت داشته باشد، بنابراین نمی تواند از متغیرهای نمونه قابل تغییر استفاده کند. یک رابط فقط می تواند از متغیرهای نهایی استفاده کند.
زمان استفاده از رابط ها در جاوا
اینترفیس ها برای جداسازی کد و پیاده سازی چندشکلی بسیار مفید هستند. میتوانیم نمونهای را در JDK با رابط List
ببینیم:
public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean add(E e);
E remove(int index);
void clear();
}
همانطور که احتمالا متوجه شدید، این کد کوتاه و بسیار توصیفی است. ما به راحتی میتوانیم امضای روش را ببینیم، که از آن برای پیادهسازی متدها در رابط با استفاده از یک کلاس مشخص استفاده میکنیم.
واسط List
حاوی قراردادی است که میتواند توسط ArrayList
، Vector
، LinkedList
و کلاس های دیگر.
برای استفاده از چندشکلی، میتوانیم به سادگی نوع متغیر خود را با List
اعلام کنیم و سپس هر یک از نمونههای موجود را انتخاب کنیم. این یک مثال است:
List list = new ArrayList();
System.out.println(list.getClass());
List list = new LinkedList();
System.out.println(list.getClass());
خروجی این کد در اینجا آمده است:
class java.util.ArrayList
class java.util.LinkedList
در این مورد، روشهای پیادهسازی برای ArrayList
، LinkedList
و Vector
همگی متفاوت هستند، که یک سناریوی عالی برای استفاده از رابط. اگر متوجه شدید که بسیاری از کلاسها متعلق به یک کلاس والد با اقدامات روش مشابه اما رفتار متفاوت هستند، بهتر است از یک رابط استفاده کنید.
بعد، اجازه دهید به چند مورد از کارهایی که میتوانیم با رابطها انجام دهیم نگاه کنیم.
نسخ یک روش رابط در جاوا
به خاطر داشته باشید که رابط نوعی قرارداد است که باید توسط یک کلاس بتن اجرا شود. متدهای واسط به طور ضمنی انتزاعی هستند و همچنین نیاز به اجرای کلاس مشخص دارند.
یک مثال در اینجا آمده است:
public class OverridingDemo {
public static void main(String[] args) {
Challenger challenger = new JavaChallenger();
challenger.doChallenge();
}
}
interface Challenger {
void doChallenge();
}
class JavaChallenger implements Challenger {
@Override
public void doChallenge() {
System.out.println("Challenge done!");
}
}
خروجی این کد در اینجا آمده است:
Challenge done!
به جزئیات توجه کنید که روش های رابط به طور ضمنی انتزاعی هستند. این بدان معنی است که ما نیازی به صراحتاً آنها را به عنوان انتزاعی نداریم.
متغیرهای ثابت در جاوا
قانون دیگری که باید به خاطر بسپارید این است که یک رابط فقط می تواند دارای متغیرهای ثابت باشد. بنابراین، کد زیر خوب است:
public interface Challenger {
int number = 7;
String name = "Java Challenger";
}
توجه داشته باشید که هر دو متغیر به طور ضمنی نهایی
و ایستا
هستند. این بدان معنی است که آنها ثابت هستند، به یک نمونه وابسته نیستند و نمی توان آنها را تغییر داد.
اگر میخواهیم متغیرها را در واسط Challenger
تغییر دهیم، به این شکل بگو:
Challenger.number = 8;
Challenger.name = "Another Challenger";
ما یک خطای کامپایل را راه اندازی خواهیم کرد، مانند این:
Cannot assign a value to final variable 'number'
Cannot assign a value to final variable 'name'
روش های پیش فرض در جاوا
یک روش پیشفرض میتواند پیادهسازی داشته باشد، در حالی که روشهای انتزاعی نمیتوانند. روشهای پیشفرض نتیجه نوآوریهای بزرگ با لامبدا و استریم هستند، اما باید با احتیاط از آنها استفاده کنیم.
یک روش در JDK که از یک روش پیشفرض استفاده میکند، forEach()
است که بخشی از رابط Iterable
است. بهجای کپی کردن کد در هر پیادهسازی Iterable
، میتوانیم به سادگی از روش forEach
دوباره استفاده کنیم:
default void forEach(Consumer<? super T> action) {
// Code implementation here…
هر پیادهسازی تکرارپذیر
میتواند از روش forEach()
بدون نیاز به اجرای روش جدید استفاده کند. سپس، میتوانیم کد را با یک روش پیشفرض دوباره استفاده کنیم.
بیایید روش پیش فرض خود را ایجاد کنیم:
public class DefaultMethodExample {
public static void main(String[] args) {
Challenger challenger = new JavaChallenger();
challenger.doChallenge();
}
}
class JavaChallenger implements Challenger { }
interface Challenger {
default void doChallenge() {
System.out.println("Challenger doing a challenge!");
}
}
خروجی این است:
Challenger doing a challenge!
نکته مهمی که در مورد روش های پیش فرض باید به آن توجه کرد این است که هر روش پیش فرض نیاز به پیاده سازی دارد. یک روش پیشفرض نمیتواند ثابت باشد.
اکنون، اجازه دهید به کلاسهای انتزاعی برویم.
ماهیت یک کلاس انتزاعی در جاوا
کلاس های انتزاعی می توانند حالت با متغیرهای نمونه داشته باشند. این به این معنی است که یک متغیر نمونه را می توان استفاده کرد و جهش داد. این یک مثال است:
public abstract class AbstractClassMutation {
private String name = "challenger";
public static void main(String[] args) {
AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
abstractClassMutation.name = "mutated challenger";
System.out.println(abstractClassMutation.name);
}
}
class AbstractClassImpl extends AbstractClassMutation { }
خروجی اینجاست:
mutated challenger
روش های انتزاعی در کلاس های انتزاعی
درست مانند واسط ها، کلاس های انتزاعی نیز می توانند متدهای انتزاعی داشته باشند. روش انتزاعی روشی بدون بدنه است. برخلاف رابط ها، متدهای انتزاعی در کلاس های انتزاعی باید به صراحت به عنوان انتزاعی اعلام شوند. این یک مثال است:
public abstract class AbstractMethods {
abstract void doSomething();
}
تلاش برای اعلام یک روش بدون پیاده سازی، و بدون کلمه کلیدی abstract
، مانند این:
public abstract class AbstractMethods {
void doSomethingElse();
}
به یک خطای کامپایل منجر میشود، مانند این:
Missing method body, or declare abstract
زمان استفاده از کلاس های انتزاعی در جاوا
این ایده خوبی است که از یک کلاس انتزاعی زمانی که نیاز به پیاده سازی حالت تغییرپذیر دارید استفاده کنید. به عنوان مثال، چارچوب مجموعههای جاوا شامل AbstractList، که از حالت متغیرها استفاده می کند.
در مواردی که نیازی به حفظ وضعیت کلاس ندارید، معمولاً بهتر است از یک رابط استفاده کنید.
کلاس های انتزاعی در عمل
تفاوتهای بین کلاسهای انتزاعی و رابطها در جاوا
از دیدگاه برنامه نویسی شی گرا، تفاوت اصلی بین یک رابط و یک کلاس انتزاعی این است که یک رابط نمی تواند حالت داشته باشد، در حالی که کلاس انتزاعی می تواند حالت داشته باشد. با متغیرهای نمونه.
یک تفاوت کلیدی دیگر این است که کلاس ها می توانند بیش از یک رابط را پیاده سازی کنند، اما می توانند تنها یک کلاس انتزاعی را گسترش دهند. این یک تصمیم طراحی مبتنی بر این واقعیت است که وراثت چندگانه (بسط دادن بیش از یک کلاس) می تواند باعث بن بست کد شود. مهندسان جاوا تصمیم گرفتند از آن اجتناب کنند.
یک تفاوت دیگر این است که رابطها را میتوان توسط کلاسها پیادهسازی کرد یا توسط رابطها گسترش داد، اما کلاسها را فقط میتوان گسترش داد.
همچنین مهم است که توجه داشته باشید که عبارات لامبدا را فقط میتوان با یک رابط عملکردی (به معنی واسط تنها با یک روش) استفاده کرد، در حالی که کلاسهای انتزاعی با تنها یک روش انتزاعی نمیتوانند از لامبدا استفاده کنند. p>
جدول ۱ تفاوتهای بین کلاسهای انتزاعی و رابطها را خلاصه میکند.
رابط ها |
کلاس های انتزاعی |
---|---|
فقط می تواند متغیرهای استاتیک نهایی را داشته باشد. یک رابط هرگز نمی تواند وضعیت خود را تغییر دهد. |
می تواند هر نوع نمونه یا متغیر ایستا، قابل تغییر یا تغییرناپذیر داشته باشد. |
یک کلاس می تواند چندین رابط را پیاده سازی کند. |
یک کلاس می تواند تنها یک کلاس انتزاعی را گسترش دهد. |
می توان با کلمه کلیدی |
فقط قابل تمدید است. |
فقط میتوان از فیلدهای نهایی، پارامترها یا متغیرهای محلی برای روشها استفاده کرد. |
می تواند فیلدها، پارامترها یا متغیرهای محلی قابل تغییر نمونه داشته باشد. |
فقط رابطهای کاربردی میتوانند از ویژگی لامبدا در جاوا استفاده کنند. |
کلاس های انتزاعی تنها با یک روش انتزاعی نمی توانند از لامبدا استفاده کنند. |
نمی توان سازنده داشت. |
می تواند سازنده داشته باشد. |
می تواند روش های انتزاعی داشته باشد. میتواند روشهای پیشفرض و استاتیک (در جاوا ۸ معرفی شده) داشته باشد. میتواند روشهای خصوصی با پیادهسازی داشته باشد (معرفی شده در جاوا ۹). |
می تواند هر نوع روشی داشته باشد. |
رابط ها
کلاس های انتزاعی
فقط می تواند متغیرهای استاتیک نهایی را داشته باشد. یک رابط هرگز نمی تواند وضعیت خود را تغییر دهد.
می تواند هر نوع نمونه یا متغیر ایستا، قابل تغییر یا تغییرناپذیر داشته باشد.
یک کلاس می تواند چندین رابط را پیاده سازی کند.
یک کلاس می تواند تنها یک کلاس انتزاعی را گسترش دهد.
می توان با کلمه کلیدی Implements
پیاده سازی کرد. یک رابط همچنین میتواند رابطهای توسعه
را گسترش دهد.
فقط قابل تمدید است.
فقط میتوان از فیلدهای نهایی، پارامترها یا متغیرهای محلی برای روشها استفاده کرد.
می تواند فیلدها، پارامترها یا متغیرهای محلی قابل تغییر نمونه داشته باشد.
فقط رابطهای کاربردی میتوانند از ویژگی لامبدا در جاوا استفاده کنند.
کلاس های انتزاعی تنها با یک روش انتزاعی نمی توانند از لامبدا استفاده کنند.
نمی توان سازنده داشت.
می تواند سازنده داشته باشد.
می تواند روش های انتزاعی داشته باشد.
میتواند روشهای پیشفرض و استاتیک (در جاوا ۸ معرفی شده) داشته باشد.
میتواند روشهای خصوصی با پیادهسازی داشته باشد (معرفی شده در جاوا ۹).
می تواند هر نوع روشی داشته باشد.
در چالش کد جاوا شرکت کنید!
بیایید تفاوتهای اصلی بین رابطها و کلاسهای انتزاعی را با چالش کد جاوا بررسی کنیم. چالش کد زیر را داریم، یا میتوانید کلاسهای انتزاعی در مقابل چالش رابطها را در قالب ویدیویی مشاهده کنید. p>
در کد زیر، هم یک کلاس واسط و هم یک کلاس انتزاعی اعلان شده است و کد نیز از لامبدا استفاده می کند.
public class AbstractResidentEvilInterfaceChallenge {
static int nemesisRaids = 0;
public static void main(String[] args) {
Zombie zombie = () -> System.out.println("Graw!!! " + nemesisRaids++);
System.out.println("Nemesis raids: " + nemesisRaids);
Nemesis nemesis = new Nemesis() { public void shoot() { shoots = 23; }};
Zombie.zombie.shoot();
zombie.shoot();
nemesis.shoot();
System.out.println("Nemesis shoots: " + nemesis.shoots +
" and raids: " + nemesisRaids);
}
}
interface Zombie {
Zombie zombie = () -> System.out.println("Stars!!!");
void shoot();
}
abstract class Nemesis implements Zombie {
public int shoots = 5;
}
به نظر شما وقتی این کد را اجرا کنیم چه اتفاقی می افتد؟ یکی از موارد زیر را انتخاب کنید:
Compilation error at line 4
Graw!!! 0
Nemesis raids: 23
Stars!!!
Nemesis shoots: 23 and raids:1
Nemesis raids: 0
Stars!!!
Graw!!! 0
Nemesis shoots: 23 and raids: 1
Nemesis raids: 0
Stars!!!
Graw!!! 1
Nemesis shoots: 23 and raids:1
Compilation error at line 6
ویدیوی چالش کد جاوا
آیا خروجی درستی را برای این چالش انتخاب کرده اید؟ ویدیو را تماشا کنید یا به خواندن ادامه دهید تا متوجه شوید.
درک رابطها و کلاسها و متدهای انتزاعی در جاوا
این چالش کد جاوا بسیاری از مفاهیم مهم را در مورد رابطها، روشهای انتزاعی و موارد دیگر نشان میدهد. گام به گام کد به خط به ما چیزهای زیادی در مورد آنچه در خروجی اتفاق می افتد می آموزد.
خط اول چالش کد شامل یک عبارت لامبدا برای رابط Zombie
است. توجه کنید که در این لامبدا یک میدان ساکن را افزایش می دهیم. یک فیلد نمونه نیز در اینجا کار می کند، اما یک متغیر محلی که خارج از لامبدا اعلام شده است، کار نمی کند. بنابراین، تا کنون، کد به خوبی کامپایل خواهد شد. همچنین توجه کنید که عبارت لامبدا هنوز اجرا نشده است، بنابراین فیلد nemesisRaids
هنوز افزایش نخواهد یافت.
در این مرحله، فیلد nemesisRaids
را چاپ خواهیم کرد، که افزایش نمییابد زیرا عبارت لامبدا هنوز فراخوانی نشده و فقط اعلام شده است. بنابراین، خروجی از این خط خواهد بود:
Nemesis raids: 0
یک مفهوم جالب دیگر در این چالش کد جاوا این است که ما از کلاس داخلی ناشناس استفاده می کنیم. این اساساً به معنای هر کلاسی است که متدهای کلاس انتزاعی Nemesis
را پیاده سازی کند. ما واقعاً کلاس انتزاعی Nemesis
را نمونه سازی نمی کنیم زیرا در واقع یک کلاس ناشناس است. همچنین توجه داشته باشید که اولین کلاس انضمامی همیشه موظف به پیاده سازی متدهای انتزاعی در هنگام گسترش آنها خواهد بود.
در داخل رابط Zombie
، رابط zombie
static
Zombie
را داریم که با عبارت لامبدا اعلام شده است. بنابراین، وقتی روش تصویربرداری زامبی
را فرا میخوانیم، موارد زیر را چاپ میکنیم:
Stars!!!
خط بعدی کد عبارت لامبدا را که در ابتدا ایجاد کردیم فراخوانی می کند. بنابراین، متغیر nemesisRaids
افزایش خواهد یافت. با این حال، از آنجایی که ما از عملگر post-increment استفاده می کنیم، تنها پس از این دستور کد افزایش می یابد. خروجی بعدی این خواهد بود:
Graw!!! 0
اکنون روش shoot
را از nemesis
فراخوانی می کنیم، که متغیر نمونه shoots
آن را به ۲۳
تغییر می دهد. . توجه داشته باشید که این قسمت از کد بزرگترین تفاوت بین یک رابط و یک کلاس انتزاعی را نشان می دهد.
در نهایت، مقدار nemesis.shoots
و nemesisRaids
را چاپ می کنیم. بنابراین، خروجی این خواهد بود:
Nemesis shoots: 23 and raids: 1
در نتیجه، خروجی صحیح گزینه C است:
Nemesis raids: 0
Stars!!!
Graw!!! 0
Nemesis shoots: 23 and raids: 1
درباره جاوا بیشتر بیاموزید
- نکات کد سریع بیشتری دریافت کنید: همه مقالات رافائل را در مجموعه چالشگران جاوا InfoWorld بخوانید.
- برای آشنایی بیشتر با استفاده از رابطها در برنامههای جاوا، از جمله مکانها و مکانهایی که نباید از روشهای پیشفرض، ایستا و خصوصی استفاده کنید، به آموزش رابطهای جاوا Java 101 مراجعه کنید.
- اگر ویدیوی این چالشگر کد جاوا را دوست داشتید، ویدیوهای دیگر را در لیست پخش ویدیوی Java Challengers.
- در وبلاگ چالشگران جاوا رافائل و در کتاب او، جاوا چلنجرهای بیشتری را با بیش از ۷۰ چالش کد.
پست های مرتبط
زمان استفاده از کلاس های انتزاعی در مقابل رابط ها در جاوا
زمان استفاده از کلاس های انتزاعی در مقابل رابط ها در جاوا
زمان استفاده از کلاس های انتزاعی در مقابل رابط ها در جاوا