Мой вопрос в значительной степени совпадает с тем, что написано в заголовке: возможно ли иметь язык программирования, который не допускает явного приведения типов?
Чтобы пояснить, что я имею в виду, предположим, что мы работаем на некотором C # -подобном языке с родительским Base
классом и дочерним Derived
классом. Понятно, что такой код будет безопасным:
Base a = new Derived();
Поскольку восходящая иерархия наследования безопасна, но
Dervied b = (Base)a;
небезопасно, так как спускаться небезопасно.
Но, независимо от безопасности, такие downcast действительны на многих языках (например, Java или C #) - код скомпилируется и просто завершится ошибкой во время выполнения, если типы не верны. Технически, код все еще безопасен, но с помощью проверок во время выполнения, а не проверок во время компиляции (кстати, я не фанат проверок во время выполнения).
Я лично считаю, что полная безопасность типов во время компиляции очень важна, по крайней мере, с теоретической точки зрения и, самое большее, с точки зрения надежного кода. Следствием безопасности типов во время компиляции является то, что приведение больше не требуется (что я считаю великолепным, потому что они в любом случае безобразны). Любое поведение, подобное приведению, может быть реализовано оператором неявного преобразования или конструктором.
Итак, мне интересно, существуют ли в настоящее время какие-либо ОО-языки, которые обеспечивают такую строгую безопасность типов во время компиляции, что приведение к ним является устаревшим? Т.е. они вообще не разрешают небезопасные операции преобразования? Или есть причина, по которой это не сработает?
Спасибо за любой вклад.
Редактировать
Если я смогу уточнить на примере, вот основная причина, по которой я так ненавижу унылых.
Допустим, у меня есть следующее (свободно основанное на коллекциях C #):
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
IEnumerable<T> Filter( Func<T, bool> );
}
public class List<T> : IEnumerable<T>
{
// All of list's implementation here
}
Теперь предположим, что кто-то решил написать такой код:
List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Let's filter out the odd numbers
List<int> result = (List<int>)list.Filter( x => x % 2 != 0 );
Обратите внимание, что приведение необходимо в этой последней строке. Но так ли это? Не в общем. Конечно, имеет смысл, что реализация List<T>.Filter
вернет другой List<T>
, но это не гарантировано (это может быть любой подтип IEnumerable<T>
). Даже если это выполняется в один момент времени, более поздняя версия может изменить это, показывая, насколько хрупок код.
Практически во всех ситуациях, которые, как мне кажется, требуются пониженные версии, сводится к чему-то вроде этого примера - у метода есть тип возврата некоторого класса или интерфейса, но, поскольку мы знаем некоторые детали реализации, мы уверены в понижении результат. Но это анти-ООП, так как ООП фактически поощряет абстрагирование от деталей реализации. Так почему же мы это делаем, даже на чисто ООП языках?