Khi nào nên sử dụng: Phương thức mặc định của giao diện Java 8+, so với phương thức trừu tượng


540

Java 8 cho phép thực hiện mặc định các phương thức trong các giao diện được gọi là Phương thức mặc định .

Tôi bối rối giữa khi tôi sẽ sử dụng loại đó interface default method, thay vì mộtabstract class (với abstract method(s)).

Vậy khi nào nên sử dụng giao diện với các phương thức mặc định và khi nào nên sử dụng một lớp trừu tượng (với phương thức trừu tượng)? Các lớp trừu tượng vẫn còn hữu ích trong kịch bản đó?


38
Có lẽ bạn vẫn không thể có các trường, phương thức riêng tư, v.v. trong các giao diện, trong khi bạn có thể trong lớp trừu tượng?
sp00m

2
Tôi đã tự hỏi về chủ đề này trước đây, bây giờ tôi đã rõ. Cảm ơn @Narendra Pathai. Tôi muốn thêm liên kết của một chủ đề khác mà bạn hỏi về cùng một chủ đề, vì cả hai đều là nghi ngờ của tôi. stackoverflow.com/questions/19998309/
Mạnh

Bạn có thể tìm thấy một bài viết hay trên bài này tại đây: blog.codefx.org/java/everything-about-default-methods
Fabri Pautasso

Đôi khi bạn vẫn có thể mã một lớp cơ sở như một giao diện ngay cả khi lớp cơ sở có trạng thái. Chỉ là giao diện phải xác định setters và getters cho trạng thái và các lớp cụ thể phải thực hiện chúng và xác định trường. Một hạn chế về điều này là trong một lớp trừu tượng, thuộc tính bean có thể là riêng tư hoặc được bảo vệ. Trong giao diện chỉ có phương thức công khai. Vì vậy, một lý do bạn sẽ sử dụng một lớp cơ sở trừu tượng là nếu các lớp của bạn có một thuộc tính cần phải riêng tư hoặc được bảo vệ.
DaBlick

@DaBlick Bạn có thể không giải quyết vấn đề trạng thái trong giao diện qua HashMap. Ví dụ: nếu bạn muốn một lớp Foo chứa int a, b, String c. và bạn muốn họ có trạng thái, tạo tên HashMap </ * của đối tượng Foo * / String, / * ánh xạ của các trường * / Hashmap </ * tên trường cụ thể * / Chuỗi, / * giá trị trường * / Object >> map . Khi bạn muốn "khởi tạo" lớp lý thuyết Foo, bạn có phương thức, khởi tạo (String nameOfFoo) có map.put (nameOfFoo, các trường) trong đó các trường là HashMap <String, Object> Field.put ("a", new int ("5")); Field.put ("b", new int ("6")); Field.put ("c", "blah"));
George Xavier

Câu trả lời:


307

Có rất nhiều lớp trừu tượng hơn các triển khai phương thức mặc định (như trạng thái riêng tư), nhưng kể từ Java 8, bất cứ khi nào bạn có lựa chọn nào, bạn nên đi với phương thức defender (aka. default) Trong giao diện.

Hạn chế của phương thức mặc định là nó chỉ có thể được thực hiện theo các điều khoản của các cuộc gọi đến các phương thức giao diện khác, không có tham chiếu đến trạng thái của một triển khai cụ thể. Vì vậy, trường hợp sử dụng chính là phương pháp tiện lợi và cao cấp hơn.

Điểm hay của tính năng mới này là, trước đây bạn buộc phải sử dụng một lớp trừu tượng cho các phương thức tiện lợi, do đó hạn chế người triển khai thành thừa kế duy nhất, giờ đây bạn có thể có một thiết kế thực sự sạch sẽ chỉ với giao diện và tối thiểu thực hiện nỗ lực buộc các lập trình viên.

Động lực ban đầu để giới thiệu defaultcác phương thức cho Java 8 là mong muốn mở rộng các giao diện Khung bộ sưu tập với các phương thức hướng lambda mà không phá vỡ bất kỳ triển khai hiện có nào. Mặc dù điều này có liên quan nhiều hơn đến các tác giả của các thư viện công cộng, bạn cũng có thể thấy tính năng tương tự hữu ích trong dự án của mình. Bạn đã có một nơi tập trung để thêm tiện lợi mới và bạn không cần phải dựa vào phần còn lại của hệ thống phân cấp.


34
Theo lý do này, điều tiếp theo họ sẽ thêm là khai báo phương thức mặc định. Tôi vẫn không chắc chắn về điều này, tính năng có vẻ giống như một bản hack đối với tôi đang bị phơi bày với mọi người vì lạm dụng.
con báo

3
Việc sử dụng duy nhất các lớp trừu tượng trong kỷ nguyên Java 8 mà tôi có thể thấy là để xác định các trường không phải là cuối cùng. Trong Giao diện, các trường theo mặc định là cuối cùng để bạn không thể thay đổi chúng sau khi chúng được gán.
Anuroop

7
@Anuroop Không chỉ mặc định --- đó là lựa chọn duy nhất. Các giao diện không thể khai báo trạng thái cá thể, đó là lý do tại sao các lớp trừu tượng ở đây để ở lại.
Marko Topolnik

2
@PhilipRego Các phương thức trừu tượng không gọi bất cứ thứ gì vì chúng không có triển khai. Các phương thức được triển khai trong một lớp có thể truy cập trạng thái của lớp (các biến thể hiện). Các giao diện không thể khai báo chúng để các phương thức mặc định không thể truy cập chúng. Họ phải dựa vào lớp cung cấp một phương thức đã thực hiện để truy cập vào trạng thái.
Marko Topolnik

2
Marko Topolnik, câu trả lời của bạn đã chết. Nhưng tôi muốn giới thiệu một bản cập nhật cho câu trả lời của bạn. Bạn có thể muốn thêm rằng vẻ đẹp của các phương thức mặc định là, nếu giao diện thêm các phương thức mặc định mới, việc triển khai giao diện đó trước đó của bạn sẽ không bị hỏng. Điều này không đúng trước Java 8.
hfontanez

125

Có một vài sự khác biệt về kỹ thuật. Các lớp trừu tượng vẫn có thể làm nhiều hơn so với các giao diện Java 8:

  1. Lớp trừu tượng có thể có một hàm tạo.
  2. Các lớp trừu tượng có cấu trúc hơn và có thể giữ một trạng thái.

Về mặt khái niệm, mục đích chính của các phương thức bảo vệ là khả năng tương thích ngược sau khi giới thiệu các tính năng mới (như các hàm lambda) trong Java 8.


20
Câu trả lời này thực sự chính xác và có ý nghĩa đặc biệt là "Về mặt khái niệm, mục đích chính của các phương pháp phòng thủ là khả năng tương thích ngược"
Thử

1
@UnKnown trang này cung cấp cái nhìn sâu sắc hơn: docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
bernie

@UnKnown, về cơ bản, nó cho phép bạn thêm các phương thức vào một giao diện và các lớp thực hiện giao diện đó sẽ tự động có được chức năng đó.
Truyền thuyết Bước sóng

3
Một điểm tinh tế hơn về điểm không. 2 ở trên về "có thể giữ trạng thái này". Các lớp trừu tượng có thể giữ trạng thái có thể được thay đổi sau này. Các giao diện cũng có thể giữ trạng thái nhưng một khi trạng thái được gán sau khi tạo cá thể, trạng thái không thể thay đổi.
Anuroop

3
@Anuroop Tôi không mô tả các public static finaltrường của giao diện là "trạng thái". Phần này staticcó nghĩa là chúng không liên quan đến một trường hợp cụ thể nào cả. Chúng được gán trên khởi tạo lớp , không giống như sau khi tạo cá thể .
geronimo

65

Điều này đang được mô tả trong bài viết này . Hãy nghĩ về forEachBộ sưu tập.

List<?> list = 
list.forEach(…);

ForEach không được tuyên bố bởi java.util.Listcũng không phải java.util.Collection giao diện. Một giải pháp rõ ràng là chỉ cần thêm phương thức mới vào giao diện hiện có và cung cấp việc triển khai ở những nơi cần thiết trong JDK. Tuy nhiên, một khi được xuất bản, không thể thêm phương thức vào giao diện mà không phá vỡ triển khai hiện có.

Lợi ích mà các phương thức mặc định mang lại là giờ đây có thể thêm một phương thức mặc định mới vào giao diện và nó không phá vỡ các triển khai.


1
'Không thể thêm phương thức vào giao diện mà không phá vỡ triển khai hiện có' - phải không?
Andrey Chaschev

26
@AndreyChaschev Nếu bạn thêm một phương thức mới vào giao diện thì tất cả những người triển khai phải thực hiện phương thức mới đó. Do đó, nó phá vỡ các triển khai hiện có.
Marko Topolnik

4
@MarkoTopolnik cảm ơn, đã bỏ lỡ điều đó. Chỉ cần đề cập đến có một cách để tránh một phần điều này - bằng cách trình bày phương pháp này trong một triển khai trừu tượng mặc định. Đối với ví dụ này, điều này sẽ được AbstractList::forEachném một UnsupportedOperationException.
Andrey Chaschev

3
@AndreyChaschev Vâng, đó là cách cũ (khm ... là cách hiện tại :), với sự thiếu sót là nó hạn chế người thực hiện kế thừa duy nhất từ ​​việc triển khai trừu tượng được cung cấp.
Marko Topolnik

Tôi sẽ không phá vỡ nếu điều đó xảy ra trước thời hạn, tất cả các triển khai đều bao gồm phương pháp đó. Điều này là không thể nhưng có thể.
George Xavier

19

Hai cái này khá khác nhau:

Các phương thức mặc định là thêm chức năng bên ngoài vào các lớp hiện có mà không thay đổi trạng thái của chúng.

Và các lớp trừu tượng là một kiểu thừa kế bình thường , chúng là các lớp bình thường được dự định mở rộng.


18

Như được mô tả trong này bài viết,

Các lớp trừu tượng so với các giao diện trong Java 8

Sau khi giới thiệu Phương thức mặc định, có vẻ như các giao diện và các lớp trừu tượng là như nhau. Tuy nhiên, chúng vẫn là khái niệm khác nhau trong Java 8.

Lớp trừu tượng có thể định nghĩa hàm tạo. Chúng có cấu trúc chặt chẽ hơn và có thể có một trạng thái liên quan đến chúng. Ngược lại, phương thức mặc định chỉ có thể được triển khai trong các điều khoản gọi các phương thức giao diện khác, không có tham chiếu đến trạng thái của một triển khai cụ thể. Do đó, cả hai sử dụng cho các mục đích khác nhau và lựa chọn giữa hai thực sự phụ thuộc vào bối cảnh kịch bản.


Tôi tin rằng lớp Trừu tượng có Trình xây dựng có thể được định nghĩa không giống như trong Giao diện. Trong Java 8, cả hai đều khác nhau do điều này.
Hemanth Peela

1
Tại sao một lớp trừu tượng có một hàm tạo nếu nó không thể được khởi tạo?
George Xavier

Chúng ta có thể gọi super () từ lớp con sẽ gọi hàm tạo của lớp trừu tượng. Điều này tác động đến trạng thái của lớp trừu tượng.
Sujay Mohan

14

Về truy vấn của bạn về

Vậy khi nào nên sử dụng giao diện với các phương thức mặc định và khi nào nên sử dụng một lớp trừu tượng? Các lớp trừu tượng vẫn còn hữu ích trong kịch bản đó?

tài liệu java cung cấp câu trả lời hoàn hảo.

Các lớp trừu tượng so với các giao diện:

Các lớp trừu tượng tương tự như giao diện. Bạn không thể khởi tạo chúng và chúng có thể chứa hỗn hợp các phương thức được khai báo có hoặc không có triển khai.

Tuy nhiên, với các lớp trừu tượng, bạn có thể khai báo các trường không tĩnh và cuối cùng và xác định các phương thức cụ thể công khai, được bảo vệ và riêng tư.

Với các giao diện, tất cả các trường được tự động công khai, tĩnh và cuối cùng và tất cả các phương thức mà bạn khai báo hoặc xác định (như các phương thức mặc định) là công khai. Ngoài ra, bạn chỉ có thể mở rộng một lớp, dù nó có trừu tượng hay không, trong khi bạn có thể thực hiện bất kỳ số lượng giao diện nào.

Các trường hợp sử dụng cho từng trường hợp đã được giải thích trong bài SE dưới đây:

Sự khác biệt giữa một giao diện và lớp trừu tượng là gì?

Các lớp trừu tượng vẫn còn hữu ích trong kịch bản đó?

Đúng. Chúng vẫn hữu ích. Chúng có thể chứa các phương thức và thuộc tính không tĩnh, không cuối cùng ( được bảo vệ, riêng tư ngoài công khai ), điều này không thể thực hiện được ngay cả với các giao diện Java-8.


Bây giờ các giao diện cũng có các phương thức riêng tư howtodoinjava.com/java9/java9-private-interface-methods
valijon

13

Bất cứ khi nào chúng ta có sự lựa chọn giữa lớp trừu tượng và giao diện, chúng ta nên luôn luôn (hầu như) thích các phương thức mặc định (còn được gọi là phương thức bảo vệ hoặc phần mở rộng ảo).

  1. Các phương thức mặc định đã chấm dứt mô hình giao diện cổ điển và một lớp đồng hành thực hiện hầu hết hoặc tất cả các phương thức trong giao diện đó. Một ví dụ là Collection and AbstractCollection. Bây giờ chúng ta nên thực hiện các phương thức trong chính giao diện để cung cấp chức năng mặc định. Các lớp thực hiện giao diện có sự lựa chọn để ghi đè các phương thức hoặc kế thừa thực hiện mặc định.
  2. Một cách sử dụng quan trọng khác của các phương thức mặc định là interface evolution. Giả sử tôi có một quả bóng đẳng cấp như:

    public class Ball implements Collection { ... }

Bây giờ trong Java 8, một luồng tính năng mới được giới thiệu. Chúng ta có thể nhận được một luồng bằng cách sử dụng streamphương thức được thêm vào giao diện. Nếu streamkhông phải là một phương thức mặc định thì tất cả các cài đặt cho Collectiongiao diện sẽ bị hỏng vì chúng sẽ không thực hiện phương thức mới này. Thêm một phương thức không mặc định vào một giao diện thì không source-compatible.

Nhưng giả sử chúng ta không biên dịch lại lớp và sử dụng tệp jar cũ có chứa lớp này Ball. Lớp sẽ tải tốt mà không có phương thức bị thiếu này, các thể hiện có thể được tạo và dường như mọi thứ đều hoạt động tốt. NHƯNG nếu chương trình gọi streamphương thức theo ví dụ của Ballchúng tôi sẽ nhận được AbstractMethodError. Vì vậy, làm cho phương thức mặc định giải quyết cả hai vấn đề.


9

Các phương thức mặc định trong giao diện Java cho phép tiến hóa giao diện .

Với một giao diện hiện có, nếu bạn muốn thêm một phương thức vào nó mà không phá vỡ tính tương thích nhị phân với các phiên bản cũ hơn của giao diện, bạn có sẵn hai tùy chọn: thêm một phương thức mặc định hoặc phương thức tĩnh. Thật vậy, bất kỳ phương thức trừu tượng nào được thêm vào giao diện sẽ phải được các lớp hoặc giao diện thực hiện giao diện này thực hiện.

Một phương thức tĩnh là duy nhất cho một lớp. Một phương thức mặc định là duy nhất cho một thể hiện của lớp.

Nếu bạn thêm một phương thức mặc định vào một giao diện hiện có, các lớp và giao diện thực hiện giao diện này không cần phải thực hiện nó. Họ có thể

  • triển khai phương thức mặc định và nó ghi đè lên việc thực hiện trong giao diện đã triển khai.
  • khai báo lại phương thức (không thực hiện) làm cho nó trừu tượng.
  • không làm gì cả (thì phương thức mặc định từ giao diện đã triển khai chỉ đơn giản là được kế thừa).

Thêm về chủ đề ở đây .


7

Mặc dù đó là một câu hỏi cũ cho phép tôi cũng đưa ra ý kiến ​​của mình về nó.

  1. lớp trừu tượng: Bên trong lớp trừu tượng chúng ta có thể khai báo các biến thể hiện, được yêu cầu cho lớp con

    Giao diện: Giao diện bên trong mỗi biến luôn là tĩnh công khai và cuối cùng chúng ta không thể khai báo các biến thể hiện

  2. lớp trừu tượng: Lớp trừu tượng có thể nói về trạng thái của đối tượng

    Giao diện: Giao diện không bao giờ có thể nói về trạng thái của đối tượng

  3. lớp trừu tượng: Bên trong lớp trừu tượng chúng ta có thể khai báo các hàm tạo

    Giao diện: Giao diện bên trong chúng ta không thể khai báo các hàm tạo vì mục đích của các
    hàm tạo là khởi tạo các biến thể hiện. Vì vậy, nhu cầu của hàm tạo ở đó là gì nếu chúng ta không thể có các biến thể hiện trong các giao diện .

  4. lớp trừu tượng: Bên trong lớp trừu tượng chúng ta có thể khai báo các thể hiện và khối tĩnh

    Giao diện: Các giao diện không thể có các khối thể hiện và tĩnh.

  5. lớp trừu tượng: Lớp trừu tượng không thể tham chiếu biểu thức lambda

    Các giao diện: Các giao diện với phương thức trừu tượng đơn có thể tham chiếu biểu thức lambda

  6. lớp trừu tượng : Bên trong lớp trừu tượng, chúng ta có thể ghi đè các phương thức LỚP

    Các giao diện: Chúng ta không thể ghi đè các phương thức LỚP ĐỐI TƯỢNG trong các giao diện.

Tôi sẽ kết thúc trên lưu ý rằng:

Các khái niệm phương thức mặc định / khái niệm phương thức tĩnh trong giao diện xuất hiện chỉ để lưu các lớp thực hiện nhưng không cung cấp triển khai hữu ích có ý nghĩa. Phương pháp default / phương pháp tĩnh là loại thực giả, "nếu bạn muốn, bạn có thể sử dụng chúng hoặc bạn có thể ghi đè lên chúng (trong trường hợp các phương pháp mặc định) trong lớp thực hiện" Như vậy tiết kiệm chúng tôi từ việc thực hiện các phương pháp mới trong các lớp thực hiện bất cứ khi nào phương pháp mới trong giao diện được thêm. Do đó giao diện không bao giờ có thể bằng các lớp trừu tượng.


5

Quy tắc Remi ForaxBạn không thiết kế với các lớp Trừu tượng. Bạn thiết kế ứng dụng của bạn với các giao diện . Wthing là phiên bản của Java, bất kể ngôn ngữ là gì. Nó được hỗ trợ bởi nguyên tắc phân biệt I nterface trong SOL I D nguyên tắc này.

Sau này bạn có thể sử dụng các lớp Trừu tượng để nhân tố mã. Bây giờ với Java 8, bạn có thể làm điều đó trực tiếp trong giao diện. Đây là một cơ sở, không hơn.


2

khi nào nên sử dụng giao diện với các phương thức mặc định và khi nào nên sử dụng một lớp trừu tượng?

Khả năng tương thích ngược: Hãy tưởng tượng rằng giao diện của bạn được thực hiện bởi hàng trăm lớp, sửa đổi giao diện đó sẽ buộc tất cả người dùng thực hiện phương thức mới được thêm vào, mặc dù nó không cần thiết cho nhiều lớp khác thực hiện giao diện của bạn, Plus nó cho phép giao diện của bạn là một giao diện chức năng

Sự kiện & Hạn chế:

1-Chỉ có thể được khai báo trong một giao diện chứ không phải trong một lớp hoặc lớp trừu tượng.

2-Phải cung cấp một cơ thể

3-Nó không được coi là trừu tượng như các phương thức bình thường khác được sử dụng trong một giao diện.


1

Trong Java 8, một giao diện trông giống như một lớp trừu tượng mặc dù chúng có thể là một số khác biệt như:

1) Các lớp trừu tượng là các lớp, vì vậy chúng không bị hạn chế đối với các hạn chế khác của giao diện trong Java, ví dụ: lớp trừu tượng có thể có trạng thái , nhưng bạn không thể có trạng thái trên giao diện trong Java.

2) Một sự khác biệt về ngữ nghĩa giữa giao diện với các phương thức mặc định và lớp trừu tượng là bạn có thể định nghĩa các hàm tạo bên trong một lớp trừu tượng , nhưng bạn không thể định nghĩa hàm tạo bên trong giao diện trong Java


Tôi đồng ý với # 2 nhưng với # 1, bạn không thể thực hiện giao diện và do đó có trạng thái thông qua lớp triển khai?
George Xavier

0

Các phương thức mặc định trong Giao diện Java sẽ được sử dụng nhiều hơn để cung cấp hàm triển khai giả, do đó lưu bất kỳ lớp triển khai nào của giao diện đó khỏi sự khai báo tất cả các phương thức trừu tượng ngay cả khi chúng chỉ muốn xử lý một. Do đó, các phương thức mặc định trong giao diện là cách thay thế nhiều hơn cho khái niệm các lớp của bộ điều hợp.

Tuy nhiên, các phương thức trong lớp trừu tượng được cho là mang lại một triển khai có ý nghĩa mà bất kỳ lớp con nào cũng chỉ nên ghi đè nếu cần để ghi đè một chức năng chung.


0

Như đã đề cập trong các câu trả lời khác, khả năng thêm triển khai vào giao diện đã được thêm vào để cung cấp khả năng tương thích ngược trong khung Bộ sưu tập. Tôi cho rằng việc cung cấp khả năng tương thích ngược có khả năng là lý do chính đáng duy nhất để thêm triển khai vào giao diện.

Mặt khác, nếu bạn thêm triển khai vào một giao diện, bạn đang vi phạm luật cơ bản về lý do tại sao giao diện được thêm vào ngay từ đầu. Java là một ngôn ngữ kế thừa duy nhất, không giống như C ++, cho phép nhiều kế thừa. Các giao diện cung cấp các lợi ích đánh máy đi kèm với một ngôn ngữ hỗ trợ nhiều kế thừa mà không đưa ra các vấn đề đi kèm với nhiều kế thừa.

Cụ thể hơn, Java chỉ cho phép kế thừa duy nhất một triển khai, nhưng nó cho phép nhiều kế thừa giao diện. Ví dụ: sau đây là mã Java hợp lệ:

class MyObject extends String implements Runnable, Comparable { ... }

MyObject kế thừa chỉ một thực hiện, nhưng nó kế thừa ba hợp đồng.

Java đã thông qua nhiều kế thừa thực hiện vì nhiều kế thừa thực hiện đi kèm với một loạt các vấn đề nhức nhối, nằm ngoài phạm vi của câu trả lời này. Các giao diện đã được thêm vào để cho phép nhiều kế thừa hợp đồng (còn gọi là giao diện) mà không gặp sự cố về kế thừa nhiều lần thực hiện.

Để ủng hộ quan điểm của tôi, đây là một trích dẫn của Ken Arnold và James Gosling từ cuốn sách Ngôn ngữ lập trình Java, ấn bản thứ 4 :

Kế thừa duy nhất loại trừ một số thiết kế hữu ích và chính xác. Các vấn đề của nhiều kế thừa phát sinh từ nhiều kế thừa thực hiện, nhưng trong nhiều trường hợp, nhiều kế thừa được sử dụng để kế thừa một số hợp đồng trừu tượng và có lẽ là một triển khai cụ thể. Cung cấp một phương tiện để kế thừa một hợp đồng trừu tượng mà không kế thừa một triển khai cho phép gõ các lợi ích của nhiều kế thừa mà không gặp các vấn đề về kế thừa nhiều triển khai. Kế thừa của một hợp đồng trừu tượng được gọi là thừa kế giao diện . Ngôn ngữ lập trình Java hỗ trợ kế thừa giao diện bằng cách cho phép bạn khai báo một interfacekiểu


-1

Hãy nghĩ đầu tiên về nguyên tắc mở / đóng. Các phương thức mặc định trong giao diện DO VIOLATE nó. Đây là một tính năng xấu trong Java. Nó khuyến khích thiết kế xấu, kiến ​​trúc xấu, chất lượng phần mềm thấp. Tôi sẽ đề nghị tránh sử dụng phương pháp mặc định hoàn toàn.

Hãy tự hỏi mình một vài câu hỏi: Tại sao bạn không thể đưa các phương thức của mình vào lớp trừu tượng? Bạn sẽ cần nhiều hơn một lớp trừu tượng? Sau đó suy nghĩ về những gì lớp học của bạn chịu trách nhiệm. Bạn có chắc chắn tất cả các phương thức bạn sẽ đưa vào lớp duy nhất thực sự hoàn thành cùng một mục đích không? Có thể bạn sẽ phân biệt một số mục đích và sau đó sẽ chia lớp của bạn thành nhiều lớp, cho mỗi mục đích lớp riêng của nó.

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.