PEMBARUAN : PHP 7.4 sekarang mendukung kovariansi dan pelanggaran yang membahas masalah utama yang diangkat dalam pertanyaan ini.
Saya telah mengalami sesuatu masalah dengan menggunakan tipe return yang mengisyaratkan di PHP 7. Pemahaman saya adalah bahwa mengisyaratkan : self
berarti Anda bermaksud agar kelas pelaksana mengembalikan dirinya sendiri. Oleh karena itu saya menggunakan : self
antarmuka saya untuk menunjukkan itu, tetapi ketika saya mencoba untuk benar-benar menerapkan antarmuka saya mendapat kesalahan kompatibilitas.
Berikut ini adalah demonstrasi sederhana dari masalah yang saya alami:
interface iFoo
{
public function bar (string $baz) : self;
}
class Foo implements iFoo
{
public function bar (string $baz) : self
{
echo $baz . PHP_EOL;
return $this;
}
}
(new Foo ()) -> bar ("Fred")
-> bar ("Wilma")
-> bar ("Barney")
-> bar ("Betty");
Output yang diharapkan adalah:
Fred Wilma Barney Betty
Apa yang sebenarnya saya dapatkan adalah:
PHP Fatal error: Deklarasi Foo :: bar (int $ baz): Foo harus kompatibel dengan iFoo :: bar (int $ baz): iFoo di test.php pada baris 7
Masalahnya adalah Foo adalah implementasi dari iFoo, sejauh yang saya tahu implementasinya harus kompatibel dengan antarmuka yang diberikan. Saya mungkin dapat memperbaiki masalah ini dengan mengubah antarmuka atau kelas pelaksana (atau keduanya) untuk mengembalikan petunjuk antarmuka dengan nama daripada menggunakan self
, tetapi pemahaman saya adalah bahwa secara semantik self
berarti "mengembalikan instance kelas yang baru saja Anda panggil metode ini ". Oleh karena itu mengubahnya ke antarmuka berarti dalam teori bahwa saya dapat mengembalikan contoh apa pun dari sesuatu yang mengimplementasikan antarmuka ketika maksud saya adalah untuk contoh yang dipanggil adalah apa yang akan dikembalikan.
Apakah ini kekeliruan di PHP atau apakah ini keputusan desain yang disengaja? Jika yang pertama, apakah ada kemungkinan melihatnya diperbaiki di PHP 7.1? Jika tidak, lalu apa cara yang benar untuk kembali yang mengisyaratkan bahwa antarmuka Anda mengharapkan Anda untuk mengembalikan contoh yang baru saja Anda panggil metode untuk merangkai?
sumber
self
jenis pengembalian seharusnya bekerja?self
mengartikan "Kembalikan contoh yang Anda panggil ini, dan bukan contoh lain yang mengimplementasikan antarmuka yang sama". Saya sepertinya ingat Java memiliki tipe pengembalian yang serupa (meskipun sudah lama sejak saya melakukan pemrograman Java)Jawaban:
self
tidak mengacu pada instance, itu mengacu pada kelas saat ini. Tidak ada cara bagi antarmuka untuk menentukan bahwa contoh yang sama harus dikembalikan - menggunakanself
cara yang Anda coba hanya akan memaksakan bahwa contoh yang dikembalikan berada dari kelas yang sama.Karena itu, deklarasi tipe kembalian dalam PHP harus invarian sementara yang Anda coba adalah kovarian.
Penggunaan Anda
self
setara dengan:interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
yang tidak diperbolehkan.
The Kembali Jenis Deklarasi RFC memiliki ini untuk mengatakan :
Untuk saat ini, setidaknya yang terbaik yang dapat Anda lakukan adalah:
interface iFoo { public function bar (string $baz) : iFoo; } class Foo implements iFoo { public function bar (string $baz) : iFoo {...} }
sumber
static
untuk bekerja, tetapi itu bahkan tidak dikenalistatic
ditambahkan ke PHP 8.Ini juga bisa menjadi solusi, bahwa Anda tidak mendefinisikan secara eksplisit jenis kembalian di Antarmuka, hanya di PHPDoc dan kemudian Anda dapat menentukan jenis kembalian tertentu dalam implementasi:
interface iFoo { public function bar (string $baz); } class Foo implements iFoo { public function bar (string $baz) : Foo {...} }
sumber
Foo
hanya digunakanself
.Dalam kasus, ketika Anda ingin memaksa dari antarmuka, metode itu akan mengembalikan objek, tetapi tipe objek bukan tipe antarmuka, tetapi kelas itu sendiri, maka Anda dapat menulisnya seperti ini:
interface iFoo { public function bar (string $baz) : object; } class Foo implements iFoo { public function bar (string $baz) : self {...} }
Ini berfungsi sejak PHP 7.4.
sumber
PHP 8 akan menambahkan "tipe pengembalian statis" yang akan menyelesaikan masalah Anda.
Lihat RFC ini: https://wiki.php.net/rfc/static_return_type
sumber
Ini terlihat seperti perilaku yang diharapkan bagi saya.
Ubah saja
Foo::bar
metode Anda untuk kembali,iFoo
bukanself
dan selesai dengannya.Penjelasan:
self
seperti yang digunakan dalam antarmuka berarti "objek bertipeiFoo
".self
seperti yang digunakan dalam implementasi berarti "objek bertipeFoo
".Oleh karena itu, jenis kembalian di antarmuka dan implementasinya jelas tidak sama.
Salah satu komentar menyebutkan Java dan apakah Anda akan mengalami masalah ini. Jawabannya adalah ya, Anda akan memiliki masalah yang sama jika Java mengizinkan Anda menulis kode seperti itu - padahal sebenarnya tidak. Karena Java mengharuskan Anda menggunakan nama jenis alih-alih
self
pintasan PHP , Anda tidak akan pernah benar-benar melihat ini. (Lihat di sini untuk diskusi tentang masalah serupa di Java.)sumber
self
, mirip dengan mendeklarasikanMyClass::class
?