Что есть в Java, чего нет в C#?

Рейтинг: 28Ответов: 5Опубликовано: 14.10.2014

Решил попробовать писать под андроид и столкнулся с необходимостью писать на Java. До этого писал больше на C#. Языки похожие во многом, но есть и отличия. И так как пока я C# знаю лучше Java, то так получается, что я пользуюсь только теми возможностями синтаксиса Java, которые знаю по C#. В общем, сейчас для меня java превратился в "урезаный C#", потому что каких-то чисто джавовских "фишек" я не знаю, а некоторых возможностей из C# в этом языке нет. Ну, например, в Java нет оператора ??, нет linq, нет свойств, нет атрибутов.

Так вот сам вопрос: а что есть в Java, чего нет в C#? Именно из синтаксиса, различных удобностей и синтаксического сахара.

Ответы

▲ 47Принят

Смотрите.

В основном по фичам в данный момент C# идёт впереди Java, Java находится в позиции догоняющего. Однако есть несколько фич, которые есть в Java и нет в C# и которые при правильном использовании могут облегчить жизнь программисту.

1) Легковесные (анонимные) производные классы.

Пример:

new Thread(new Runnable() { // это анонимный производный класс!
        @override
        public void run() {
            // do some work
        }
    }).start();

В C# надо было бы объявить производный класс явно. Аналогичная, но не равносильная фича C# — анонимные методы, то есть лямбды.

2) Нестатические внутренние классы. В C# внутренние классы лишь логически находятся «внутри» и не имеют доступа к instance-переменным. В Java внутренние классы более богаты.

3) enum'ы. В C# они такие же, как в C++, и являются по существу именованными константами целочисленного типа. В Java enum'ы есть константы объектного типа, гораздо более богатые семантически.

4) checked exceptions. Вы можете объявлять как часть сигнатуры метода исключения, которые бросаются этим методом. В C# такой возможности нету. (Хотя разработчики C# считают, что эта фича не нужна и даже вредна, тем не менее по факту это фича, которая есть в Java и нет в C#.)

5) final-параметры. В C# const могут лишь переменные и поля, в Java можно параметр объявить как final, и при попытке его изменить компилятор наругается на вас.

6) SoftReference представляет собой «более сильную» версию WeakReference (которые тоже есть в Java): объекты, референсируемые ими, не удаляются, пока памяти хватает, даже если другие объекты уничтожаются в процессе сборки мусора.

7) У Java есть симпатичные помеченные блоки, которые позволяют выйти из любого количества циклов за раз (break) или пропустить итерацию во внешнем цикле (continue). Также break <label>; может выйти из любого блока, не только цикла (что делает его равносильным goto).

8) В Java вы можете ловить в одном catch несколько исключений: catch (IOException | SQLException ex). C# такое не умеет.


С выходом новой версии Java 8 добавилось ещё одно расхождение:

9) У Java есть очень полезная фича: default interface implementation, с помощью которой можно устраивать подобие mixin'ов: написать код, который можно добавить к любому классу, с возможностью перегрузки.

У C# похожее поведение можно смоделировать при помощи extension-методов (которых в Java как раз нет) и маркерных интерфейсов, но вы не сможете полиморфно перегрузить метод в конкретном классе, только перекрыть. В результате если у вас есть указатель на базовый класс, вызов в случае C# приведёт к вызову метода для базового класса.

▲ 8

Вот теперь ведь никто мимо не пройдёт.

Ну, например, в Java нет оператора ??

Зато есть обычный {o == null ? no : o}, это не урезание, а лишь отсутствие сахара. Функционально не обедняет.

нет linq

Да, linq нет, но для работы с коллекциями можно библиотечку подыскать вроде Google Guava, если в самом деле так нужно. Для работы с результатами запросов из бд тоже можно решить проблему. На мой взгляд, решения делать через синтаксис языка лучше, чем liq, который работает примерно так: парсим один синтаксис, получаем другой, другой исполняем...

нет свойств

Да, в java принято самому писать get* и set* методы. Это даже в спецификации java bean описано. (Ну, если вспомить C#, он просто делает методы get_*, set_* за кулисами). В настоящий момент нет необходимости всё подряд через геттеры получать... можно и поля аннотировать.

нет атрибутов

Но есть аннотации.

Лично я считаю, .Net и Java - аналогичные технологии, призванные решать по большому счёту одни и те же задачи. Есть, конечно, свои особенности... и простое правило: пишешь только под Windows - используй .Net. Нет, Java лучше.

▲ 6

Вкратце

C# сильнее как язык: в Java нет практически ничего, чего нет в C#. Как язык, Java отстал от времени и находится в роли догоняющего. Новые возможности натыкаются на несметное число проблем, унаследованных из-за обратной совместимости. Одна из критических проблем — type erasure.

Java сильнее как платформа: для C# нет такого количества библиотек, как для Java. Java гораздо популярнее в опен-сорсе. Разрабатывая более-менее популярные вещи, вы никогда не заметите разницу, но если закапываться в экзотику, то разница весьма ощутима.

Раньше C# был относительно закрытым проектом, теперь он со всех сторон открытый, однако поезд ушёл, поэтому разрыв в размере опенсорсного сообщества не сократится никогда. В свою очередь Java как язык тоже уже ничто не может спасти, ибо обратная совместимость — святая корова.

Если перед вами стоит выбор "C# или Java", то всё просто:

  • Хотите получать удовольствие от красивого кода — изучайте C#.
  • Хотите более мощное опенсорсное сообщество — изучайте Java и переходите на Kotlin.

Про фичи

Достаточно поглядеть Comparison of C# and Java, чтобы понять пропасть между языками. Не верьте тем, кто говорит, что Джава сильна минимализмом — они врут (или просто привыкли писать огромные конструкции, когда достаточно одной строчки).

Приведённые в первом посте фичи — это или обман, или незнание, или манипуляция.

  • "Легковесные классы" никто не использует со времён появления лямбд. Это был корявый нечитаемый костыль для обхода проблемы. Где-то они остались как царское наследие, но в новом коде их быть не должно.
  • Нестатические внутренние классы — это просто классы со ссылкой владельца, проблема решается в две строки кода. Это очень минимальный синтаксический сахар.
  • Проверяемые исплючения — источник огромного количества проблем, который корректно работает только на красивых примерах в десять строчек. В реальности же возникают проблемы с версионностью, наследованием, типами для лямбд и прочим.
  • Final параметры не нужны, так как это всего лишь огрызок константности из C++, а при отсутствии всей системы вокруг это бессмысленная декорация.
  • Информация про catch устарела. Теперь там можно городить что угодно с паттерн-матчингом.
  • Аналогично с default interface implementation — эту фичу уже добавили.

Полный список важных фич, которые отсутствуют в C#, но есть в Java:

  • Strict floating point: детерминированные вычисления над числами с плавающей точкой. Можно получить костыльным способом, зависящим от ОС.
  • Soft, phantom ссылки. Польза спорная, потому что, скорее всего, кэш вы будете городить ручками, а не надеяться на невнятную магию.
  • Возможность запустить на большем количестве устройств. Холодильник скорее будет поддерживать джаву, андроид нативно поддерживает джаву.
  • Более гибкая настройка рантайма: зоопарк сборщиков мусора, вот это всё.

В общем-то, всё. Если же перечислять то, что есть в C#, но нет в Java, то список получится непомерно длинным.

Разумеется, даже в условиях серьёзного отставания джавы джависты не хотят предавать платформу. Определённое распространение получили альтернативные языки на JVM, например, Kotlin и Scala. В чём-то они мощнее C#, но по популярности они сильно уступают обоим языкам, и у них свои проблемы. У Kotlin наиболее серьёзные шансы, потому что он получил официальную поддержку от Google на Android.

Итог

По популярности в энтерпрайзе языки примерно на одном уровне. Детали зависят от области разработки и страны проживания.

Если вы хотите получать удовольствие от программирования, выбирайте C#. Если для вас критичен более широкий выбор редких библиотек, то выбирайте Java, но по возможности переходите на Kotlin. Если же вы хотите покопаться во всём, то морально готовьтесь к тому, что вам ну никак не обойти ещё несколько языков типа JavaScript (весь веб до сих пор на нём, не имеет отношения к Java), Python (один из самых популярных языков для связки инструментария) и прочих.

▲ 2

Сила, брат, в простоте. Мощь Java как раз и кроется в простоте. Есть такой принцип Бритва Оккама - проще говоря:

Не умножай сущностей без необходимости

Все эти linq, attributes - без них спокойно можно прожить.

Например, в C# есть такое понятие, как partial - так это вообще преступление и оксюморон!

▲ 2

Из того, что еще не было упомянуто: по-разному устроены способы реализации ко- и контра- вариантности в generics-ах.

Как оно работает в C#

В шарпах, как известно, например, ковариантный интерфейс можно объявить вот так (пример нагло сворован с MSDN):

interface ICovariant<out R>
{
    R GetSomething();
}

Тогда, если у нас есть класс по типу:

class Implementation<T> : ICovariant<T> where T : new()
{
    public T GetSomething()
    {
        return new T();
    }
}

И два наследующих друг от друга класса:

class A {}
class B : A {}

Мы можем сделать так:

Implementation<B> b = new Implementation<B>();
ICovariant<A> a = b;

Т.е. получается, что ICovariant<B> как будто наследует от ICovariant<A>. Аналогично с контравариантностью, только там у type paramet-ра модификатор in вместо out, и все ровно наоборот - созданный от базового класса (A) экземпляр как бы "наследует" от производного (B). (на самом деле, это не наследование, а отношение is-a, т.е. если B есть A, то ICovariant<B> есть ICovariant<A>, но это уже семантические дебри).

Как оно работает в Java

А в Джаве по те же цели запилили другой механизм, который иногда ругают за ограниченность и меньшую безопасность, однако работает же!

Если я ничего не путаю, это называется call-site co/contra- variance. Фокус тут в том, что в шарпах вариантность (ко-, контра-, ин-) задается в декларации интерфейсов и делегатов, а у Джавы - в декларации самих объектов.

Это пашет через Джавины wildcard-ы (когда вместо T в "клювиках" стоит ?). Аналогичный данному для шарпов пример тут будет выглядеть так:

class GenericClass<T> {
    public T field;
}

class A {}
class B extends A {}

GenericClass<B> b = new GenericClass<B>();
GenericClass<? extends A> a = b;

? (Wildcard) в качетсве type-paramet-ра обозначает, что черт его, собственно, знает, что это за тип такой, так что расслабиться с проверкой типов чуток можно.

Они бывают unbounded (просто один ? в клювиках), upper-bounded (<? extends A>, т.е. об этом типе есть информация, что он наследует от какого-то другого типа - A. По сути, <? extends Object> аналогично unbounded wildcard), и lower-bounded (<? super B>, т.е. этот тип должен быть одним из предков другого типа - B).

Upper-bounded wildcards используются, как я продемонстрировал выше, для ковариантности, а lower-bounded wildcards, как ясно, для контравариантности.

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