У меня есть эти две маленькие программы:
С
#include <stdio.h>
int main()
{
if (5) {
printf("true\n");
}
else {
printf("false\n");
}
return 0;
}
Джава
class type_system {
public static void main(String args[]) {
if (5) {
System.out.println("true");
}
else {
System.out.println("false");
}
}
}
который сообщает об ошибке:
type_system.java:4: error: incompatible types: int cannot be converted to boolean
if (5) {
^
1 error
Мое понимание
До сих пор я понимал этот пример как демонстрацию систем разных типов. C более слабо типизирован и позволяет конвертировать из int в логическое значение без ошибок. Java более строго типизирована и дает сбой, потому что неявный разговор не допускается.
Поэтому мой вопрос: где я неправильно понял вещи?
Что я не ищу
Мой вопрос не связан с плохим стилем кодирования. Я знаю, что это плохо, но меня интересует, почему C это позволяет, а Java нет. Поэтому меня интересует система типов языка, особенно его сильные стороны.
java
c
type-systems
toogley
источник
источник
int
значениями. Более правильный пример будетif (pointer)
.Ответы:
1. C и Java - это разные языки
Тот факт, что они ведут себя по-разному, не должен удивлять.
2. C не делает никаких преобразований из
int
вbool
Как это могло? C даже не имел истинного
bool
типа для преобразования до 1999 года . C был создан в начале 1970-х годов иif
был его частью еще до того, как стал C, когда это была просто серия модификаций B 1 .if
не был простоNOP
в C в течение почти 30 лет. Он напрямую воздействовал на числовые значения. Словоблудие в стандарте C ( ссылка PDF ), даже спустя более десяти лет после введенияbool
s в C, по-прежнему определяет поведениеif
(p 148) и?:
(p 100), используя термины «неравный 0» и «равный 0». ", а не булевы термины" истина "или" ложь "или что-то подобное.Удобно, ...
3. ... числа просто соответствуют инструкциям процессора.
JZ
иJNZ
ваши основные инструкции по сборке x86 для условного ветвления. Сокращения , являются " J UMP , если Z эро" и " J UMP , если N OT Z ERO". Эквиваленты для PDP-11, где С возникла, являютсяBEQ
( « Б ранчо , если EQ UAL„) иBNE
(“ Б ранчо , если N OT E каче»).Эти инструкции проверяют, привела ли предыдущая операция к нулю или нет, и переходят (или нет) соответственно.
4. Java уделяет гораздо больше внимания безопасности, чем C когда-либо делал 2
И, учитывая безопасность, они решили, что ограничение
if
наboolean
s стоит затрат (как на внедрение такого ограничения, так и в результате альтернативных издержек).1. У B вообще нет типов. Языки ассемблера, как правило, тоже нет. Тем не менее, языки B и ассемблера прекрасно справляются с ветвлением.
2. По словам Денниса Ричи, описывая запланированные модификации B, которые стали C (выделено мной):
источник
C 2011 Онлайн проект
Обратите внимание, что в этом пункте указывается только то, что управляющее выражение должно иметь скалярный тип (
char
/short
/int
/long
/ и т. Д.), А не конкретно булев тип. Ветвь выполняется, если управляющее выражение имеет ненулевое значение.Сравните это с
Спецификация языка Java SE 8
Java, OTOH, в частности, требует , чтобы управляющее выражение в
if
операторе имело логический тип.Таким образом, речь идет не о слабой и строгой типизации, а о том, что каждое соответствующее определение языка определяет как допустимое контрольное выражение.
редактировать
Что касается того, почему языки отличаются в этом конкретном отношении, несколько моментов:
C был получен из B, который был "типизированным" языком - в основном, все было 32- или 36-битным словом (в зависимости от аппаратного обеспечения), и все арифметические операции были целочисленными операциями. Система типов C была добавлена немного, так что ...
C не имел определенного булева типа до версии языка 1999 года. C просто следовал соглашению B об использовании нуля для представления
false
и ненулевого для представленияtrue
.Java пост-датирует C доброй парой десятилетий и была разработана специально для устранения некоторых недостатков C и C ++. Без сомнения, ужесточение ограничения на то, что может быть контрольным выражением в
if
утверждении, было частью этого.Нет никаких оснований ожидать, что любые два языка программирования будут действовать одинаково. Даже языки, тесно связанные с C и C ++, расходятся в некоторых интересных аспектах, так что вы можете иметь легальные программы на C, которые не являются легальными программами C ++ или являются легальными программами C ++, но с другой семантикой и т. Д.
источник
int
в,boolean
а Java - нет. Ответ в том, что такого преобразования нет в C. Языки разные, потому что это разные языки.Кажется, что многие ответы нацелены на встроенное выражение присваивания, которое находится внутри условного выражения. (Хотя это известная потенциальная ловушка, в данном случае она не является источником сообщения об ошибке Java.)
Возможно, это связано с тем, что OP не опубликовал фактическое сообщение об ошибке, и
^
каретка указывает непосредственно на=
оператора присваивания.Однако компилятор указывает на
=
потому, что именно оператор создает конечное значение (и, следовательно, конечный тип) выражения, которое видит условное выражение.Он жалуется на тестирование не булевого значения со следующей ошибкой:
Тестирование целых чисел, хотя иногда и удобно, считается потенциальной ловушкой, которую Java-дизайнеры предпочитают избегать. В конце концов, Java имеет истинный логический тип данных , который C делает не (это не имеет никакого логического типа) .
Это также относится к указателям тестирования C на null / non-null через
if (p) ...
иif (!p) ...
, которые Java аналогично не позволяет вместо этого требовать явного оператора сравнения для получения требуемого логического значения.источник
if
утверждение.bool b = ...; int v = 5 + b;
. Это отличается от языков с полным логическим типом, которые нельзя использовать в арифметике.int v = 5; float f = 2.0 + v;
это допустимо в C._Bool
является одним из стандартных целочисленных типов без знака, как определено в Стандарте (см. 6.2.5 / 6).Ваш вопрос состоит из двух частей:
Почему Java не конвертируется
int
вboolean
?Это сводится к тому, что Java должна быть максимально явной. Это очень статично, очень "в вашем лице" с его системой типов. Вещи, которые автоматически приводятся в других языках, не таковы в Java. Вы также должны написать
int a=(int)0.5
. Преобразованиеfloat
вint
потеряет информацию; такой же, как преобразованиеint
вboolean
и, таким образом, будет подвержен ошибкам. Также им пришлось бы указывать много комбинаций. Конечно, эти вещи кажутся очевидными, но они намеревались ошибиться на стороне осторожности.Да, и по сравнению с другими языками Java была чрезвычайно точна в своей спецификации, поскольку байт-код был не просто внутренней деталью реализации. Они должны были бы точно указать любое и все взаимодействия. Огромное начинание.
Почему
if
не принимает другие типы, чемboolean
?if
вполне может быть определено, чтобы разрешить другие типы, чемboolean
. У этого может быть определение, которое говорит, что следующее эквивалентно:true
int != 0
String
с.length>0
null
(и не имеетBoolean
значенияfalse
).null
и чей методObject.check_if
(изобретенный мной только для этого случая) возвращаетtrue
.Они не сделали; в этом не было особой необходимости, и они хотели, чтобы он был как можно более надежным, статичным, прозрачным, легко читаемым и т. д. Нет явных особенностей. Кроме того, реализация будет довольно сложной, я уверен, что придется проверять каждое значение для всех возможных случаев, так что производительность могла также сыграть небольшую роль (Java раньше была запутанной на компьютерах того времени; не было JIT-компиляторов с первыми выпусками, по крайней мере, не на компьютерах, которые я использовал тогда).
Более глубокая причина
Более глубокая причина вполне может заключаться в том, что у Java есть свои примитивные типы, поэтому ее система типов разрывается между объектами и примитивами. Возможно, если бы они избежали этого, все могло бы сложиться иначе. С помощью правил, приведенных в предыдущем разделе, они должны были бы точно определять истинность каждого отдельного примитива (поскольку примитивы не разделяют суперкласс, и
null
для примитивов нет четкого определения ). Это быстро превратилось бы в кошмар.прогноз
Ну, и, в конце концов, может быть, это просто предпочтение языковых дизайнеров. Кажется, каждый язык крутится по-своему ...
Например, в Ruby нет примитивных типов. Все, буквально все, является объектом. Им очень легко убедиться, что у каждого объекта есть определенный метод.
Ruby ищет правдивость на всех типах объектов, которые вы можете бросить в него. Интересно, что у него все еще нет
boolean
типа (потому что у него нет примитивов), и у него тоже нетBoolean
класса. Если вы спросите, какой классtrue
имеет значение (доступно с помощьюtrue.class
), вы получитеTrueClass
. Этот класс на самом деле имеет методы, а именно 4 оператора для booleans (| & ^ ==
). Здесь,if
считает его значение фальси тогда и только тогда, когда оно равноfalse
илиnil
(thenull
Ruby). Все остальное правда. Так что,0
или""
оба правда.Для них было бы тривиально создать метод,
Object#truthy?
который можно было бы реализовать для любого класса и вернуть индивидуальную правдивость. Например,String#truthy?
мог быть реализован, чтобы быть верным для непустых строк, или еще много чего. Они этого не сделали, хотя Ruby является противоположностью Java в большинстве отделов (динамическая типизация утки с миксином, повторное открытие классов и все такое).Что может удивить программиста на Perl, который привык
$value <> 0 || length($value)>0 || defined($value)
быть правдивым. И так далее.Введите SQL с его соглашением, что
null
внутри любого выражения автоматически делает его ложным, несмотря ни на что. Так(null==null) = false
. В рубине(nil==nil) = true
. Счастливые времена.источник
((int)3) * ((float)2.5)
это довольно четко определено в Java (это так7.5f
).int
вfloat
также теряет информацию в целом. Java также запрещает такое неявное приведение?В дополнение к другим хорошим ответам я хотел бы поговорить о согласованности между языками.
Когда мы думаем о математически чистом операторе if, мы понимаем, что условие может быть либо истинным, либо ложным, а не другим значением. Каждый основной язык программирования уважает этот математический идеал; если вы дадите логическое значение true / false для оператора if, вы можете ожидать постоянного и интуитивного поведения.
Все идет нормально. Это то, что реализует Java, и только то, что реализует Java.
Другие языки пытаются принести удобство для небулевых значений. Например:
n
, это целое число. Теперь определите,if (n)
чтобы быть сокращением дляif (n != 0)
.x
это число с плавающей точкой. Теперь определите,if (x)
чтобы быть сокращением дляif (x != 0 && !isNaN(x))
.p
, это тип указателя. Теперь определите,if (p)
чтобы быть сокращением дляif (p != null)
.s
, это строковый тип. Теперь определитесь,if (s)
чтобы бытьif (s != null && s != "")
.a
, это тип массива. Теперь определитесь,if (a)
чтобы бытьif (a != null && a.length > 0)
.Эта идея предоставления сокращенных if-тестов на первый взгляд кажется хорошей ... пока вы не столкнетесь с различиями в дизайне и мнениях:
if (0)
трактуется как ложный в C, Python, JavaScript; но рассматривается как истинный в Ruby.if ([])
трактуется как ложное в Python, но верно в JavaScript.У каждого языка есть свои веские причины относиться к выражениям тем или иным образом. (Например, единственными ложными значениями в Ruby являются
false
иnil
, следовательно, они0
являются правдивыми.)Java приняла явный дизайн, чтобы заставить вас указывать логическое значение для оператора if. Если вы поспешно перевели код с C / Ruby / Python на Java, вы не можете оставить любые слабые if-тесты без изменений; вам нужно явно выписать условие в Java. Если сделать паузу и подумать, можно спасти вас от небрежных ошибок.
источник
x != 0
это так же, какx != 0 && !isNaN(x)
? Кроме того, это обычноs != null
для указателей, но что-то еще для не указателей.Каждый скалярный тип в C, указатель, логическое значение (начиная с C99), число (с плавающей запятой или нет) и перечисление (из-за прямого сопоставления с числами) имеют «естественное» значение Фолси, так что этого достаточно для любого условного выражения. ,
В Java они тоже есть (даже если указатели Java называются ссылками и сильно ограничены), но в Java 5.0 введен автобокс, который недопустимо запутывает воду. Кроме того, Java-программисты придают больше смысла печатать больше.
Одна ошибки , которая породила дебаты ли ограничение условных выражений для логического типа является опечаткой писать задание , где было задумано сравнение, которое не адресовано, ограничивая условный тип выражений вообще , но запрещая использованием голой присваивании-выражение за его ценность.
Любой современный компилятор C или C ++ справляется с этим легко, выдавая предупреждение или ошибку для таких сомнительных конструкций, если их спросят.
Для тех случаев, когда это именно то, что было задумано, добавление скобок помогает.
Подводя итог, можно сказать, что ограничение на логическое (и коробочный эквивалент в Java) выглядит как неудачная попытка создать класс опечаток, вызванных выбором
=
ошибок компиляции для присваивания.источник
==
с булевыми операндами, первый из которых является переменной (которую затем можно было бы опечатать=
) внутри,if
происходит гораздо реже, чем с другими типами, я бы сказал, так что это всего лишь неудачная попытка.""
,(Object) ""
,0.0
,-0.0
,NaN
, пустые массивы, аBoolean.FALSE
? Особенно последний забавен, так как это ненулевой указатель (true), который распаковывает в false. +++ Я также ненавижу писатьif (o != null)
, но экономить несколько символов каждый день и позволять мне тратить полдня на отладку своего «умного» выражения - не очень хорошая сделка. Тем не менее, я хотел бы видеть какой-то средний путь для Java: более щедрые правила, но не оставляющие двусмысленности вообще.Двумя целями разработки Java были:
Позвольте разработчику сосредоточиться на бизнес-проблеме. Сделайте вещи проще и менее подверженными ошибкам, например, сборку мусора, чтобы разработчикам не приходилось фокусироваться на утечках памяти.
Переносим между платформами, например, работает на любом компьютере с любым процессором.
Известно, что использование присваивания в качестве выражения вызывало много ошибок из-за опечаток, поэтому в соответствии с целью № 1, приведенной выше, недопустимо то, как вы пытаетесь его использовать.
Кроме того, вывод о том, что любое ненулевое значение = true и любое нулевое значение = false не обязательно является переносимым (да, верите или нет, некоторые системы рассматривают 0 как true и 1 как false ), поэтому в рамках цели № 2 выше не допускается неявно , Вы все еще можете явно разыграть.
источник
В качестве примера того, что делают другие языки: Swift требует выражения типа, поддерживающего протокол «BooleanType», что означает, что он должен иметь метод «boolValue». Тип "bool", очевидно, поддерживает этот протокол, и вы можете создавать свои собственные типы, поддерживающие его. Целочисленные типы не поддерживают этот протокол.
В более старых версиях языка дополнительные типы поддерживали «BooleanType», поэтому вы могли написать «если x» вместо «if x! = Nil». Что делает использование "Optional Bool" очень запутанным. Необязательный bool имеет значения nil, false или true и «if b» не будет выполнено, если b равно nil, и выполнено, если b равно true или false. Это больше не разрешено.
И, кажется, есть один парень, которому очень не нравится открывать свои горизонты ...
источник