JavaRush /Blog Java /Random-VI /Các phương thức mặc định trong Java 8: Chúng có thể và kh...
Spitfire
Mức độ

Các phương thức mặc định trong Java 8: Chúng có thể và không thể làm gì?

Xuất bản trong nhóm
Bản dịch một bài viết của Peter Verhas ngày tháng 4 năm 2014. Các phương thức mặc định trong Java 8: Chúng có thể và không thể làm gì?  - 1Từ người dịch: thuật ngữ " phương thức mặc định " vừa xuất hiện trong Java và tôi không chắc liệu có bản dịch chính thức sang tiếng Nga cho nó hay không. Tôi sẽ sử dụng thuật ngữ "phương pháp mặc định", mặc dù tôi không nghĩ nó lý tưởng. Tôi mời bạn thảo luận về một bản dịch thành công hơn.

Phương pháp mặc định là gì

Giờ đây, với việc phát hành Java 8, bạn có thể thêm các phương thức mới vào giao diện để giao diện vẫn tương thích với các lớp triển khai nó. Điều này rất quan trọng nếu bạn đang phát triển một thư viện được nhiều lập trình viên từ Kyiv đến New York sử dụng. Trước Java 8, nếu bạn đã xác định một giao diện trong thư viện, bạn không thể thêm các phương thức vào đó mà không gặp rủi ro rằng một số ứng dụng chạy giao diện của bạn sẽ bị hỏng khi nó được cập nhật. Vì vậy, trong Java 8 bạn không còn có thể sợ điều này nữa? Không, bạn không thể. Việc thêm một phương thức mặc định vào giao diện có thể khiến một số lớp không sử dụng được. Đầu tiên chúng ta hãy nhìn vào những điều tốt đẹp về các phương thức mặc định. Trong Java 8, phương thức này có thể được triển khai trực tiếp trong giao diện. (Các phương thức tĩnh trong một giao diện giờ đây cũng có thể được triển khai, nhưng đó là một câu chuyện khác.) Một phương thức được triển khai trong một giao diện được gọi là phương thức mặc định và được biểu thị bằng từ khóa mặc định . Nếu một lớp triển khai một giao diện, thì nó có thể, nhưng không bắt buộc, triển khai các phương thức được triển khai trong giao diện đó. Lớp kế thừa việc triển khai mặc định. Đây là lý do tại sao không cần thiết phải sửa đổi các lớp khi thay đổi giao diện mà chúng triển khai.

Đa kế thừa?

Mọi thứ trở nên phức tạp hơn nếu một lớp triển khai nhiều hơn một (chẳng hạn như hai) giao diện và chúng triển khai cùng một phương thức mặc định. Lớp sẽ kế thừa phương thức nào? Câu trả lời là không có. Trong trường hợp này, lớp phải tự triển khai phương thức đó (trực tiếp hoặc bằng cách kế thừa nó từ lớp khác). Tình huống tương tự nếu chỉ có một giao diện có phương thức mặc định và ở giao diện kia, phương thức tương tự là trừu tượng. Java 8 cố gắng kỷ luật và tránh các tình huống mơ hồ. Nếu các phương thức được khai báo trong nhiều giao diện thì lớp đó sẽ không kế thừa cài đặt mặc định nào - bạn sẽ gặp lỗi biên dịch. Mặc dù vậy, bạn có thể không gặp lỗi biên dịch nếu lớp của bạn đã được biên dịch. Java 8 không đủ mạnh về mặt này. Có những lý do cho việc này mà tôi không muốn đề cập đến (ví dụ: bản phát hành Java đã được phát hành và thời gian thảo luận đã trôi qua từ lâu và nói chung, đây không phải là nơi dành cho họ).
  • Giả sử bạn có hai giao diện và một lớp triển khai cả hai giao diện đó.
  • Một trong các giao diện triển khai phương thức mặc định m().
  • Bạn biên dịch tất cả các giao diện và lớp.
  • Bạn thay đổi một giao diện không có phương thức m() bằng cách khai báo nó như một phương thức trừu tượng.
  • Bạn chỉ biên dịch giao diện đã sửa đổi.
  • Bắt đầu lớp học.
Các phương thức mặc định trong Java 8: Chúng có thể và không thể làm gì?  - 2Trong trường hợp này lớp học hoạt động. Bạn không thể biên dịch nó với các giao diện cập nhật, nhưng nó được biên dịch với các phiên bản cũ hơn và do đó hoạt động. Hiện nay
  • thay đổi giao diện bằng phương thức trừu tượng m() và thêm cách triển khai mặc định.
  • Biên dịch giao diện đã sửa đổi.
  • Chạy lớp: lỗi.
Khi có hai giao diện cung cấp cách triển khai mặc định của một phương thức, thì phương thức đó không thể được gọi trong một lớp trừ khi nó được chính lớp đó triển khai (một lần nữa, phương thức đó là của chính nó hoặc được kế thừa từ một lớp khác). Các phương thức mặc định trong Java 8: Chúng có thể và không thể làm gì?  - 3Tương thích lớp. Nó có thể được tải với một giao diện được sửa đổi. Nó thậm chí có thể chạy cho đến khi một phương thức được gọi có triển khai mặc định trong cả hai giao diện.

Mã ví dụ

Các phương thức mặc định trong Java 8: Chúng có thể và không thể làm gì?  - 4Để chứng minh điều trên, tôi đã tạo một thư mục test cho lớp C.java và 3 thư mục con cho các giao diện trong các tệp I1.java và I2.java. Thư mục gốc của bài kiểm tra chứa mã nguồn của lớp C.java. Thư mục cơ sở chứa phiên bản của các giao diện phù hợp để thực thi và biên dịch: giao diện I1 có phương thức mặc định m(); Giao diện I2 chưa có bất kỳ phương thức nào. Lớp có một phương thức mainđể chúng ta có thể thực thi nó để kiểm tra nó. Nó kiểm tra xem có bất kỳ đối số dòng lệnh nào không, vì vậy chúng ta có thể dễ dàng thực thi nó có hoặc không có lệnh gọi 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 {
}
Bạn có thể biên dịch và chạy lớp từ dòng lệnh.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
Thư mục tương thích chứa một phiên bản của giao diện I2 khai báo phương thức m() là trừu tượng và vì lý do kỹ thuật, một bản sao chưa sửa đổi của I1.java.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
Tập hợp như vậy không thể được sử dụng để biên dịch lớp 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
Thông báo lỗi rất chính xác. Tuy nhiên, chúng ta có C.class từ bản biên dịch trước đó và nếu chúng ta biên dịch các giao diện vào thư mục tương thích, chúng ta sẽ có hai giao diện vẫn có thể được sử dụng để chạy lớp:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
Thư mục thứ ba - wrong- chứa phiên bản I2, cũng khai báo phương thức m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
Bạn thậm chí không phải lo lắng về việc biên dịch. Mặc dù phương thức này được khai báo hai lần nhưng lớp này vẫn có thể được sử dụng và chạy cho đến khi phương thức m() được gọi. Đây là những gì chúng ta cần đối số dòng lệnh cho:
~/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$

Phần kết luận

Khi bạn chuyển thư viện của mình sang Java 8 và thay đổi giao diện để bao gồm các phương thức mặc định, bạn có thể sẽ không gặp bất kỳ vấn đề nào. Ít nhất, đó là điều mà các nhà phát triển thư viện Java 8 đang hy vọng khi họ bổ sung thêm chức năng. Các ứng dụng sử dụng thư viện của bạn vẫn đang sử dụng nó cho Java 7, nơi không có phương thức mặc định. Nếu một số thư viện được sử dụng cùng nhau, có thể xảy ra xung đột. Làm thế nào để tránh nó? Thiết kế API thư viện của bạn theo cách tương tự như trước đây. Đừng trở nên tự mãn bằng cách dựa vào khả năng của các phương pháp mặc định. Họ là một phương sách cuối cùng. Chọn tên cẩn thận để tránh xung đột với các giao diện khác. Hãy xem quá trình phát triển Java sử dụng tính năng này sẽ phát triển như thế nào.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION