Каковы недостатки системы типа Java/С#?

Часто он слышит, что Haskell (который я не знаю) имеет очень интересную систему типов. Я очень хорошо знаком с Java и немного с С#, и иногда бывает так, что я борюсь с системой типов, поэтому какой-то дизайн подходит или работает лучше определенным образом.

Это заставило меня задуматься...

Какие проблемы возникают из-за недостатков системы типа Java/С#? Как вы с ними справляетесь?

Ответ 1

Массивы разбиты.

  Object[] foo = new String[1];
  foo[0] = new Integer(4);

Дает вам java.lang.ArrayStoreException

Вы справляетесь с ними с осторожностью.

Нулемость - еще одна большая проблема. NullPointerExceptions прыгают на вашем лице повсюду. Вы действительно ничего не можете сделать с ними, кроме языка переключения, или используйте соглашения, чтобы избежать их как можно больше (правильно инициализировать поля и т.д.).

В более общем плане системы типа Java/С# не очень выразительны. Самое важное, что может вам дать Haskell, - это то, что с его типами вы можете обеспечить выполнение функций, которые не имеют побочных эффектов. Имея доказательство времени компиляции, что части программ являются просто выраженными выражениями, делает программы более надежными, удобными и понятными. (Игнорируйте тот факт, что реализации Haskell дают вам способы обойти это).

Сравните это с Java, где вызов метода может сделать почти все!

Также у Haskell есть сопоставление образцов, которое дает вам другой способ создания программ; у вас есть данные о том, какие функции работают, часто рекурсивно. В сопоставлении с образцом вы уничтожаете данные, чтобы увидеть, что это такое, и вести себя в соответствии с ним. например У вас есть список, который либо пуст, либо голова и хвост. Если вы хотите рассчитать длину, вы определяете функцию, которая говорит: если список пуст, длина = 0, в противном случае длина = 1 + длина (хвост).

Если вам действительно нравится больше узнать, есть два отличных онлайн-источника:

Учите вас Haskell и Real World Haskell

Ответ 2

Мне не нравится тот факт, что существует дифференциация между примитивными (родными) типами (int, boolean, double) и соответствующими классами-оболочками (Integer, boolean, double) в Java.

Это часто бывает очень неприятно, особенно при написании общего кода. Родные типы не могут быть обобщены, вы должны создать экземпляр оболочки. Дженерики должны сделать ваш код более абстрактным и более простым в использовании, но на Java они привносят ограничения, явно не имеющие причин.

private static <T> T First(T arg[]) {
    return arg[0];
}

public static void main(String[] args) {        
    int x[] = {1, 2, 3};
    Integer y[] = {3, 4, 5};

    First(x); // Wrong
    First(y); // Fine
}

В .NET таких проблем нет, хотя существуют отдельные значения и ссылочные типы, поскольку они строго реализуют "все является объектом".

Ответ 4

Мне не нравится тот факт, что классы не являются первоклассными объектами, и вы не можете делать причудливые вещи, такие как статический метод, являющийся частью интерфейса.

Ответ 5

Основной недостаток в системе типа Java/.net заключается в том, что он не имеет декларативных средств для определения того, как состояние объекта относится к содержимому его полей ссылочного типа, или о том, что метод разрешен для сохранения ссылочного типа, типа. Хотя в некотором смысле это хорошо для среды выполнения, чтобы иметь возможность использовать поле Foo одного типа ICollection<integer> для обозначения многих разных вещей, невозможно, чтобы система типов обеспечивала реальную поддержку таких вещей, как неизменность, тестирование эквивалентности, клонирование или любые другие такие функции, не зная, есть ли Foo:

  • Ссылка только для чтения на коллекцию, которая никогда не будет мутировать; класс может свободно делиться такой ссылкой с внешним кодом, не затрагивая ее семантику. Ссылка инкапсулирует только неизменяемое состояние и, вероятно, не инкапсулирует идентичность.
  • Доступная для записи ссылка на коллекцию, тип которой изменен, но которая никогда не будет мутировать; класс может передавать только такие ссылки с кодом, которому можно доверять, чтобы не мутировать его. Как и выше, ссылка инкапсулирует только неизменяемое состояние и, вероятно, не инкапсулирует идентификатор.
  • Единственная ссылка в любом месте юниверса на коллекцию, которую она мутирует. Ссылка будет инкапсулировать изменчивое состояние, но не будет инкапсулировать идентификатор (замена коллекции другим, содержащим одни и те же элементы, не изменит состояние объекта-объекта).
  • Ссылка на коллекцию, которую он мутирует, и содержимое которой оно считает своим, но к которому внешний код содержит ссылки, которые он ожидает, присоединяться к текущему состоянию Foo. Ссылка включала бы как идентификатор, так и изменяемое состояние.
  • Ссылка на изменяемую коллекцию, принадлежащую другому объекту, который, как ожидается, будет прикреплен к этому другому состоянию объекта (например, если объект, содержащий `Foo`, должен отображать содержимое какой-либо другой коллекции). Эта ссылка будет инкапсулировать идентификацию, но не будет инкапсулировать изменчивое состояние.

Предположим, что нужно скопировать состояние объекта, содержащего Foo, в новый, отсоединенный объект. Если Foo представляет # 1 или # 2, можно сохранить в новом объекте либо копию ссылки в Foo, либо ссылку на новый объект, содержащий одни и те же данные; копирование ссылки было бы быстрее, но обе операции были бы правильными. Если Foo представляет # 3, правильная отдельная копия должна содержать ссылку на новый отдельный объект, состояние которого копируется из оригинала. Если Foo представляет # 5, правильная отдельная копия должна содержать копию исходной ссылки - она ​​НЕ должна содержать ссылку на новый отдельный объект. И если Foo представляет # 4, состояние объекта, содержащего его, не может быть скопировано по отдельности; возможно, можно скопировать кучу взаимосвязанных объектов, чтобы получить новую связку, состояние которой эквивалентно оригиналу, но невозможно было бы копировать состояние объектов по отдельности.

Пока система типов не будет декларативно указывать все возможные отношения, которые могут существовать между объектами, и что делать с ними, должно быть возможно, чтобы система и структура типа правильно генерировали код для производить семантически-правильные тесты эквивалентности, методы клонирования, плавно взаимодействующие изменяемые, неизменные и "читаемые" типы и т.д. в большинстве случаев, если он знал, какие поля инкапсулируют идентичность, изменяемое состояние, то и другое, или ни одно из них. Кроме того, должно быть возможно, чтобы структура минимизировала оборонительное копирование и обертывание в обстоятельствах, когда оно могло гарантировать, что переданные ссылки не будут переданы ни на что, что бы их мутировать.

Ответ 6

(Re: С# в частности.)

Мне бы понравилось тегированные объединения.

Ditto on первоклассные объекты для классов, методов, свойств и т.д.

Хотя я никогда не использовал их, Python имеет типы классов, которые в основном представляют собой типы, которые представляют классы и как они ведут себя.

Ненулевые ссылочные типы, поэтому нулевые проверки не нужны. Первоначально он рассматривался для С#, но был отброшен. (Существует вопрос переполнение стека).

Ковариация, поэтому я могу применить List<string> к List<object>.

Ответ 7

Это незначительно, но для текущих версий Java и С# объявление объектов разбивает принцип DRY:

Object foo = new Object; 
Int x = new Int; 

Ответ 8

Ни у кого из них нет средств метапрограммирования, как говорят, что у старой черной собаки С++ есть.

Использование "использования" дублирования и отсутствия typedef - один из примеров, который нарушает DRY и может даже вызвать пользовательские ошибки "сглаживания" и многое другое. Java-шаблоны даже не стоит упоминать.