Pull to refresh

Comments 87

>> Кто в проекте не использует log4j? А можете сходу назвать библиотеки, которые тоже обходятся без него?

Те кто используют slf4j и н-р logback

>> Классы-оболочки — избыточны… Зачем все это?

Для автоматического boxing и unboxing?

Если честно, то холиварный пост, и создается ощущение, что автор не до конца разобрался в предмете.
Пост о том, что мне не нравится в java в повседневном использовании. Я уверен, что у Вас тоже есть что сказать.
Согласен практически со всем, что Вы написали. Мне еще не хватает оверлоадинга операций (как в C#), это решило бы проблемы аналогичные описанной выше с BigDecimal. И даже не знаю, с одной стороны хочется, а с другой стороны понимаю, что читабельность исходников ухудшается, создание объектов в стиле C++:

вместо: SomeObject o = new Someobject(a,b,c);
хочется: SomeObject o(a,b,c);
Не знаю, может стоит тогда уйти с java? Да мне тоже не что то не нравится, например что надо типизировать все коллекции по типу List хотя в самом конце оно всё равно возвращается в List
Зачем сходить с джавы? За джаву больше всего платят )
Ну и все в целом не так плохо — минусы есть, но язык вполне хороший. И в ближайшее время никаких проблем с трудоустройством не будет точно.
Как это «зачем» — конкуренция снизится, тем, кто останется, платить станут больше :)
Ну блин, если не нравится — не пиши, а хочешь писать — не ной.
Ничего. Я когда асм учил я плакал и писал, и ничего жив остался.
Зачем просто поднимать пост о том что «мне не нравится», а называть «Плохая Java или как делать ненадо».

Я вобще очень люблю всё в таком духе. Как в передаче Среда обитания например. говорят что «всё гавно» и «жрать нечего», но не говорят чем можно заменить.
Этот пост примерно тоже самое, вот я решил сказать, что можно сделать.
Так используйте другие языки под ту же JVM. В чем проблема?
>>Для автоматического boxing и unboxing?

Вы можете посмотреть, как это реализовано в C#. Там, в частности, приведенные автором в пункте «Numbers» примеры будут компилироваться(если, конечно, заменить типы на соответствующие им в .net). Это вдвойне странно, если учитывать, что боксинг был сделан в Java уже после того, как он появился в .net.
Ничего не странно. Это все для обратной совместимости — чтобы были не примитивы, а субклассы Object.
Согласен. С рядом его замечаний я согласен полностью, ряд выглядят мелкими придирками.

В целом Java как язык весьма ограничена и примитивна, но как developmemt/runtime платформа великолепна по ряду причин.

Автор, откройте для себя что Java — это не язык, точнее не только язык. Посмотрите на Groovy, Scala, Jython.

Вы жаловались на Switch? Так вот вам как должен выглядить мощный switch:

switch (20) {
case 2: assert false; break // Равно двум?
case 2..15: assert false; break // Попадает в диапазон?
case [11,12,14,16]: assert false; break // Входит в список?
case Double: assert false; break // является Double-типом?
case {it % 5 == 0}: assert true; break // Делится на пять?
case ~/../: assert true; break // Матчится regex-y? В эту ветку мы не заходим
default: assert false; break
}

Что человек как-то не до конца проникася явой чувствуется когда начинается подсчет байтов,
и дисскусия сводится к вопросами экономии их… даже в отрыве от задачи.
>мало того что 2 байта занимает каждый символ
-XX:+UseCompressedStrings

Многопоточность такая из-за самой архитектуры языка. Erlang изначально был спроектирован под легковесные потоки.
Вы путаете 2 парадигмы организации многопоточности: concurrency vs message passing. И та, и другая имеют преимущества и недостатки.
В эрланге у одного потока нет доступа к памяти другого потока, поэтому все данные должны передаваться через сообщения. Т.е. здесь вы проигрываете в скорости доступа к данным, но выигрываете в простоте и удобстве. К слову, никто вам не мешает в java использовать общение потоков только через очередь сообщений, не публикуя ссылки на объекты, принадлежащие потоку.
Еще из претензий к Java: нет generic-методов, информация о параметризации отсутствует во время исполнения.
В Java НЕТ Generics!
В отличие от шаблонов C#, шаблоны Java не поддерживаются средой исполнения — компилятор просто создаёт байт-код, в котором никаких шаблонов уже нет. Реализация шаблонов в Java принципиально отличается от реализации аналогичных механизмов в C++: компилятор не порождает для каждого случая использования шаблона отдельный вариант класса или метода-шаблона, а просто создаёт одну реализацию байт-кода, содержащую необходимые проверки и преобразования типов. Это приводит к ряду ограничений использования шаблонов в программах на Java.
Во первых, как Ваш комментарий относится к моему? И во-вторых, то что Вы описали — лишь особенности реализации, которые прикладного программиста касаются в малой степени (лишь в той, где «это приводит к ряду ограничений», да и те ограничения не такие уж существенные).
Если для вас они несущественные — значит Вы никогда не использовали reflection в связке с generics.
Если приходится использовать generics в связке с reflection — значит что-то не то с вашей архитекторой.
Generic в Java ЕСТЬ, в Java нет того Тьюринг-полного механизма TEMPLATES, который есть в C++.
А нужен ли он Яве? Как минимум, один недостаток

> Тьюринг-полного механизма TEMPLATES

— недетерминированность времени компиляции.
Я лично считаю, что не нужен. Достаточно посмотреть сколько времени компилируются сложный темплейтный код на С++… чтобы их возненавидеть.

Сколько времени будет компилироваться код на С++ с темплейтами, в котором скажем 3-4 миллиона строк кода?
Все верно, через рифлекшен информацию о типах не достать…
В отличие от С# в Java генерики появились только в пятой версии, но при этом новый код с генериками буде скомпилирован запустится и отработает на любой древней jvm, которая о них и не слышала. Обратная совместимость, wright once run everywhere и т.д. и т.п. Это основополагающие принципы java. В молодом C# конечно уже по-другому.
Мне тоже это не нравится, хочется рантайм-генериков и вообще java 2.0 без обратной совместимости. Чтобы ууух сколько классных фич сразу. Но не выйдет, целевая аудитория не та. Java, она для энтэрпрайз мастадонтов, которые может ещё лет десять будут jvm 1.3 использовать потому что масштабы не те и обновиться как у себя на ноутбучеке быстро и просто не получится.
Про обратную совместисть хороший поинт. Это очень сильная сторона Java. Я например хорошо помню что .NET 1.0, .Net 1.1, .Net 2.0 были несовместимы. Что в те годы меня дико бесило.
Сам-то запускать пробовал? Может и запустится, но не факт что не свалится с каким-нибудь methodnotfoundexception на isEmpty()
Это не то, что я имел в виду. По вышей ссылке идет описание использование generic-параметра класса в методе. В c# метод может иметь свою параметризацию:

public static void Add(T first, T second) {...}

В джаве же Т я должен указывать в объявлении класса.
упс, парсер съел скобки.
public static void Add<T>(T first, T second) {...}
Осторожнее, воинствующий xonix может и Вам минусиков наставить ;)
> В джаве же Т я должен указывать в объявлении класса.

?

public class C {
    public static void main(String[] args) {
        someMethod(123, 456);
        someMethod(new Object(), new Object());
        someMethod("aaa", "bbb");
    }

    static <T> void someMethod(T a, T b) {
        System.out.println("a=" + a + ", b=" + b);
    }
}

Вывод:

a=123, b=456
a=java.lang.Object@fa3ac1, b=java.lang.Object@276af2
a=aaa, b=bbb
хм. Спасибо, я действительно был не прав.
причем даже аргументом указывать не нужно при желании.

public class T
{
public static void main(String[] args) {
String l = T.foo();
}
public static T foo()
{
return (T)(new String());
}
}

простите за стаб в виде new String()
Про свитч: все же они реализовали его немного эффективней, они при компиляции просчитывают hashCode у строк, для всех объектов этого сделать нельзя.
Хоть конечно не понятно что им мешает сделать тупую реализацию, все равно хуже не станет…
Думаю, тут проблема в том что просто_объекты могут быть изменяемыми. Даже если не касаться вопроса оптимизации (нельзя просчитать хэшкод во время компиляции), я даже представить боюсь к чему может привести свитч по изменяемым объектам…
Про Serializable мы имеем следующую проблему: вот Map, к примеру, Serializable или нет? А проблема в том что зависит это от типа ключа и значения, вот и переложили эту проблему полностью в runtime…
Про Collections: ну так обратная совместимость, нельзя так вот просто поменять методы старых классов видимо. Хоть вроде же есть сподвижки в правильную сторону в следующей java? (я про что-то наподобие extension методов).
Про многопоточность: акторы лишь иной способ многопоточности, видимо достаточно хороший, но java.concurrency уже тоже не локи в чистом виде, все же чуть более высокий уровень. Ну и есть Akka.
Про строчки: а что такое дешевые строчки? Храните char array где вам это нужно. Не встречал проблем со String, хоть может быть они и действительно распространенны.

Но вообще очень жаль, конечно, что ява очень медленно развивается.
>гораздо удобней было бы оперировать примитивами и иметь возможность присваивать им null значение. Если простой int занимает 4 байта, то обьект Integer уже целых 16.

Спасибо, не надо. Это только увеличит число NPE (NullPointerException). От этого наоборот стоит уходить. Скажем, меня удивляет, почему еще не добавили поддержку JSR-305 (@Nullable, @NonNull, ...) в компилятор.
И потом, Вы уверены, что если разрешить примитивным типам принимать null, то для int всё еще хватит 4 байта?
Все знают что интерфейс Clonable пустой, без единого метода и переменной, просто маркер для JVM. Вопрос — почему?

Потому что есть такой паттерн — Marker Interface, его цель — именно пометить объект.
Если бы в Object не было метода clone(), то его приходилось бы каждый раз определять самому, а так вам надо переопределять метод clone(), только если дефолтный вас не устраивает.
А если бы Clonable не было, то все объекты по умолчанию считались бы клонируемыми, тогда любой вызов метода clone() мог бы обернуться клонированием огромного графа связанных объектов. Поэтому, когда вы пишете implements Cloneable, вы как бы говорите «Я знаю, что делаю, если будут проблемы — значит сам дурак»
C Serializable все несколько забавнее.
writeObject() и readObject() предполагаются быть приватными, дабы никто кроме jvm (и соответственно рефлекшена) не имел к ним доступа.
Но как объявить приватные члены интерфейса? Никак…
Именно поэтому пришлось организовать подобную магию.

Можно было бы применить абстрактный класс, но
private abstract void writeObject(); не является допустимой конструкцией также
>> Ведь основная цель java была в облегчении жизни нам — простым разработчикам.
Это вас кто-то обманул. Главное преимущество джавы — надежность и предсказуемость. Ценой многократных, по сравнению с другими языками, усилий разработчиков.
Верно. В частности поэтому на ней пишут в банках, телекоме, на биржах и т.п. И еще, кстати, теперь это производительность, очень хорошая. Уступающая по сути, только C / C++/ Fortran-у.
Добавлю от себя:

— Stack расширяет Vector — по ООП в универе за такое 2 ставят
— CloneNotSupportedException — checked — т.е. нужно обязательно ловить
— InterruptedException — аналогично
— Каждый объект — монитор/лок (wft!)
— и еще что-то можно вспомнить, если постараться

Скорее бы замыкания! Хотя в продакшн из запустить еще не скоро получится.
Каждый объект — монитор/лок (wft!)

Почему вам это мешает?

InterruptedException — аналогично

Это очень мощное средство для того, чтоб управлять состоянием треда. Почитайте какую-нибудь хорошую книжку про concurrency в Java, увидите, что это используется довольно часто.
Мешает мне это потому что в 99% это не нужно. А тот 1% я смогу хэндлить — поэтому и UncheckedException тут намного лучше подходит.

Для монитора можно было бы сделать объект Lock extends Object с методами wait, notify, notifyAll. И на котором синхронизоваться можно. Так сделано в питоне — и ничего, все нормально.

Книжки читал.
UFO landed and left these words here
Да, устраивает.
Lock использую.

Я понимаю, зачем сделали все объекты мониторами — для синхронизации на методе. Однако я думаю, что есть решение изящнее. И практика использования того же питона показывает, что действительно есть.
Еще один интересный момент.
Класс Number изначально знает обо всех своих детях объявляя у себя методы а-ля:
public abstract double doubleValue();
ООП переворачивается в гробу ;)
Он вроде бы еще не умер, чтобы переворачиваться )
> Класс Number изначально знает обо всех своих детях

Так можно сказать, что любой абстрактный класс знает о своих детях.

Класс Number обязывает любого кто осмелился его унаследовать реализовать методы конвертации этих самых наследников в стандартные типы явы (int, float, byte, etc...). По-моему весьма предусмотрительно, т.к. пользователи любых реализаций Number бесплатно получат удобство конвертации в эти типы, а не будет городить каждый раз своё преобразование.
Вы неправы, ни один из абстрактных классов не должен знать о специфике своих наследников.
Он знает лишь об их общих чертах.

getValue() — общая черта
getInteger() — специфичная, основанная на том что один из наследников работает с Integer
Неправы таки Вы.

Дело в том, что в природе существует не так много разных Number'ов. Самых основных можно пересчитать по пальцам: byte, double, float, int, long, short. Собственно это и есть список примитивных типов явы. Работая с числами, вы в 99,9% работаете в основном с этими 6ю типами. Вот почему дизайнеры JDK обязали все реализации Number (а их, на минуточку, есть не только в JDK но и в разных там commons.lang и др. библиотеках) реализовывать приведение к примитивным типам — для удобства конечных пользователей этих реализаций Number.

Если Вам, как программисту очередной реализации Number лень/не хочется реализовывать эти методы — просто не наследуйте Number, делов-то. Только, учтите, что тогда Ваши конечные пользователи не смогут пользоваться удобствами из JDK, скажем, java.text.NumberFormat / DateFormat /… или пакет commons.lang.math и ему подобные, понимающий любой Number именно за счет обязательной реализации этих методов.
Зачем это было сделано — очевидно. Мне лишь не нравится реализация где предок знает о потомках.
Смотрите мое предложение чуть ниже. или реализацию Int32 в C#
msdn.microsoft.com/en-us/library/system.int32.aspx
, где (на мое удивление) используется тот же принцип, что и предложенный мной ниже — т.е. каждому числу навязывается набор интерфейсов для возможностей конвертации в другие форматы.
> Смотрите мое предложение чуть ниже. или реализацию Int32 в C#
msdn.microsoft.com/en-us/library/system.int32.aspx, где (на мое удивление) используется тот же принцип, что и предложенный мной ниже


Что-то Вы опять ошибаетесь. По Вашей же ссылке нет и намёка на отдельные

> интерфейсы Float/Double/Imteger/etcExportable

Однако, если открыть документацию к интерфейсу IConvertible: msdn.microsoft.com/ru-ru/library/system.iconvertible.aspx мы увидим, что это та же идея что и в яве, только выделенная в отдельный интерфейс, который (о боже!)

> изначально знает обо всех своих детях объявляя у себя методы а-ля

ToBoolean
ToByte
ToChar
ToDateTime
ToDecimal
ToDouble
Пля…
Вы правы ;)
И в шарпе фигня и в яве. Обоснованная, необходимая, понятная, но фигня неООП-шная ;)
Всё там нормально и ООПно
примитивные типы вообще стоят особняком по отношению к объектной иерархии и к ООП отношения вообще не имеют
IMNSHO все должно было бы работать иначе.
Должны были существовать интерфейсы Float/Double/Imteger/etcExportable, которые бы навязывались классу -реализации Number. Т.е. например:

class Integer extends Number implements FloatExportable, DoubleExportable, ByteExportable,…

Так было бы гораздо более по-ООПшному, согласны?
> которые бы навязывались классу -реализации Number

Обождите-ка. Как раз текущая реализация навязывает, да. А в Вашем варианте никто не мешает сделать без Ваших *Exportable

class Integer extends Number { ... }


и привет.
Вы точно внимательно прочитали мой комментарий? ;)
на всякий случай:
Integer — класс-реализация Number, и я предложил другой вариант его описания
Тот факт что switch работает только со строками, возможно, основан на следующем:
Switch по сути своей идентичен последовательности if — else, за двумя исключениями:
— более компактный синтаксис
— switch быстрее(как минимум был раньше) за счет ограниченной поддержки типов. Возможно работа со стрингАми захардкожена таким образом что equals для стринга проверяется быстрее чем equals для Object.

p.s. Это предположение
а если break не ставить в конце каждого case, то switch уже не так и идентичен if-else.
«Из этого же разряда — нету возможности отфильтровать обьекты по какому-то свойству. Всегда приходится делать это вручную. Плохо, очень плохо.»

Хотите как в CriteriaAPI?
После кодирования на haskell ну очень не хватает tuples из коробки.
Кортежи без сопоставления с образцом — малоосмысленная штука.
про Generics
" К счастью, в 7-й java это исправили. Не понятно только — зачем так долго ждали?"

Очень интересно как исправили!
надеюсь не как в С# через var?
Параметры справа можно опускать:
Map<MyKeyObject, PersistedPlan> plansById = new HashMap<>();
А как же

Map<MyKeyObject, PersistedPlan> plansById = newHashMap();

из Guava? Или вы в 2011 году не пользуетесь?
Вроде как и в 6-й(5?) можно писать еще проще
Map<Object, Object> m = new HashMap();
Map m = new HashMap<Object, Object>();

один черт — и первое и второе — фикции, а не женерики.

Или оставляя <> получаем какой то профит в проверках?
Сравните тип слева в приведённом мной варианте из Java 7 и в ваших.
«С System связано еще несколько странностей — например out и in поля класса — final. Но в то же время есть методы setOut и setIn.»

Не так красиво как хотелось бы, но вполне разумно.
Ни in, ни out не сэтятся напрямую. Их установка происходит в native методах, которым плевать на идентификатор final.
Т.е. получается что Вы можете использовать публичные in и out, но можете их изменить только через сэттеры. А в сеттерах (прикрученных много позже появления System.out) проверяются разрешения на установку ввода-вывода.

p.s. Да, наличие публичных геттеров и приватных полей было бы красивее.
То что есть сейчас — всего лишь одни из самых адекватных костылей к древнему, но широко используемому коду.
> Почему List имееет indexOf(Object o), а HashSet не имеет get(Object o), который бы по хеш коду и equals возвращал бы ссылку на обьект из коллекции.

Потому что, если этот метод зачем-то нужен, то equals реализован неправильно.
Создается впечатление, что автор имеет весьма и весьма поверхностные знания о Java.

HashSet — в большинстве языков программирования, в которых есть множества объектов, они реализованы именно таким образом. Тут не добавить, не убавить — как говориться, вам шашечки или ехать. В большинстве случаев скорость работы структуры данных много важнее ее размера.

Вы может быть еще byte используете для чисел? В современном мире byte может понадобиться только при работе с бинарными данными. И то, обычно продолжают использовать int, которому практически на всех аппаратных платформах соответствует машинный тип.

Default logger, Clonable, System, Runtime, Properties — результат исторического развития платформы. Шестнадцать лет назад у нас было гораздо меньше опыта построения крупных объектно-ориентированных систем. Джош Блок еще не писал Effective Java — он писал реализацию collections API и сам учился писать правильно. А затем никто уже ничего менять не стал из-за обратной совместимости. И поверьте мне, на сегодняшний день обратная совместимость — основная причина, по которой бизнес не боится использовать Java.

Integer, Boolean и т.п. — это результат компромисса между скоростью исполнения и удобством API. Джаве нужны были примитивные типы, которые при этом вели себя нормально с точки зрения объектной модели. Да, в современных языках умеют сделать это элегантнее, а на тот момент могли только так.

Switch — каждый Java-разработчик знает, что свич плохо пахнет. Если нужно полиморфное поведение, то в языке уже есть три механизма, его обеспечивающие. Если не хочется городить огород, то:
1. Если значения переключателя известны в момент компиляции, используем свитч.
2. Иначе — цепочку if else if — смотрится она вполне нормально.
Скажете, а как же строковые константы? — а enum на что? Или вы любите stringly-typed-код?

Многопоточность: все отлично в Java с многопоточностью, большинство языков нервно курят в сторонке. Java предлагает целый спектр возможностей: от реализаций data-parallelism через Fork/Join и Hadoop, software-transactional memory и actor model в Clojure и Akka до самого низкоуровневого взаимодействия через wait(), notifyAll() и т.п. Эрланг предлагает одно решение, бесспорно хорошее, и считает, что оно подойдет для любой проблемы. Java может все, что и Erlang, даже hot code reload можно реализовать через ClassLoaders. Единственное, что от вас требуется — выбрать наиболее подходящий инструмент для задачи. Подсказываю: wait-notify — не подходящий в 99.99% случаев.

String — про Юникод мсье не слышал? Вообще прекращайте уже на байты мастурбировать.

Замыкания: зачем ждать? Groovy, Scala, Xtend, Clojure, Mirah, javac.info Выбирайте. Annonymous Inner Classes тоже никто не отменял. Громоздко, но не более.

Числа. Точность нужна только для финансовых расчетов. При этом никто не мешает вам использовать long или int и делить на 100 при выводе. Опять же, никто не мешает вам всю работу вести с BigDecimal, а описанный мною способ применять в случае сложных вычислений. В других языках на JVM есть поддержка чисел с фиксированной точкой на уровне синтаксиса.

Java немного староват как язык, но отнюдь не плох. Вы вряд ли найдете настолько солидное сочетание скорости работы и огромного объема уже реализованной функциональности. 99% языков до нее не дотягивают: некорректно работают со строками, или с числами, или с датами, многие из них имеют нестабильные реализации, у многих проблема с обратной совместимостью. С другой стороны вас никто не заставляет все делать на одном языке: use right tool for a job.
UFO landed and left these words here
UFO landed and left these words here
Извините, но некоторые моменты просто бредовые. А тотальный подсчет байтиков больше похож на паранойю.
Меня в Яве огорчает то, что там стандартную библиотеку (rt.jar) разработчики поленились и написали на Яве, а не на Си, например. Отвратительно. Какой в ней смысл? На Яве накривокодить любой может и сам.

Ну а switch с объектами и HashMap для объектов — это маразм. Кому без этого совсем никак, приделывает к объекту метод getUniqueId() и не парится.
> на Яве, а не на Си, например. Отвратительно. Какой в ней смысл?

Так портируемость же. Чем меньше написано на си — тем легче портировать.
Очевидная нелепость. Зачем писать стандартную библиотеку Java на С? Как вы будете использовать классы из нее в Java? JNI не поддерживает прямой маппинг между классами Java /C++, и хорошо что не поддерживает.
На самом деле очень немногие компоненты JRE написаны на С. JVM, сама java.exe, интерпрератор байткода, JIT, GC. Из библиотеки классов только в отдельных местах, там где идет работа с IO, threads etc, используются native методы.
UFO landed and left these words here
А меня тоже почти весь ваш список дико бесит в яве. Я с вами. Еще дико бесит одно из ограничений генериков. Я хочу иметь возможность сделать Map<Class, T>, чтобы оно понимало, что я по классу кладу инстанс объекта, а не кастить везде явно. В остальном генерики явы только радуют. А вот про HashSet и все, что с ним связано много можно понаписать, объяснение простое, язык очень консервативный, ибо таков и корпоративный рынок. И хоть я понимаю, почему каждый пункт сделан так как сделан, все равно бесит. И бесит вместо HashSet использовать свою реализацию через HashMap, как раз для того, чтобы не создавать ентри на каждое обновление объекта в коллекции и иметь возможность реализовать метод get(Object o).
Я утешаюсь, что чем проще язык и чем меньше у него конструкций, тем сложнее на нем написать жуткую нечитаемую фигню, с-но проще чужой код на нем читать. А то можно действительно в DSL уйти и прочее метапрограмирование.
Only those users with full accounts are able to leave comments. Log in, please.