Saya punya dua program kecil ini:
C
#include <stdio.h>
int main()
{
if (5) {
printf("true\n");
}
else {
printf("false\n");
}
return 0;
}
Jawa
class type_system {
public static void main(String args[]) {
if (5) {
System.out.println("true");
}
else {
System.out.println("false");
}
}
}
yang melaporkan pesan kesalahan:
type_system.java:4: error: incompatible types: int cannot be converted to boolean
if (5) {
^
1 error
Pemahaman saya
Sejauh ini, saya mengerti contoh ini sebagai demonstrasi dari sistem tipe yang berbeda. Ketik C lebih lemah dan memungkinkan konversi dari int ke boolean tanpa kesalahan. Java lebih kuat diketik dan gagal, karena tidak ada percakapan implisit yang diizinkan.
Karena itu, pertanyaan saya: Di mana saya salah paham?
Apa yang tidak saya cari
Pertanyaan saya tidak terkait dengan gaya pengkodean yang buruk. Saya tahu itu buruk, tapi saya tertarik mengapa C mengizinkannya dan Java tidak. Oleh karena itu, saya tertarik pada sistem tipe bahasa, khususnya kekuatannya.
java
c
type-systems
toogley
sumber
sumber
int
nilai. Contoh yang lebih tepat adalahif (pointer)
.Jawaban:
1. C dan Java adalah bahasa yang berbeda
Fakta bahwa mereka berperilaku berbeda seharusnya tidak terlalu mengejutkan.
2. C tidak melakukan konversi dari
int
kebool
Bagaimana mungkin? C bahkan tidak memiliki
bool
tipe yang benar untuk dikonversi hingga 1999 . C diciptakan pada awal 1970-an, danif
merupakan bagian dari itu sebelum itu bahkan C, kembali ketika itu hanya serangkaian modifikasi B 1 .if
bukan hanyaNOP
dalam C selama hampir 30 tahun. Itu langsung bertindak pada nilai numerik. Kata-kata dalam standar C ( tautan PDF ), bahkan lebih dari satu dekade setelah pengenalanbool
s ke C, masih menentukan perilakuif
(p 148) dan?:
(p 100) menggunakan istilah "tidak sama dengan 0" dan "sama dengan 0 "Daripada istilah Boolean" benar "atau" salah "atau yang serupa.Dengan ...
3. ... angka kebetulan sesuai dengan instruksi prosesor yang beroperasi.
JZ
danJNZ
instruksi perakitan x86 dasar Anda untuk percabangan bersyarat. Singkatannya adalah " J ump if Z ero" dan " J ump if N ot Z ero". Setara dengan PDP-11, tempat asal C, adalahBEQ
(" B ranch if EQ ual") danBNE
(" B ranch if N ot E qual").Instruksi ini memeriksa apakah operasi sebelumnya menghasilkan nol atau tidak dan melompat (atau tidak) sesuai.
4. Java memiliki penekanan yang jauh lebih tinggi pada keselamatan daripada yang pernah dilakukan 2
Dan, dengan mempertimbangkan keselamatan, mereka memutuskan bahwa membatasi
if
keboolean
s sepadan dengan biayanya (keduanya menerapkan pembatasan seperti itu dan biaya peluang yang dihasilkan).1. B bahkan tidak memiliki tipe sama sekali. Bahasa assembly umumnya juga tidak. Namun B dan bahasa assembly berhasil menangani percabangan dengan baik.
2. Dalam kata-kata Dennis Ritchie ketika menggambarkan modifikasi yang direncanakan untuk B yang menjadi C (penekanan milikku):
sumber
C 2011 Draf Online
Perhatikan bahwa klausa ini hanya menentukan bahwa ekspresi pengontrol harus memiliki tipe skalar (
char
/short
/int
/long
/ dll.), Tidak secara khusus tipe Boolean. Cabang dijalankan jika ekspresi pengontrol memiliki nilai bukan nol.Bandingkan dengan
Spesifikasi Bahasa Java SE 8
Java, OTOH, secara khusus mensyaratkan bahwa ekspresi pengontrol dalam
if
pernyataan memiliki tipe Boolean.Jadi, ini bukan tentang pengetikan lemah vs. pengetikan, ini tentang apa yang masing-masing definisi bahasa tentukan sebagai ekspresi kontrol yang valid.
Edit
Adapun mengapa bahasa berbeda dalam hal ini, beberapa poin:
C diturunkan dari B, yang merupakan bahasa "tanpa huruf" - pada dasarnya, semuanya adalah kata 32-36-bit (tergantung pada perangkat keras), dan semua operasi aritmatika adalah operasi integer. Sistem tipe C melesat sedikit demi sedikit, sehingga ...
C tidak memiliki tipe Boolean yang berbeda sampai versi bahasa 1999. C cukup mengikuti konvensi B untuk menggunakan nol untuk mewakili
false
dan bukan nol untuk mewakilitrue
.Java pasca-tanggal C oleh beberapa dekade yang baik, dan dirancang khusus untuk mengatasi beberapa kekurangan C dan C ++. Tidak diragukan lagi memperketat pembatasan pada apa yang mungkin menjadi ekspresi kontrol dalam
if
pernyataan adalah bagian dari itu.Tidak ada alasan untuk mengharapkan setiap dua bahasa pemrograman untuk melakukan hal-hal dengan cara yang sama. Bahkan bahasa yang memiliki kaitan erat seperti C dan C ++ berbeda dalam beberapa cara yang menarik, sehingga Anda dapat memiliki program C legal yang bukan program C ++ legal, atau program C ++ legal tetapi dengan semantik berbeda, dll.
sumber
int
keboolean
sementara Java tidak. Jawabannya adalah tidak ada konversi seperti itu dalam C. Bahasa berbeda karena mereka adalah bahasa yang berbeda.Banyak jawaban tampaknya menargetkan ekspresi penugasan tertanam yang ada dalam ekspresi bersyarat. (Walaupun itu adalah jenis jebakan potensial yang diketahui, itu bukan sumber pesan kesalahan Java dalam kasus ini.)
Mungkin ini karena OP tidak mempublikasikan pesan kesalahan yang sebenarnya, dan,
^
tanda sisipan menunjuk langsung ke=
operator penugasan.Namun kompiler menunjuk ke
=
karena itu adalah operator yang menghasilkan nilai akhir (dan karenanya jenis terakhir) dari ekspresi yang dilihat oleh kondisi.Mengeluh tentang pengujian nilai non-boolean, dengan jenis kesalahan berikut:
Bilangan bulat pengujian, meskipun terkadang mudah, dianggap sebagai perangkap potensial yang harus dihindari oleh perancang Java. Setelah semua, Java memiliki tipe data boolean benar , yang C tidak tidak (tidak memiliki tipe boolean) .
Ini juga berlaku untuk pointer pengujian C untuk null / non-null via
if (p) ...
danif (!p) ...
, yang Java juga tidak mengizinkan sebaliknya membutuhkan operator pembanding eksplisit untuk mendapatkan boolean yang diperlukan.sumber
if
pernyataan itu.bool b = ...; int v = 5 + b;
. Ini berbeda dengan bahasa dengan tipe boolean penuh yang tidak dapat digunakan dalam aritmatika.int v = 5; float f = 2.0 + v;
legal di C._Bool
adalah salah satu tipe integer standar yang tidak ditandatangani sebagaimana didefinisikan oleh Standar (lihat 6.2.5 / 6).Ada dua bagian untuk pertanyaan Anda:
Mengapa Java tidak mengkonversi
int
keboolean
?Ini bermuara pada Jawa yang dimaksudkan untuk menjadi sejelas mungkin. Ini sangat statis, sangat "di wajahmu" dengan sistem tipenya. Hal-hal yang secara otomatis diketik-ketik dalam bahasa lain tidak demikian, di Jawa. Anda harus menulis
int a=(int)0.5
juga. Konversifloat
keint
akan kehilangan informasi; sama dengan mengonversiint
keboolean
dan karenanya akan rawan kesalahan. Juga, mereka harus menentukan banyak kombinasi. Tentu, hal-hal ini tampaknya jelas, tetapi mereka bermaksud berbuat salah di sisi kehati-hatian.Oh, dan dibandingkan dengan bahasa lain, Java sangat tepat dalam spesifikasinya, karena bytecode bukan hanya detail implementasi internal. Mereka harus menentukan interaksi apa saja dan semua, tepatnya. Usaha besar.
Mengapa
if
tidak menerima tipe lain selainboolean
?if
dapat dengan sempurna didefinisikan untuk memungkinkan jenis selainboolean
. Itu bisa memiliki definisi yang mengatakan yang berikut ini setara:true
int != 0
String
dengan.length>0
null
(dan bukanBoolean
dengan nilaifalse
).null
dan metode yangObject.check_if
(ditemukan oleh saya hanya untuk kesempatan ini) kembalitrue
.Mereka tidak; tidak ada kebutuhan nyata, dan mereka ingin memilikinya sekuat, statis, transparan, mudah dibaca dll. mungkin. Tidak ada fitur implisit. Juga, implementasinya akan sangat kompleks, saya yakin, harus menguji setiap nilai untuk semua kasus yang mungkin, jadi kinerja mungkin memainkan faktor kecil juga (Java dulu sloooow di komputer pada hari itu; ingat ada tidak ada kompiler JIT dengan rilis pertama, setidaknya tidak pada komputer yang saya gunakan saat itu).
Alasan yang lebih dalam
Alasan yang lebih dalam adalah fakta bahwa Jawa memiliki tipe primitifnya, karenanya sistem tipenya terbelah antara objek dan primitif. Mungkin, jika mereka menghindari itu, segalanya akan berubah dengan cara lain. Dengan aturan yang diberikan pada bagian sebelumnya, mereka harus mendefinisikan kebenaran dari setiap primitif secara eksplisit (karena primitif tidak berbagi kelas super, dan tidak ada yang didefinisikan dengan baik
null
untuk primitif). Ini akan berubah menjadi mimpi buruk, dengan cepat.Pandangan
Yah, dan pada akhirnya, mungkin itu hanya preferensi para perancang bahasa. Setiap bahasa tampaknya berputar sendiri di sana ...
Misalnya, Ruby tidak memiliki tipe primitif. Semuanya, secara harfiah segalanya, adalah objek. Mereka memiliki waktu yang sangat mudah untuk memastikan bahwa setiap objek memiliki metode tertentu.
Ruby memang mencari kebenaran pada semua jenis objek yang bisa Anda lemparkan padanya. Yang cukup menarik, masih tidak memiliki
boolean
tipe (karena tidak memiliki primitif), dan tidak memilikiBoolean
kelas juga. Jika Anda bertanya pada kelas berapa nilainyatrue
(tersedia dengan mudahtrue.class
), Anda dapatkanTrueClass
. Kelas itu sebenarnya memiliki metode, yaitu 4 operator untuk boolean (| & ^ ==
). Di sini,if
anggap falsey nilainya jika dan hanya jika salah satufalse
ataunil
(null
Ruby). Yang lainnya benar. Jadi,0
atau""
keduanya benar.Sepele bagi mereka untuk menciptakan metode
Object#truthy?
yang dapat diterapkan untuk kelas apa pun dan mengembalikan kebenaran individu. Misalnya,String#truthy?
bisa diterapkan untuk string yang tidak kosong, atau yang lainnya. Mereka tidak, meskipun Ruby adalah antitesis Jawa di sebagian besar departemen (mengetik bebek dinamis dengan mixin, membuka kembali kelas dan semua itu).Yang mungkin mengejutkan bagi seorang programmer Perl yang terbiasa
$value <> 0 || length($value)>0 || defined($value)
menjadi kebenaran. Dan seterusnya.Masukkan SQL dengan konvensi bahwa
null
di dalam setiap ekspresi secara otomatis membuatnya salah, apa pun yang terjadi. Jadi(null==null) = false
. Di Ruby(nil==nil) = true
,. Saat-saat bahagia.sumber
((int)3) * ((float)2.5)
cukup didefinisikan dengan baik di Jawa7.5f
.int
kefloat
juga kehilangan informasi secara umum. Apakah Java juga melarang pemeran tersirat tersebut?Selain jawaban bagus lainnya, saya ingin berbicara tentang konsistensi antar bahasa.
Ketika kita memikirkan pernyataan if yang murni secara matematis, kita memahami bahwa kondisinya dapat benar atau salah, tidak ada nilai lain. Setiap bahasa pemrograman utama menghormati ideal matematika ini; jika Anda memberikan nilai Boolean true / false pada pernyataan if, maka Anda dapat berharap untuk melihat perilaku yang konsisten dan intuitif sepanjang waktu.
Sejauh ini baik. Inilah yang Java implementasikan, dan hanya Java yang implementasikan.
Bahasa lain mencoba menghadirkan kenyamanan untuk nilai-nilai non-Boolean. Sebagai contoh:
n
bilangan bulat. Sekarang tentukanif (n)
sebagai singkatanif (n != 0)
.x
adalah angka floating-point. Sekarang tentukanif (x)
sebagai singkatanif (x != 0 && !isNaN(x))
.p
adalah tipe pointer. Sekarang tentukanif (p)
sebagai singkatanif (p != null)
.s
adalah tipe string. Sekarang tentukanif (s)
menjadiif (s != null && s != "")
.a
adalah tipe array. Sekarang tentukanif (a)
menjadiif (a != null && a.length > 0)
.Gagasan memberikan steno jika-tes ini tampaknya bagus di permukaan ... sampai Anda menemukan perbedaan dalam desain dan pendapat:
if (0)
diperlakukan sebagai false dalam C, Python, JavaScript; tetapi diperlakukan sebagai benar di Ruby.if ([])
diperlakukan sebagai false dalam Python tetapi benar dalam JavaScript.Setiap bahasa memiliki alasan sendiri yang baik untuk memperlakukan ekspresi dengan satu atau lain cara. (Misalnya, satu-satunya nilai palsu di Ruby adalah
false
dannil
, karenanya0
benar.)Java mengadopsi desain eksplisit untuk memaksa Anda untuk memasok nilai Boolean ke pernyataan if. Jika Anda dengan tergesa-gesa menerjemahkan kode dari C / Ruby / Python ke Java, Anda tidak dapat meninggalkan kelemahan jika-tes tidak berubah; Anda perlu menuliskan kondisi secara eksplisit di Jawa. Meluangkan waktu sejenak untuk berhenti dan berpikir dapat menyelamatkan Anda dari kesalahan yang ceroboh.
sumber
x != 0
sama denganx != 0 && !isNaN(x)
? Juga, biasanyas != null
untuk pointer, tetapi sesuatu yang lain untuk non-pointer.Ya, setiap tipe skalar dalam C, pointer, boolean (sejak C99), angka (floating-point atau tidak) dan enumerasi (karena pemetaan langsung ke angka) memiliki nilai falsey "alami", jadi itu cukup baik untuk ekspresi bersyarat apa pun .
Java juga memilikinya (bahkan jika pointer Java disebut referensi dan sangat dibatasi), tetapi Java memperkenalkan tinju otomatis di Jawa 5.0 yang membuat air tidak dapat diterima. Juga, programmer Java mendesak nilai intrinsik mengetik lebih banyak.
Satu kesalahan yang memicu perdebatan apakah membatasi ekspresi kondisional ke tipe boolean adalah kesalahan penulisan tugas di mana perbandingan dimaksudkan, yang tidak ditangani dengan membatasi tipe ekspresi kondisional sama sekali , tetapi dengan melarang penggunaan penugasan-ekspresi telanjang untuk nilainya.
Setiap kompiler C atau C ++ modern menangani dengan mudah, memberikan peringatan atau kesalahan untuk konstruksi yang dipertanyakan seperti itu jika diminta.
Untuk kasus-kasus di mana persis seperti yang dimaksudkan, menambahkan tanda kurung membantu.
Untuk meringkas, membatasi ke boolean (dan setara kotak di Jawa) tampak seperti upaya gagal untuk membuat kelas kesalahan ketik yang disebabkan oleh memilih
=
untuk tugas-kesalahan kompilasi.sumber
==
dengan operan boolean, yang mana yang pertama adalah variabel (yang kemudian bisa dengan ketik=
) di dalamif
terjadi jauh lebih jarang daripada dengan jenis lain, saya akan mengatakan, jadi itu hanya upaya semi-gagal.""
,(Object) ""
,0.0
,-0.0
,NaN
, array kosong, danBoolean.FALSE
? Terutama yang terakhir lucu, karena itu pointer non-null (true) yang membuka kotak ke false. +++ Saya juga benci menulisif (o != null)
, tetapi menyelamatkan saya beberapa karakter setiap hari dan membiarkan saya menghabiskan setengah hari men-debug ekspresi "pintar" saya bukanlah hal yang bagus. Karena itu, saya ingin melihat jalan tengah untuk Jawa: Aturan yang lebih murah hati tetapi tidak meninggalkan ambiguitas sama sekali.Dua tujuan desain Jawa adalah:
Izinkan pengembang untuk fokus pada masalah bisnis. Jadikan hal-hal lebih sederhana dan tidak terlalu rentan kesalahan, misalnya pengumpulan sampah sehingga pengembang tidak perlu fokus pada kebocoran memori.
Portable antar platform, mis. Berjalan di komputer mana saja dengan CPU apa pun.
Penggunaan penugasan sebagai ungkapan diketahui menyebabkan banyak bug karena kesalahan pengetikan, jadi berdasarkan tujuan # 1 di atas, itu tidak diizinkan seperti yang Anda coba menggunakannya.
Juga, menyimpulkan bahwa nilai bukan nol = benar dan nilai nol apa pun = salah tidak harus portabel (ya, percaya atau tidak, beberapa sistem memperlakukan 0 sebagai benar dan 1 sebagai salah ), jadi berdasarkan tujuan # 2 di atas, tidak diperbolehkan secara implisit . Anda masih dapat melakukan casting secara eksplisit tentunya.
sumber
Sebagai contoh apa yang dilakukan bahasa lain: Swift memerlukan ekspresi tipe yang mendukung protokol "BooleanType", yang artinya harus memiliki metode "boolValue". Tipe "bool" jelas mendukung protokol ini dan Anda dapat membuat tipe Anda sendiri yang mendukungnya. Jenis integer tidak mendukung protokol ini.
Dalam versi yang lebih lama dari bahasa, tipe opsional mendukung "BooleanType" sehingga Anda dapat menulis "jika x" bukannya "jika x! = Nil". Yang dibuat dengan menggunakan "bool opsional" sangat membingungkan. Bool opsional memiliki nilai nil, false atau true dan "jika b" tidak akan dieksekusi jika b adalah nil dan dieksekusi jika b benar atau salah. Ini tidak diizinkan lagi.
Dan tampaknya ada satu orang yang benar-benar tidak suka membuka cakrawala Anda ...
sumber