Di java, Anda harus secara eksplisit melakukan cast untuk menurunkan variabel
public class Fruit{} // parent class
public class Apple extends Fruit{} // child class
public static void main(String args[]) {
// An implicit upcast
Fruit parent = new Apple();
// An explicit downcast to Apple
Apple child = (Apple)parent;
}
Apakah ada alasan untuk persyaratan ini, selain dari fakta bahwa java tidak melakukan inferensi tipe apa pun?
Apakah ada "gotcha" dengan menerapkan downcasting otomatis dalam bahasa baru?
Misalnya:
Apple child = parent; // no cast required
object-oriented
type-inference
language-design
Sam Washburn
sumber
sumber
Jawaban:
Orang yang terbuang selalu berhasil.
Downcasts dapat menyebabkan kesalahan runtime, ketika tipe objek runtime bukan subtipe dari tipe yang digunakan dalam cetakan.
Karena yang kedua adalah operasi yang berbahaya, sebagian besar bahasa pemrograman yang diketik memerlukan programmer untuk secara eksplisit memintanya. Pada dasarnya, programmer memberitahu kompiler "percayalah, saya tahu lebih baik - ini akan baik-baik saja pada saat runtime".
Ketika jenis sistem prihatin, upcast menempatkan beban bukti pada kompiler (yang harus memeriksanya secara statis), downcast meletakkan beban bukti pada programmer (yang harus berpikir keras tentang hal itu).
Orang bisa berpendapat bahwa bahasa pemrograman yang dirancang dengan baik akan melarang sepenuhnya downcast, atau memberikan alternatif gips yang aman, misalnya mengembalikan jenis opsional
Option<T>
. Namun, banyak bahasa yang tersebar luas memilih pendekatan yang lebih sederhana dan lebih pragmatis untuk sekadar mengembalikanT
dan memunculkan kesalahan.Dalam contoh spesifik Anda, kompiler dapat dirancang untuk menyimpulkan bahwa
parent
sebenarnya merupakanApple
analisis statis sederhana, dan memungkinkan pemeran tersirat. Namun, secara umum masalahnya tidak dapat dipastikan, jadi kami tidak dapat mengharapkan kompiler melakukan terlalu banyak sihir.sumber
Biasanya downcasting adalah apa yang Anda lakukan ketika pengetahuan yang diketahui secara statis tentang kompiler tentang jenis sesuatu kurang spesifik dari apa yang Anda ketahui (atau setidaknya harapan).
Dalam situasi seperti contoh Anda, objek dibuat sebagai
Apple
dan kemudian pengetahuan itu dibuang dengan menyimpan referensi dalam variabel tipeFruit
. Maka Anda ingin menggunakan referensi yang sama denganApple
lagi.Karena informasi itu hanya dibuang "secara lokal", tentu saja, kompiler dapat mempertahankan pengetahuan yang
parent
benar-benar sebuahApple
, meskipun jenis yang dinyatakannya adalahFruit
.Tetapi biasanya tidak ada yang melakukan itu. Jika Anda ingin membuat
Apple
dan menggunakannya sebagaiApple
, Anda menyimpannya dalamApple
variabel, bukan variabelFruit
.Ketika Anda memiliki
Fruit
dan ingin menggunakannya sebagaiApple
biasanya berarti Anda memperolehFruit
melalui beberapa cara yang umumnya dapat mengembalikan segala jenisFruit
, tetapi dalam hal ini Anda tahu itu adalahApple
. Hampir selalu Anda tidak hanya membangunnya, Anda telah melewatinya dengan beberapa kode lain.Contoh yang jelas adalah jika saya memiliki
parseFruit
fungsi yang dapat mengubah string seperti "apel", "oranye", "lemon", dll, menjadi subkelas yang sesuai; umumnya yang kita (dan kompiler) dapat tahu tentang fungsi ini adalah bahwa ia mengembalikan beberapa jenisFruit
, tetapi jika saya meneleponparseFruit("apple")
maka saya tahu itu akan memanggilApple
dan mungkin ingin menggunakanApple
metode, jadi saya bisa downcast.Sekali lagi kompiler yang cukup pintar dapat mengetahui hal itu di sini dengan menguraikan kode sumber untuk
parseFruit
, karena saya memanggilnya dengan konstanta (kecuali ada di modul lain dan kami memiliki kompilasi terpisah, seperti di Jawa). Tetapi Anda harus dapat dengan mudah melihat bagaimana contoh yang lebih rumit yang melibatkan informasi dinamis dapat menjadi lebih sulit (atau bahkan tidak mungkin!) Bagi kompiler untuk memverifikasi.Dalam kode downcasts realistis biasanya terjadi di mana kompiler tidak dapat memverifikasi bahwa downcast aman menggunakan metode generik, dan tidak dalam kasus sederhana seperti segera setelah upcast membuang informasi jenis yang sama yang kita coba untuk kembali dengan downcasting.
sumber
Ini masalah di mana Anda ingin menarik garis. Anda dapat merancang bahasa yang akan mendeteksi validitas downcast implisit:
Sekarang, mari kita ekstrak metode:
Kami masih bagus. Analisis statis jauh lebih sulit tetapi masih memungkinkan.
Tetapi masalah muncul begitu seseorang menambahkan:
Sekarang, di mana Anda ingin meningkatkan kesalahan? Ini menciptakan dilema, orang bisa mengatakan bahwa masalahnya ada
Apple child = parent;
, tetapi ini bisa dibantah oleh "Tapi itu berhasil sebelumnya". Dari sisi lain, menambahkaneat(trouble);
menyebabkan masalah ", tetapi seluruh titik polimorfisme adalah untuk memungkinkan hal itu.Dalam hal ini, Anda dapat melakukan beberapa pekerjaan programmer, tetapi Anda tidak dapat melakukan semuanya. Semakin jauh Anda mengambilnya sebelum menyerah, semakin sulit untuk menjelaskan apa yang salah. Jadi, lebih baik berhenti sesegera mungkin, sesuai dengan prinsip kesalahan pelaporan awal.
BTW, di Jawa downcast yang Anda jelaskan sebenarnya bukan downcast. Ini adalah pemeran umum, yang bisa melempar apel ke jeruk juga. Jadi, secara teknis ide @ chi sudah ada di sini, tidak ada downcast di Jawa, hanya "everycasts". Masuk akal untuk merancang operator downcast khusus yang akan melempar kesalahan kompilasi ketika tipe hasil itu tidak dapat ditemukan hilir dari tipe argumen itu. Ini akan menjadi ide yang baik untuk membuat "everycast" jauh lebih sulit untuk digunakan untuk mencegah programmer menggunakannya tanpa alasan yang sangat bagus.
XXXXX_cast<type>(argument)
Sintaks C ++ muncul di pikiran.sumber