Mengapa kelas abstrak yang mengimplementasikan antarmuka bisa melewatkan deklarasi / implementasi salah satu metode antarmuka?

123

Suatu hal yang aneh terjadi di Java ketika Anda menggunakan kelas abstrak untuk mengimplementasikan sebuah antarmuka: beberapa metode antarmuka bisa hilang sama sekali (yaitu tidak ada deklarasi abstrak atau implementasi aktual), tetapi kompilator tidak mengeluh.

Misalnya, antarmuka yang diberikan:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

kelas abstrak berikut dikompilasi dengan riang tanpa peringatan atau kesalahan:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

Bisakah Anda menjelaskan mengapa?

Giulio Piancastelli
sumber
2
Seseorang tidak dapat membuat objek dari kelas abstrak. Jadi, selama implementasi tidak disediakan untuk kelas abstrak, objek tidak dapat dibuat untuk IAnything. Jadi ini baik-baik saja untuk kompiler. Compiler mengharapkan bahwa, setiap kelas non-abstrak yang mengimplementasikan IAnything harus mengimplementasikan semua metode yang dideklarasikan dari IAnything. Dan karena seseorang harus memperluas dan mengimplementasikan AbstractThing untuk dapat membuat objek, compiler akan menampilkan error, jika implementasi tersebut tidak mengimplementasikan metode IAnything yang ditinggalkan oleh AbstractThing.
VanagaS
Saya memiliki kelas konkret yang memperluas "AbstractThing" -nya sendiri dalam skenario yang identik dengan ini, dan meskipun saya belum mengimplementasikan salah satu metode dalam antarmuka, kompilasi itu tidak dapat dijelaskan. Sekarang ia melakukan apa yang saya harapkan, tetapi saya tidak tahu apa yang menyebabkannya berhasil sebelumnya. Saya kira saya tidak memiliki :wsalah satu file.
Braden Best
Anda dapat melihat jawaban untuk pertanyaan serupa stackoverflow.com/questions/8026580/…
Do Nhu Vy

Jawaban:

156

Itu karena jika suatu kelas abstrak, maka menurut definisi Anda diminta untuk membuat subkelasnya untuk dibuat instance-nya. Subclass akan diminta (oleh compiler) untuk mengimplementasikan metode antarmuka apa pun yang ditinggalkan oleh class abstrak.

Mengikuti kode contoh Anda, coba buat subclass AbstractThingtanpa mengimplementasikan m2metode dan lihat kesalahan apa yang diberikan compiler kepada Anda. Ini akan memaksa Anda untuk menerapkan metode ini.

Bill the Lizard
sumber
1
Saya pikir kompiler harus tetap memberikan peringatan mengenai kelas abstrak yang mengimplementasikan antarmuka secara tidak lengkap, hanya karena Anda perlu melihat melalui 2 definisi kelas daripada 1 untuk melihat apa yang Anda butuhkan dalam subkelas. Ini adalah batasan bahasa / kompiler.
workmad3
3
Itu bukan ide yang bagus, karena biasanya ada banyak kelas abstrak dan peringatan 'palsu' akan segera membanjiri Anda, menyebabkan Anda melewatkan peringatan 'sebenarnya'. Jika Anda memikirkannya, kata kunci 'abstrak' ada di sana secara khusus untuk memberi tahu kompiler untuk menekan peringatan untuk kelas itu.
belugabob
4
@workmad - jika Anda memiliki implementasi umum untuk subset metode antarmuka, lebih masuk akal untuk memfaktorkannya ke dalam kelas dasar yang terpisah (KERING mengalahkan kode satu tempat)
Gishu
4
Akan berbahaya jika Anda meminta Anda untuk meletakkan implementasi metode kosong di kelas abstrak. Jika Anda melakukannya, maka pelaksana subkelas akan mewarisi non-perilaku ini tanpa kompilator memberi tahu mereka bahwa ada masalah.
Bill the Lizard
8
Saya pikir apa yang mungkin disarankan oleh workmad adalah Anda mendefinisikan metode dalam kelas abstrak tanpa badan metode dan menandainya sebagai abstrak. Sepertinya bukan ide yang buruk bagiku.
Dónal
33

Sangat baik.
Anda tidak dapat membuat instance kelas abstrak .. tetapi kelas abstrak dapat digunakan untuk menampung implementasi umum untuk m1 () dan m3 ().
Jadi jika implementasi m2 () berbeda untuk setiap implementasi tetapi m1 dan m3 tidak. Anda dapat membuat berbagai implementasi beton yang berbeda hanya dengan implementasi m2 yang berbeda dan berasal dari AbstractThing - dengan menghormati prinsip DRY. Memvalidasi jika antarmuka diimplementasikan sepenuhnya untuk kelas abstrak adalah sia-sia ..

Pembaruan : Menariknya, saya menemukan bahwa C # memberlakukan ini sebagai kesalahan kompilasi. Anda dipaksa untuk menyalin tanda tangan metode dan mengawalnya dengan 'publik abstrak' di kelas dasar abstrak dalam skenario ini .. (sesuatu yang baru setiap hari :)

Gishu
sumber
7

Tidak apa-apa. Untuk memahami hal di atas, Anda harus memahami hakikat kelas abstrak terlebih dahulu. Mereka mirip dengan antarmuka dalam hal itu. Inilah yang dikatakan Oracle tentang ini di sini .

Kelas abstrak mirip dengan antarmuka. Anda tidak dapat membuat instance, dan mereka mungkin berisi campuran metode yang dideklarasikan dengan atau tanpa implementasi.

Jadi, Anda harus memikirkan tentang apa yang terjadi ketika sebuah antarmuka memperluas antarmuka lain. Sebagai contoh ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

... seperti yang Anda lihat, ini juga dikompilasi dengan baik. Hanya karena, seperti kelas abstrak, sebuah antarmuka TIDAK dapat digunakan. Jadi, tidak perlu secara eksplisit menyebutkan metode dari "induknya". Namun, SEMUA tanda tangan metode induk DO secara implisit menjadi bagian dari antarmuka perluasan atau mengimplementasikan kelas abstrak. Jadi, sekali kelas yang tepat (yang bisa dipakai) memperluas di atas, itu AKAN diperlukan untuk memastikan bahwa setiap metode abstrak diimplementasikan.

Semoga membantu ... dan Allahu 'alam!

Berterimakasih
sumber
Itu sudut pandang yang menarik. Itu membuat saya berpikir bahwa "kelas abstrak" sebenarnya adalah "antarmuka konkret", yaitu antarmuka dengan beberapa metode konkret, daripada kelas dengan beberapa metode abstrak.
Giulio Piancastelli
... sedikit dari keduanya. Tapi satu hal yang pasti, mereka tidak bisa dipakai.
Berterima kasih
4

Interface berarti kelas yang tidak mengimplementasikan metodenya, tetapi hanya dengan deklarasi.
Di sisi lain, kelas abstrak adalah kelas yang dapat menerapkan beberapa metode bersama dengan beberapa metode hanya dengan deklarasi, tanpa implementasi.
Ketika kita mengimplementasikan sebuah antarmuka ke kelas abstrak, itu berarti kelas abstrak mewarisi semua metode antarmuka. Karena, tidak penting untuk mengimplementasikan semua metode dalam kelas abstrak namun ia datang ke kelas abstrak (dengan pewarisan juga), sehingga kelas abstrak dapat meninggalkan beberapa metode dalam antarmuka tanpa implementasi di sini. Tetapi, ketika kelas abstrak ini akan diwarisi oleh beberapa kelas konkret, mereka harus mengimplementasikan semua metode yang tidak diimplementasikan di kelas abstrak.

Mustakimur Rahman
sumber
4

Diberikan antarmuka:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

Beginilah sebenarnya Java melihatnya:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

Jadi Anda dapat membiarkan beberapa (atau semua) abstractmetode ini tidak diimplementasikan, seperti yang akan Anda lakukan dalam kasus abstractkelas memperluas abstractkelas lain .

Bila Anda implementseorang interface, aturan bahwa semua interfacemetode harus dilaksanakan di berasal class, hanya berlaku untuk beton classpelaksanaan (yaitu, yang tidak abstractsendiri).

Jika Anda memang berencana untuk abstract classmembuatnya, maka tidak ada aturan yang mengatakan Anda telah menggunakan implementsemua interfacemetode (perhatikan bahwa dalam kasus seperti itu, wajib menyatakan turunan classsebagai abstract)

sharhp
sumber
Menggunakan javap IAnything.classuntuk menghasilkan cuplikan kode kedua.
sharhp
3

Ketika Kelas Abstrak Mengimplementasikan Antarmuka

Pada bagian Antarmuka, dicatat bahwa kelas yang mengimplementasikan antarmuka harus mengimplementasikan semua metode antarmuka. Namun, dimungkinkan untuk mendefinisikan kelas yang tidak mengimplementasikan semua metode antarmuka, asalkan kelas tersebut dinyatakan abstrak. Sebagai contoh,

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

Dalam hal ini, kelas X harus abstrak karena tidak sepenuhnya mengimplementasikan Y, tetapi kelas XX sebenarnya mengimplementasikan Y.

Referensi: http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

Do Nhu Vy
sumber
1

Kelas abstrak tidak diperlukan untuk mengimplementasikan metode. Jadi meskipun itu mengimplementasikan sebuah antarmuka, metode abstrak dari antarmuka dapat tetap abstrak. Jika Anda mencoba mengimplementasikan antarmuka dalam kelas konkret (yaitu bukan abstrak) dan Anda tidak mengimplementasikan metode abstrak, kompilator akan memberi tahu Anda: Implementasikan metode abstrak atau deklarasikan kelas sebagai abstrak.

Vincent Ramdhanie
sumber