ترجمه مقاله ای نوشته پیتر ورهاس در آوریل 2014. از مترجم: اصطلاح " روش پیش فرض " به تازگی در جاوا ظاهر شده است و من مطمئن نیستم که ترجمه ای به زبان روسی برای آن وجود دارد یا خیر. من از اصطلاح "روش پیش فرض" استفاده خواهم کرد، اگرچه فکر نمی کنم ایده آل باشد. شما را به بحث در مورد ترجمه موفق تر دعوت می کنم.
روش پیش فرض چیست؟
اکنون با انتشار جاوا 8 می توانید روش های جدیدی را به اینترفیس ها اضافه کنید تا رابط با کلاس هایی که آن را پیاده سازی می کنند سازگار بماند. اگر در حال توسعه کتابخانه ای هستید که برنامه نویسان زیادی از کیف تا نیویورک از آن استفاده می کنند، این بسیار مهم است. قبل از جاوا 8، اگر یک رابط را در یک کتابخانه تعریف میکردید، نمیتوانستید روشهایی را به آن اضافه کنید، بدون اینکه خطر خراب شدن برخی از برنامههایی که رابط شما را اجرا میکنند، در زمان بهروزرسانی آن خراب شود. بنابراین، در جاوا 8 دیگر نمی توانید از این ترسید؟ نه نمی توانی. افزودن یک روش پیشفرض به یک رابط ممکن است برخی از کلاسها را غیرقابل استفاده کند. بیایید ابتدا به چیزهای خوب در مورد روش های پیش فرض نگاه کنیم. در جاوا 8، روش را می توان به طور مستقیم در رابط پیاده سازی کرد. (روشهای استاتیک در یک رابط اکنون نیز قابل پیادهسازی هستند، اما این داستان دیگری است.) روشی که در یک رابط پیادهسازی میشود، متد پیشفرض نامیده میشود و با کلمه کلیدی پیشفرض نشان داده میشود . اگر کلاسی یک اینترفیس را پیاده سازی کند، می تواند متدهای پیاده سازی شده در اینترفیس را پیاده سازی کند، اما لازم نیست. کلاس پیاده سازی پیش فرض را به ارث می برد. به همین دلیل است که نیازی به تغییر کلاس ها در هنگام تغییر رابطی که پیاده سازی می کنند نیست.ارث چندگانه؟
اگر یک کلاس بیش از یک (مثلاً دو) اینترفیس را پیاده سازی کند، و آنها همان روش پیش فرض را پیاده سازی کنند، اوضاع پیچیده تر می شود. کلاس کدام متد را به ارث می برد؟ جواب هیچکدام نیست. در این حالت، کلاس باید خود متد را پیاده سازی کند (یا مستقیماً یا با ارث بردن آن از کلاس دیگری). وضعیت مشابه است اگر فقط یک رابط دارای روش پیش فرض باشد و در دیگری همان روش انتزاعی باشد. جاوا 8 سعی می کند منضبط باشد و از موقعیت های مبهم دوری کند. اگر متدها در بیش از یک اینترفیس اعلام شده باشند، هیچ پیاده سازی پیش فرضی توسط کلاس به ارث نمی رسد - یک خطای کامپایل دریافت خواهید کرد. اگرچه، اگر کلاس شما قبلاً کامپایل شده باشد، ممکن است با خطای کامپایل مواجه نشوید. جاوا 8 در این زمینه به اندازه کافی قوی نیست. دلایلی برای این وجود دارد که من نمیخواهم در مورد آنها صحبت کنم (مثلاً: نسخه جاوا قبلاً منتشر شده است و زمان بحث طولانی شده است و به طور کلی اینجا جای آنها نیست).- فرض کنید شما دو رابط دارید و یک کلاس هر دوی آنها را پیاده سازی می کند.
- یکی از اینترفیس ها متد پیش فرض m() را پیاده سازی می کند.
- شما تمام اینترفیس ها و کلاس را کامپایل می کنید.
- شما رابطی را که متد m() ندارد با اعلام آن به عنوان متد انتزاعی تغییر می دهید.
- شما فقط رابط اصلاح شده را کامپایل می کنید.
- کلاس را شروع کنید.
- رابط را با متد انتزاعی m() تغییر دهید و یک پیاده سازی پیش فرض اضافه کنید.
- رابط اصلاح شده را کامپایل کنید.
- کلاس اجرا: خطا.
کد نمونه
برای نشان دادن موارد فوق، یک دایرکتوری آزمایشی برای کلاس 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$
GO TO FULL VERSION