Mengapa Multiple Inheritance tidak diperbolehkan di Java atau C #?


115

Saya tahu bahwa multiple inheritance tidak diperbolehkan di Java dan C #. Banyak buku hanya mengatakan, pewarisan ganda tidak diperbolehkan. Tapi itu bisa diimplementasikan dengan menggunakan antarmuka. Tidak ada yang dibahas tentang mengapa itu tidak diperbolehkan. Adakah yang bisa memberi tahu saya dengan tepat mengapa itu tidak diizinkan?


1
Sama seperti untuk menunjukkan bahwa ada yang kerangka luar sana yang memungkinkan MI-seperti perilaku dalam C # kelas. Dan tentu saja Anda memiliki pilihan untuk memiliki mixin stateless (menggunakan metode ekstensi) - meskipun stateless, mereka tidak terlalu berguna.
Dmitri Nesteruk

1
Antarmuka yang ada hubungannya dengan warisan adalah ilusi yang dibuat oleh sintaks bahasa. Warisan seharusnya membuat Anda lebih kaya. Tidak seperti itu untuk antarmuka, Anda tidak mewarisi squat dan itu membuat Anda jauh lebih miskin. Anda mewarisi hutang judi paman Harry, Anda memiliki lebih banyak pekerjaan untuk dilakukan untuk mengimplementasikan antarmuka.
Hans Passant

Jawaban:


143

Jawaban singkatnya adalah: karena desainer bahasa memutuskan untuk tidak melakukannya.

Pada dasarnya, tampaknya desainer .NET dan Java tidak mengizinkan banyak pewarisan karena mereka beralasan bahwa menambahkan MI menambahkan terlalu banyak kerumitan pada bahasa sambil memberikan manfaat yang terlalu sedikit .

Untuk bacaan yang lebih menyenangkan dan mendalam, ada beberapa artikel yang tersedia di web dengan wawancara dari beberapa desainer bahasa. Misalnya, untuk .NET, Chris Brumme (yang bekerja di MS pada CLR) telah menjelaskan alasan mengapa mereka memutuskan untuk tidak:

  1. Bahasa yang berbeda sebenarnya memiliki ekspektasi yang berbeda terhadap cara kerja MI. Misalnya, bagaimana konflik diselesaikan dan apakah basis duplikat digabungkan atau berlebihan. Sebelum kita dapat mengimplementasikan MI di CLR, kita harus melakukan survei terhadap semua bahasa, mengetahui konsep umum, dan memutuskan bagaimana mengekspresikannya dengan bahasa yang netral. Kami juga harus memutuskan apakah MI termasuk dalam CLS dan apa artinya ini untuk bahasa yang tidak menginginkan konsep ini (mungkin VB.NET, misalnya). Tentu saja, itulah bisnis yang kami jalankan sebagai runtime bahasa umum, tetapi kami belum sempat melakukannya untuk MI.

  2. Jumlah tempat di mana MI benar-benar sesuai sebenarnya cukup kecil. Dalam banyak kasus, pewarisan beberapa antarmuka dapat menyelesaikan pekerjaan sebagai gantinya. Dalam kasus lain, Anda mungkin dapat menggunakan enkapsulasi dan delegasi. Jika kita menambahkan konstruksi yang sedikit berbeda, seperti mixin, apakah itu benar-benar lebih kuat?

  3. Beberapa pewarisan implementasi menyuntikkan banyak kerumitan ke dalam implementasi. Kompleksitas ini memengaruhi casting, tata letak, pengiriman, akses lapangan, serialisasi, perbandingan identitas, verifikasi, refleksi, generik, dan mungkin banyak tempat lain.

Anda dapat membaca artikel lengkapnya di sini.

Untuk Java, Anda bisa membaca artikel ini :

Alasan untuk menghilangkan beberapa pewarisan dari bahasa Java sebagian besar berasal dari tujuan "sederhana, berorientasi objek, dan familiar". Sebagai bahasa sederhana, pembuat Java menginginkan bahasa yang dapat dipahami oleh sebagian besar pengembang tanpa pelatihan ekstensif. Untuk itu, mereka bekerja untuk membuat bahasa yang semirip mungkin dengan C ++ (familiar) tanpa membawa kompleksitas C ++ yang tidak perlu (sederhana).

Menurut pendapat para desainer, warisan ganda menyebabkan lebih banyak masalah dan kebingungan daripada penyelesaiannya. Jadi mereka memotong banyak warisan dari bahasa (sama seperti mereka memotong kelebihan operator). Pengalaman C ++ desainer yang ekstensif mengajari mereka bahwa banyak warisan tidak sebanding dengan sakit kepala.


10
Perbandingan yang bagus. Saya kira cukup menunjukkan proses pemikiran yang mendasari kedua platform.
CurtainDog

97

Beberapa pewarisan implementasi adalah apa yang tidak diperbolehkan.

Masalahnya adalah bahwa compiler / runtime tidak dapat menemukan apa yang harus dilakukan jika Anda memiliki kelas Cowboy dan Artist, keduanya dengan implementasi untuk metode draw (), dan kemudian Anda mencoba untuk membuat tipe CowboyArtist baru. Apa yang terjadi ketika Anda memanggil metode draw ()? Apakah seseorang tergeletak mati di jalan, atau apakah Anda memiliki cat air yang indah?

Saya percaya itu disebut masalah warisan berlian ganda.


80
Anda mendapatkan cat air indah dari seseorang yang meninggal di jalan :-)
Dan F

Apakah ini satu-satunya masalah? Saya pikir, saya tidak yakin, bahwa C ++ menyelesaikan masalah ini dengan kata kunci virtual, apakah ini benar? Saya tidak pandai C ++
Abdulsattar Mohammed

2
Dalam C ++ Anda bisa menentukan fungsi kelas dasar mana yang akan dipanggil dalam turunan atau mengimplementasikannya kembali sendiri.
Dmitry Risenberg

1
Desain modern cenderung lebih mengutamakan komposisi daripada warisan dalam semua hal kecuali kasus yang paling sederhana. Warisan ganda tidak akan pernah dihitung sebagai kasus sederhana. Dengan komposisi, Anda memiliki kendali yang tepat atas apa yang dilakukan kelas Anda ketika ada masalah berlian seperti ini ...
Bill Michell

Anda hanya membutuhkan daftar prioritas. Ini digunakan dalam sistem objek Common Lisp, CLOS. Semua kelas membentuk heirachy dan metode yang dipanggil ditentukan dari sana. Selanjutnya, CLOS memungkinkan Anda untuk menentukan aturan yang berbeda sehingga seorang CowboyArtist.draw () mungkin pertama-tama menggambar seorang Cowboy dan kemudian seorang Artis. Atau apa pun yang Anda buat.
Sebastian Krog

19

Alasan: Java sangat populer dan mudah dikodekan, karena kesederhanaannya.

Jadi apa pun yang dirasa pengembang java sulit dan rumit untuk dipahami oleh programmer, mereka berusaha menghindarinya. Salah satu jenis properti tersebut adalah multiple inheritance.

  1. Mereka menghindari petunjuk
  2. Mereka menghindari banyak warisan.

Masalah dengan banyak warisan: Masalah berlian.

Contoh :

  1. Asumsikan bahwa kelas A memiliki metode fun (). kelas B dan kelas C berasal dari kelas A.
  2. Dan kelas B dan C, menggantikan metode fun ().
  3. Sekarang asumsikan bahwa kelas D mewarisi kelas B, dan C. (hanya Asumsi)
  4. Buat objek untuk kelas D.
  5. D d = baru D ();
  6. dan mencoba mengakses d.fun (); => akankah itu menyebut kesenangan kelas B () atau kesenangan kelas C ()?

Ini adalah ambiguitas yang ada dalam masalah intan.

Bukan tidak mungkin untuk memecahkan masalah ini, tetapi ini menciptakan lebih banyak kebingungan dan kerumitan bagi programmer saat membacanya. Itu menyebabkan lebih banyak masalah daripada yang coba dipecahkan.

Catatan : Tapi dengan cara apapun Anda selalu bisa mengimplementasikan beberapa warisan secara tidak langsung dengan menggunakan antarmuka.


1
"Tapi dengan cara apa pun Anda selalu dapat menerapkan beberapa warisan secara tidak langsung dengan menggunakan antarmuka." - yang mengharuskan programmer untuk menentukan kembali tubuh metode setiap waktu, lagi dan lagi.
Kari

13

Karena Java memiliki filosofi desain yang sangat berbeda dari C ++. (Saya tidak akan membahas C # di sini.)

Dalam mendesain C ++, Stroustrup ingin menyertakan fitur yang berguna, terlepas dari bagaimana fitur tersebut dapat disalahgunakan. Mungkin untuk mengacaukan banyak waktu dengan beberapa warisan, kelebihan operator, templat, dan berbagai fitur lainnya, tetapi mungkin juga untuk melakukan beberapa hal yang sangat baik dengannya.

Filosofi desain Java adalah untuk menekankan keamanan dalam konstruksi bahasa. Hasilnya adalah ada hal-hal yang jauh lebih canggung untuk dilakukan, tetapi Anda bisa lebih yakin bahwa kode yang Anda lihat memiliki arti seperti yang Anda pikirkan.

Selanjutnya, Java sebagian besar merupakan reaksi dari C ++ dan Smalltalk, bahasa OO yang paling terkenal. Ada banyak bahasa OO lainnya (Common Lisp sebenarnya adalah bahasa pertama yang distandarisasi), dengan sistem OO berbeda yang menangani MI dengan lebih baik.

Belum lagi MI di Java sangat mungkin dilakukan, menggunakan antarmuka, komposisi, dan delegasi. Ini lebih eksplisit daripada di C ++, dan oleh karena itu lebih canggung untuk digunakan tetapi akan memberi Anda sesuatu yang lebih mungkin Anda pahami pada pandangan pertama.

Tidak ada jawaban yang benar disini. Ada jawaban yang berbeda, dan mana yang lebih baik untuk situasi tertentu bergantung pada aplikasi dan preferensi individu.


12

Alasan utama (meskipun bukan satu-satunya) alasan orang menjauh dari MI adalah apa yang disebut "masalah berlian" yang menyebabkan ambiguitas dalam implementasi Anda. Ini artikel wikipedia membahas dan menjelaskan lebih baik daripada aku bisa. MI juga dapat menyebabkan kode yang lebih kompleks, dan banyak desainer OO mengklaim bahwa Anda tidak memerlukan MI, dan jika Anda menggunakannya, model Anda mungkin salah. Saya tidak yakin saya setuju dengan poin terakhir ini, tetapi menjaga hal-hal sederhana selalu merupakan rencana yang baik.


8

Dalam C ++ multiple inheritance merupakan sakit kepala besar bila digunakan secara tidak benar. Untuk menghindari masalah desain populer itu, beberapa antarmuka "warisan" dipaksa sebagai gantinya dalam bahasa modern (java, C #).


8

Warisan Ganda adalah

  • sulit dimengerti
  • sulit untuk di-debug (misalnya, jika Anda mencampur kelas dari beberapa kerangka kerja yang memiliki metode dengan nama yang sama jauh di lubuk hati, sinergi yang tidak terduga dapat terjadi)
  • mudah disalahgunakan
  • tidak benar-benar yang berguna
  • sulit diterapkan, terutama jika Anda ingin melakukannya dengan benar dan efisien

Oleh karena itu, dapat dianggap sebagai pilihan bijak untuk tidak memasukkan Warisan Ganda ke dalam bahasa Java.


3
Saya tidak setuju dengan semua hal di atas, karena telah bekerja dengan Common Lisp Object System.
David Thornley

2
Bahasa dinamis tidak dihitung ;-) Dalam sistem seperti Lisp, Anda memiliki REPL yang membuat debugging jadi lebih mudah. Juga, CLOS (seperti yang saya mengerti, saya tidak pernah benar-benar menggunakannya, hanya membacanya) adalah meta-object-system, dengan banyak fleksibilitas dan sikap roll-your-own. Tetapi pertimbangkan bahasa yang dikompilasi secara statis seperti C ++, di mana kompilator menghasilkan beberapa pencarian metode yang sangat rumit menggunakan beberapa vtables (mungkin tumpang tindih): dalam implementasi seperti itu, mencari tahu implementasi metode apa yang dipanggil mungkin merupakan tugas yang tidak terlalu sepele.
mfx

5

Alasan lain adalah bahwa pewarisan tunggal membuat casting menjadi sepele, tanpa mengeluarkan instruksi assembler (selain memeriksa kompatibilitas jenis yang diperlukan). Jika Anda memiliki multiple-inheritance, Anda perlu mencari tahu di mana di kelas anak induk tertentu dimulai. Jadi kinerja tentu saja merembes (meski bukan satu-satunya).


4

Kembali ke masa lalu (70-an) ketika Ilmu Komputer lebih banyak Ilmu dan kurang produksi massal programmer punya waktu untuk memikirkan desain yang baik dan implementasi yang baik dan sebagai hasilnya produk (program) memiliki kualitas tinggi (misalnya desain TCP / IP dan implementasi). Saat ini, ketika semua orang memprogram, dan manajer mengubah spesifikasi sebelum tenggat waktu, masalah halus seperti yang dijelaskan di tautan wikipedia dari posting Steve Haigh sulit dilacak; oleh karena itu, "multiple inheritance" dibatasi oleh desain kompilator. Jika Anda suka, Anda masih dapat menggunakan C ++ .... dan memiliki semua kebebasan yang Anda inginkan :)


4
... termasuk kebebasan untuk menembak diri sendiri di kaki, beberapa kali;)
Mario Ortegón

4

Saya mengambil pernyataan bahwa "Warisan ganda tidak diperbolehkan di Java" dengan sedikit garam.

Beberapa Warisan didefinisikan ketika "Jenis" mewarisi dari lebih dari satu "Jenis". Dan antarmuka juga diklasifikasikan sebagai tipe karena memiliki perilaku. Jadi Java memang memiliki banyak warisan. Hanya saja itu lebih aman.


6
Tetapi Anda tidak mewarisi dari sebuah antarmuka, Anda menerapkannya. Antarmuka bukanlah kelas.
Blorgbeard keluar

2
Ya, tetapi warisan tidak pernah didefinisikan secara sempit, kecuali dalam beberapa buku pendahuluan. Dan saya pikir kita harus melihat melampaui tata bahasanya. Keduanya melakukan hal yang sama, dan antarmuka "diciptakan" hanya untuk alasan itu.
Rig Veda

Ambil antarmuka Closeable: satu metode, close (). Misalkan Anda ingin memperluasnya, ke Openable: ini memiliki metode open () yang menyetel bidang boolean isOpen ke true. Mencoba membuka Openable yang sudah terbuka akan melempar Exception, seperti halnya mencoba menutup Openable yang tidak terbuka ... Ratusan silsilah kelas yang lebih menarik mungkin ingin mengadopsi fungsi tersebut ... tanpa harus memilih untuk memperluas masing-masing waktu dari kelas Dapat dibuka. Jika Openable hanyalah sebuah antarmuka, ia tidak dapat menyediakan fungsionalitas ini. QED: antarmuka tidak memberikan warisan!
mike rodent

4

Pemuatan kelas yang dinamis membuat implementasi multiple inheritance menjadi sulit.

Di java sebenarnya mereka menghindari kompleksitas multiple inheritance dengan menggunakan single inheritance dan interface. Kompleksitas multiple inheritance sangat tinggi dalam situasi seperti yang dijelaskan di bawah ini

masalah berlian dari banyak warisan. Kami memiliki dua kelas B dan C yang diturunkan dari A. Asumsikan bahwa B dan C menimpa metode yang diwariskan dan mereka menyediakan implementasinya sendiri. Sekarang D mewarisi dari B dan C melakukan multiple inheritance. D harus mewarisi metode yang ditimpa, jvm tidak dapat memutuskan metode mana yang akan digunakan?

Di c ++ fungsi virtual digunakan untuk menangani dan kita harus melakukannya secara eksplisit.

Ini dapat dihindari dengan menggunakan antarmuka, tidak ada badan metode. Antarmuka tidak dapat dibuat instance-nya — ia hanya dapat diterapkan oleh kelas atau diperluas oleh antarmuka lain.


3

Sebenarnya multiple inheritance akan menimbulkan kompleksitas jika kelas yang diturunkan memiliki fungsi yang sama. yaitu kompilator akan bingung mana yang harus dipilih (masalah berlian). Jadi di Java kompleksitas itu dihapus dan memberi antarmuka untuk mendapatkan fungsionalitas seperti yang diberikan oleh banyak warisan. Kita bisa menggunakan interface


2

Java memiliki konsep yaitu polimorfisme. Ada 2 jenis polimorfisme di Jawa. Ada metode overloading dan metode overriding. Di antara mereka, penggantian metode terjadi dengan hubungan super- dan subclass. Jika kita membuat objek subclass dan menjalankan metode superclass, dan jika subclass memanjang lebih dari satu kelas, metode kelas super mana yang harus dipanggil?

Atau, saat memanggil konstruktor superclass super(), konstruktor kelas super mana yang akan dipanggil?

Keputusan ini tidak mungkin dilakukan oleh fitur API java saat ini. jadi multiple inheritance tidak diperbolehkan di java.


Pada dasarnya, sistem tipe Java mengasumsikan bahwa setiap instance objek memiliki satu tipe, mentransmisikan objek ke supertipe akan selalu berfungsi dan mempertahankan referensi, dan mentransmisikan referensi ke subtipe akan mempertahankan referensi jika instance adalah subtipe itu atau subtipe daripadanya. Asumsi seperti itu berguna, dan menurut saya tidak mungkin mengizinkan beberapa pewarisan umum dengan cara yang konsisten dengannya.
supercat

1

Warisan Ganda tidak diizinkan di Java secara langsung, tetapi melalui antarmuka diizinkan.

Alasan:

Warisan Ganda: Memperkenalkan lebih banyak kompleksitas dan ambiguitas.

Antarmuka: Antarmuka adalah kelas yang sepenuhnya abstrak di Java yang memberi Anda cara seragam untuk menggambarkan struktur atau cara kerja bagian dalam program Anda dengan benar dari antarmuka yang tersedia untuk umum, dengan konsekuensi menjadi lebih banyak fleksibilitas dan kode yang dapat digunakan kembali serta lebih banyak kontrol tentang cara Anda membuat dan berinteraksi dengan kelas lain.

Lebih tepatnya, mereka adalah konstruksi khusus di Java dengan karakteristik tambahan yang memungkinkan Anda untuk melakukan semacam pewarisan berganda yaitu kelas yang dapat dikirim ke lebih dari satu kelas.

Mari kita ambil contoh sederhana.

  1. Misalkan ada 2 kelas super kelas A dan B dengan nama metode yang sama tetapi fungsionalitas yang berbeda. Melalui kode berikut dengan (memperluas) beberapa pewarisan kata kunci tidak mungkin.

       public class A                               
         {
           void display()
             {
               System.out.println("Hello 'A' ");
             }
         }
    
       public class B                               
          {
            void display()
              {
                System.out.println("Hello 'B' ");
              }
          }
    
      public class C extends A, B    // which is not possible in java
        {
          public static void main(String args[])
            {
              C object = new C();
              object.display();  // Here there is confusion,which display() to call, method from A class or B class
            }
        }
  2. Tetapi melalui antarmuka, dengan (mengimplementasikan) kata kunci banyak pewarisan dimungkinkan.

    interface A
        {
           // display()
        }
    
    
     interface B
        {
          //display()
        }
    
     class C implements A,B
        {
           //main()
           C object = new C();
           (A)object.display();     // call A's display
    
           (B)object.display(); //call B's display
        }
    }

0

Adakah yang bisa memberi tahu saya dengan tepat mengapa itu tidak diizinkan?

Anda dapat menemukan jawabannya dari tautan dokumentasi ini

Salah satu alasan mengapa bahasa pemrograman Java tidak mengizinkan Anda untuk memperluas lebih dari satu kelas adalah untuk menghindari masalah pewarisan status berganda, yang merupakan kemampuan untuk mewarisi bidang dari beberapa kelas.

Jika beberapa pewarisan diperbolehkan dan saat Anda membuat objek dengan membuat instance kelas itu, objek itu akan mewarisi bidang dari semua kelas super kelas. Ini akan menyebabkan dua masalah.

  1. Bagaimana jika metode atau konstruktor dari kelas super berbeda membuat instance bidang yang sama?

  2. Metode atau konstruktor mana yang akan diutamakan?

Meskipun beberapa pewarisan status sekarang diizinkan, Anda tetap dapat menerapkan

Jenis warisan berganda : Kemampuan kelas untuk mengimplementasikan lebih dari satu antarmuka.

Beberapa pewarisan implementasi (melalui metode default di antarmuka): Kemampuan untuk mewarisi definisi metode dari beberapa kelas

Lihat pertanyaan SE terkait ini untuk info tambahan:

Beberapa Ambiguitas Warisan dengan Antarmuka


0

Dalam C ++ kelas dapat mewarisi (secara langsung atau tidak langsung) dari lebih dari satu kelas, yang disebut sebagai warisan berganda .

C # dan Java, bagaimanapun, membatasi kelas ke satu warisan yang diwarisi setiap kelas dari kelas induk tunggal.

Beberapa pewarisan adalah cara yang berguna untuk membuat kelas yang menggabungkan aspek dari dua hierarki kelas yang berbeda, sesuatu yang sering terjadi saat menggunakan kerangka kelas yang berbeda dalam satu aplikasi.

Jika dua kerangka kerja menentukan kelas dasarnya sendiri untuk pengecualian, misalnya, Anda dapat menggunakan beberapa pewarisan untuk membuat kelas pengecualian yang dapat digunakan dengan salah satu kerangka kerja.

Masalah dengan multiple inheritance adalah hal itu dapat menyebabkan ambiguitas. Contoh klasiknya adalah ketika sebuah kelas mewarisi dari dua kelas lainnya, yang masing-masing mewarisi dari kelas yang sama:

class A {
    protected:
    bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
    public:
    void setFlag( bool nflag ){
        flag = nflag; // ambiguous
    }
};

Dalam contoh ini, flaganggota data ditentukan oleh class A. Tapi class Dturun dari class B dan class C, yang keduanya berasal dari A, jadi pada dasarnya dua salinan dari flagyang tersedia karena dua contoh Adalam Dhirarki kelas 's. Mana yang mau Anda setel? Kompilator akan mengeluh bahwa referensi ke flagdalam Dbersifat ambigu . Salah satu cara memperbaikinya adalah dengan menguraikan referensi secara eksplisit:

B::flag = nflag;

Perbaikan lainnya adalah dengan mendeklarasikan B dan C sebagai virtual base classes , yang berarti bahwa hanya satu salinan A yang dapat berada dalam hierarki, menghilangkan ambiguitas.

Kompleksitas lain ada dengan beberapa pewarisan, seperti urutan di mana kelas dasar diinisialisasi saat objek turunan dibangun, atau cara anggota dapat secara tidak sengaja disembunyikan dari kelas turunan. Untuk menghindari kerumitan ini, beberapa bahasa membatasi diri pada model pewarisan tunggal yang lebih sederhana.

Meskipun ini sangat menyederhanakan pewarisan, kegunaannya juga terbatas karena hanya kelas dengan leluhur yang sama yang dapat berbagi perilaku. Antarmuka mengurangi pembatasan ini dengan mengizinkan kelas dalam hierarki yang berbeda untuk mengekspos antarmuka umum meskipun tidak diimplementasikan dengan berbagi kode.


-1

Bayangkan Contoh ini: Saya punya kelas Shape1

Ini memiliki CalcualteAreametode:

Class Shape1
{

 public void CalculateArea()

     {
       //
     }
}

Ada kelas lain Shape2yang juga memiliki metode yang sama

Class Shape2
{

 public void CalculateArea()

     {

     }
}

Sekarang saya memiliki Circle kelas anak, itu berasal dari Shape1 dan Shape2;

public class Circle: Shape1, Shape2
{
}

Sekarang ketika saya membuat objek untuk Circle, dan memanggil metode tersebut, sistem tidak tahu metode menghitung luas mana yang akan dipanggil. Keduanya memiliki tanda tangan yang sama. Jadi compiler akan bingung. Itu sebabnya banyak warisan tidak diperbolehkan.

Tetapi mungkin ada banyak antarmuka karena antarmuka tidak memiliki definisi metode. Meskipun kedua antarmuka memiliki metode yang sama, keduanya tidak memiliki implementasi apa pun dan selalu metode di kelas anak akan dijalankan.


desainer bahasa dapat memberikan panggilan metode eksplisit seperti yang mereka lakukan di Interface. Tidak ada gunanya! Alasan lain adalah bagaimana dengan kelas abstrak dengan metode abstrak sekarang bagaimana Anda mempertimbangkannya?
Nomi Ali
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.