Câu trả lời ngắn gọn là: sẽ an toàn nếu bạn sử dụng chúng một cách an toàn :)
Câu trả lời quái gở: tell me gì bạn có nghĩa là bởi đặc điểm, và có lẽ tôi sẽ cung cấp cho bạn một câu trả lời tốt hơn :)
Về mức độ nghiêm trọng, thuật ngữ "đặc điểm" không được xác định rõ ràng. Nhiều nhà phát triển Java quen thuộc nhất với các đặc điểm khi chúng được thể hiện bằng Scala, nhưng Scala không phải là ngôn ngữ đầu tiên có các đặc điểm, cả về tên gọi hoặc hiệu lực.
Ví dụ, trong Scala, các đặc điểm là trạng thái (có thể có var
các biến); trong Pháo đài chúng là hành vi thuần túy. Các giao diện của Java với các phương thức mặc định là không trạng thái; điều này có nghĩa là chúng không phải là đặc điểm? (Gợi ý: đó là một câu hỏi mẹo.)
Một lần nữa, trong Scala, các đặc điểm được hình thành thông qua tuyến tính hóa; nếu lớp A
mở rộng các đặc điểm X
vàY
, thì thứ tự trong đó X
và Y
được trộn lẫn trong đó xác định cách các xung đột giữa X
và Y
được giải quyết. Trong Java, cơ chế tuyến tính hóa này không tồn tại (nó đã bị từ chối, một phần, vì nó quá "không giống Java".)
Lý do gần nhất cho việc thêm các phương thức mặc định vào giao diện là để hỗ trợ sự phát triển của giao diện , nhưng chúng tôi nhận thức rõ rằng chúng tôi đã vượt ra ngoài điều đó. Cho dù bạn coi đó là "sự tiến hóa giao diện ++" hay "đặc điểm--" là vấn đề diễn giải cá nhân. Vì vậy, để trả lời câu hỏi của bạn về sự an toàn ... miễn là bạn bám vào những gì cơ chế thực sự hỗ trợ, thay vì cố gắng kéo dài nó sang một thứ mà nó không hỗ trợ một cách mơ hồ, bạn sẽ ổn thôi.
Mục tiêu thiết kế chính là, từ quan điểm của khách hàng của một giao diện, các phương thức mặc định không thể phân biệt được với các phương thức giao diện "thông thường". Do đó, hàm mặc định của một phương thức chỉ thú vị đối với người thiết kế và triển khai giao diện.
Dưới đây là một số trường hợp sử dụng phù hợp với mục tiêu thiết kế:
Tiến hóa giao diện. Ở đây, chúng tôi đang thêm một phương thức mới vào một giao diện hiện có, có một triển khai mặc định hợp lý về các phương thức hiện có trên giao diện đó. Một ví dụ sẽ là thêm forEach
phương thức vào Collection
, nơi triển khai mặc định được viết theo iterator()
phương thức.
Các phương pháp "tùy chọn". Ở đây, người thiết kế giao diện nói rằng "Người triển khai không cần triển khai phương pháp này nếu họ sẵn sàng sống với những hạn chế trong chức năng đòi hỏi". Ví dụ, Iterator.remove
đã được đưa ra một mặc định ném UnsupportedOperationException
; vì phần lớn các triển khai Iterator
đều có hành vi này, mặc định làm cho phương thức này về cơ bản là tùy chọn. (Nếu hành vi từ AbstractCollection
được biểu thị dưới dạng mặc định trênCollection
, chúng tôi có thể làm tương tự đối với các phương thức gây đột biến.)
Các phương pháp tiện lợi. Đây là những phương thức hoàn toàn để thuận tiện, một lần nữa thường được thực hiện theo các phương thức không mặc định trên lớp. Các logger()
phương pháp trong ví dụ đầu tiên của bạn là một minh hoạ hợp lý về điều này.
Bộ tổ hợp. Đây là các phương thức tổng hợp tạo ra các phiên bản mới của giao diện dựa trên phiên bản hiện tại. Ví dụ, các phương pháp Predicate.and()
hoặcComparator.thenComparing()
là ví dụ về tổ hợp.
Nếu bạn cung cấp một triển khai mặc định, bạn cũng nên cung cấp một số thông số kỹ thuật cho mặc định (trong JDK, chúng tôi sử dụng @implSpec
thẻ javadoc cho việc này) để hỗ trợ người triển khai hiểu liệu họ có muốn ghi đè phương thức hay không. Một số giá trị mặc định, như các phương thức tiện lợi và tổ hợp, hầu như không bao giờ bị ghi đè; những phương thức khác, như các phương thức tùy chọn, thường bị ghi đè. Bạn cần cung cấp đủ thông số kỹ thuật (không chỉ tài liệu) về những gì mặc định hứa sẽ làm, để người triển khai có thể đưa ra quyết định hợp lý về việc họ có cần ghi đè nó hay không.