Các phương thức mặc định của Java 8 là đặc điểm: an toàn?


116

Sử dụng các phương thức mặc định như một phiên bản đặc điểm của người nghèo trong Java 8 có an toàn không?

Một số cho rằng nó có thể khiến gấu trúc buồn nếu bạn sử dụng chúng chỉ vì mục đích của nó, bởi vì nó rất tuyệt, nhưng đó không phải là ý định của tôi. Người ta cũng thường nhắc rằng các phương thức mặc định đã được giới thiệu để hỗ trợ sự phát triển của API và khả năng tương thích ngược, điều này đúng, nhưng điều này không làm cho việc sử dụng chúng như các đặc điểm riêng.

Tôi có trong đầu trường hợp sử dụng thực tế sau :

public interface Loggable {
    default Logger logger() {
        return LoggerFactory.getLogger(this.getClass());
    }
}

Hoặc có lẽ, xác định PeriodTrait:

public interface PeriodeTrait {
    Date getStartDate();
    Date getEndDate();
    default isValid(Date atDate) {
        ...
    }
}

Phải thừa nhận rằng bố cục có thể được sử dụng (hoặc thậm chí là các lớp trợ giúp) nhưng nó có vẻ dài dòng hơn, lộn xộn hơn và không cho phép hưởng lợi từ tính đa hình.

Vì vậy, liệu sử dụng các phương pháp mặc định làm đặc điểm cơ bản có ổn / an toàn không hay tôi có nên lo lắng về các tác dụng phụ không lường trước được không?

Một số câu hỏi trên SO có liên quan đến đặc điểm Java và Scala; đó không phải là vấn đề ở đây. Tôi cũng không chỉ hỏi ý kiến. Thay vào đó, tôi đang tìm kiếm một câu trả lời có thẩm quyền hoặc ít nhất là thông tin chi tiết về lĩnh vực: nếu bạn đã sử dụng các phương pháp mặc định làm đặc điểm cho dự án công ty của mình, thì nó có phải là một quả bom hẹn giờ không?


Đối với tôi, dường như bạn có thể nhận được những lợi ích tương tự khi kế thừa một lớp trừu tượng và không phải lo lắng về việc khiến gấu trúc khóc ... Lý do duy nhất tôi có thể thấy để sử dụng một phương thức mặc định trong Giao diện là bạn cần chức năng và không thể sửa đổi một loạt mã kế thừa dựa trên Giao diện.
Deven Phillips

1
Tôi đồng ý với @ infosec812 về việc mở rộng một lớp trừu tượng xác định trường ghi nhật ký tĩnh của riêng nó. Phương thức logger () của bạn sẽ không khởi tạo một phiên bản trình ghi mới mỗi khi nó được gọi?
Eidan Spiegel

Để ghi nhật ký, bạn có thể muốn xem Projectlombok.org và chú thích @ Slf4j của họ.
Deven Phillips

Câu trả lời:


120

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ó varcá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 Amở rộng các đặc điểm XY , thì thứ tự trong đó XYđược trộn lẫn trong đó xác định cách các xung đột giữa XYđượ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ế 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 forEachphươ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 @implSpecthẻ 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.


9
Cảm ơn Brian vì câu trả lời toàn diện này. Bây giờ tôi có thể sử dụng các phương pháp mặc định với một trái tim nhẹ. Người đọc: có thể tìm thấy thêm thông tin từ Brian Goetz về sự phát triển giao diện và các phương pháp mặc định trong NightHacking Worldwide Lambdas chẳng hạn.
youri

Cảm ơn @ brian-goetz. Từ những gì bạn đã nói, tôi nghĩ rằng các phương pháp mặc định của Java gần với khái niệm về các đặc điểm truyền thống như được định nghĩa trong Ducasse và cộng sự ( scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf ). Đối với tôi, "đặc điểm" Scala có vẻ không phải là đặc điểm, vì chúng có trạng thái, sử dụng tuyến tính hóa trong thành phần của chúng và có vẻ như chúng cũng ngầm giải quyết các xung đột về phương pháp - và đây là tất cả những điều mà các đặc điểm truyền thống không có. Trên thực tế, tôi có thể nói rằng các đặc điểm Scala giống với hỗn hợp hơn là các đặc điểm. Bạn nghĩ sao? Tái bút: Tôi chưa bao giờ viết mã bằng Scala.
adino

Làm thế nào về việc sử dụng triển khai mặc định trống cho các phương thức trong một giao diện hoạt động như một trình lắng nghe? Việc triển khai trình lắng nghe có thể chỉ quan tâm đến việc lắng nghe một số phương thức giao diện, do đó, bằng cách đặt các phương thức mặc định, trình triển khai chỉ phải triển khai các phương thức mà nó cần lắng nghe.
Lahiru Chandima 21/02/17

1
@LahiruChandima Thích với MouseListener? Trong phạm vi mà kiểu API này có ý nghĩa, điều này phù hợp với nhóm "phương pháp tùy chọn". Đảm bảo ghi rõ ràng tài liệu tùy chọn-ness!
Brian Goetz

Đúng. Cũng như trong MouseListener. Cảm ơn vì sự trả lời.
Lahiru Chandima
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.