JavaRush /وبلاگ جاوا /Random-FA /روش های پیش فرض در جاوا 8: چه کاری می توانند انجام دهند و...
Spitfire
مرحله

روش های پیش فرض در جاوا 8: چه کاری می توانند انجام دهند و چه کاری نمی توانند انجام دهند؟

در گروه منتشر شد
ترجمه مقاله ای نوشته پیتر ورهاس در آوریل 2014. روش های پیش فرض در جاوا 8: چه کاری می توانند انجام دهند و چه کاری نمی توانند انجام دهند؟  - 1از مترجم: اصطلاح " روش پیش فرض " به تازگی در جاوا ظاهر شده است و من مطمئن نیستم که ترجمه ای به زبان روسی برای آن وجود دارد یا خیر. من از اصطلاح "روش پیش فرض" استفاده خواهم کرد، اگرچه فکر نمی کنم ایده آل باشد. شما را به بحث در مورد ترجمه موفق تر دعوت می کنم.

روش پیش فرض چیست؟

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

ارث چندگانه؟

اگر یک کلاس بیش از یک (مثلاً دو) اینترفیس را پیاده سازی کند، و آنها همان روش پیش فرض را پیاده سازی کنند، اوضاع پیچیده تر می شود. کلاس کدام متد را به ارث می برد؟ جواب هیچکدام نیست. در این حالت، کلاس باید خود متد را پیاده سازی کند (یا مستقیماً یا با ارث بردن آن از کلاس دیگری). وضعیت مشابه است اگر فقط یک رابط دارای روش پیش فرض باشد و در دیگری همان روش انتزاعی باشد. جاوا 8 سعی می کند منضبط باشد و از موقعیت های مبهم دوری کند. اگر متدها در بیش از یک اینترفیس اعلام شده باشند، هیچ پیاده سازی پیش فرضی توسط کلاس به ارث نمی رسد - یک خطای کامپایل دریافت خواهید کرد. اگرچه، اگر کلاس شما قبلاً کامپایل شده باشد، ممکن است با خطای کامپایل مواجه نشوید. جاوا 8 در این زمینه به اندازه کافی قوی نیست. دلایلی برای این وجود دارد که من نمی‌خواهم در مورد آنها صحبت کنم (مثلاً: نسخه جاوا قبلاً منتشر شده است و زمان بحث طولانی شده است و به طور کلی اینجا جای آنها نیست).
  • فرض کنید شما دو رابط دارید و یک کلاس هر دوی آنها را پیاده سازی می کند.
  • یکی از اینترفیس ها متد پیش فرض m() را پیاده سازی می کند.
  • شما تمام اینترفیس ها و کلاس را کامپایل می کنید.
  • شما رابطی را که متد m() ندارد با اعلام آن به عنوان متد انتزاعی تغییر می دهید.
  • شما فقط رابط اصلاح شده را کامپایل می کنید.
  • کلاس را شروع کنید.
روش های پیش فرض در جاوا 8: چه کاری می توانند انجام دهند و چه کاری نمی توانند انجام دهند؟  - 2در این مورد کلاس کار می کند. شما نمی توانید آن را با رابط های به روز شده کامپایل کنید، اما با نسخه های قدیمی کامپایل شده است و بنابراین کار می کند. اکنون
  • رابط را با متد انتزاعی m() تغییر دهید و یک پیاده سازی پیش فرض اضافه کنید.
  • رابط اصلاح شده را کامپایل کنید.
  • کلاس اجرا: خطا.
وقتی دو واسط وجود دارد که اجرای پیش‌فرض یک متد را ارائه می‌کند، آن متد را نمی‌توان در یک کلاس فراخوانی کرد، مگر اینکه توسط خود کلاس پیاده‌سازی شود (دوباره، یا به تنهایی یا از کلاس دیگری به ارث برده شده است). روش های پیش فرض در جاوا 8: چه کاری می توانند انجام دهند و چه کاری نمی توانند انجام دهند؟  - 3سازگار با کلاس می توان آن را با یک رابط تغییر یافته بارگذاری کرد. حتی ممکن است اجرا شود تا زمانی که متدی فراخوانی شود که دارای پیاده سازی پیش فرض در هر دو اینترفیس باشد.

کد نمونه

روش های پیش فرض در جاوا 8: چه کاری می توانند انجام دهند و چه کاری نمی توانند انجام دهند؟  - 4برای نشان دادن موارد فوق، یک دایرکتوری آزمایشی برای کلاس C.java و 3 زیر شاخه برای رابط های موجود در فایل های I1.java و I2.java ایجاد کردم. دایرکتوری ریشه برای آزمایش حاوی کد منبع کلاس C.java است. دایرکتوری پایه شامل نسخه ای از اینترفیس هایی است که برای اجرا و کامپایل مناسب هستند: رابط I1 دارای یک متد پیش فرض m(); رابط I2 هنوز هیچ روشی ندارد. کلاس یک متد دارد mainکه می توانیم برای آزمایش آن را اجرا کنیم. بررسی می کند که آیا آرگومان های خط فرمان وجود دارد، بنابراین می توانیم به راحتی آن را با یا بدون فراخوانی اجرا کنیم m().
~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}
می توانید کلاس را از خط فرمان کامپایل و اجرا کنید.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
دایرکتوری سازگار حاوی نسخه ای از رابط I2 است که متد m() را انتزاعی اعلام می کند و همچنین به دلایل فنی یک کپی اصلاح نشده از I1.java است.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
چنین مجموعه ای را نمی توان برای کامپایل کلاس C استفاده کرد:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error
پیام خطا بسیار دقیق است. با این حال، ما C.class را از یک کامپایل قبلی داریم و اگر اینترفیس ها را در دایرکتوری سازگار کامپایل کنیم، دو اینترفیس خواهیم داشت که همچنان می توان از آنها برای اجرای کلاس استفاده کرد:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
دایرکتوری سوم - wrong- حاوی نسخه I2 است که متد را نیز اعلام می کند m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
شما حتی نیازی به نگرانی در مورد تدوین ندارید. حتی اگر متد دو بار اعلام شده باشد، کلاس همچنان می تواند مورد استفاده قرار گیرد و اجرا شود تا متد m() فراخوانی شود. این همان چیزی است که برای آن به آرگومان خط فرمان نیاز داریم:
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

نتیجه

وقتی کتابخانه خود را به جاوا 8 پورت می‌کنید و رابط‌های خود را طوری تغییر می‌دهید که متدهای پیش‌فرض را شامل شود، احتمالاً هیچ مشکلی نخواهید داشت. حداقل، این همان چیزی است که توسعه دهندگان کتابخانه جاوا 8 به دلیل افزودن قابلیت ها به آن امیدوار هستند. برنامه‌هایی که از کتابخانه شما استفاده می‌کنند همچنان از آن برای جاوا 7 استفاده می‌کنند، جایی که هیچ روش پیش‌فرضی وجود ندارد. اگر چندین کتابخانه با هم استفاده شود، احتمال تضاد وجود دارد. چگونه از آن اجتناب کنیم؟ API کتابخانه خود را به همان روش قبلی طراحی کنید. با تکیه بر قابلیت های روش های پیش فرض راضی نباشید. آنها آخرین راه حل هستند. نام ها را با دقت انتخاب کنید تا از برخورد با رابط های دیگر جلوگیری کنید. بیایید ببینیم توسعه جاوا با استفاده از این ویژگی چگونه توسعه می یابد.
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION