uk
Feedback

Не попадись на ботовода! Telemetrio знаходить і позначає такі канали мітками 👉 Хочеш бачити мітку, оформляй підписку

По-явански

По-явански

Відкрити в Telegram

Пишу про Java-технологии, Kotlin и Android-разработку. Редко но метко. Кодревью: http://javanese.online/%D1%80%D0%B0%D0%B7%D0%B1%D0%BE%D1%80_%D0%BA%D0%BE%D0%B4%D0%B0/ Поболтать IRL: @coffeeCodeEverywhere Автор: @Harmonizr Менеджер по рекламе: /dev/null

Показати більше
827
Підписники
Немає даних24 години
+17 днів
+530 днів
Архів дописів
В подвале клуба любителей Kotlin обнаружен static Эмпирически доступ к статикам на примере Java можно определить так: слева от . или :: стоит тип.
Class.staticField
Class.staticMethod()
Class::staticMethod
var cls = Class // error
Отсутствие static на примере Kotlin: при доступе к членам слева от . или :: всегда стоит выражение.
obj.prop
obj::prop
WithCompanion.func()
WithCompanion::func
// можно передать, присвоить:
val cmp = WithCompanion
Есть одно исключение: энамы.
SomeEnum.Constant
SomeEnum::Constant
val expr = SomeEnum // error
Если у энам-класса нет компаньона, то имя класса не будет выражением. А следовательно, enum-константы (а ещё values(), entries, valueOf()) — это статические члены! В спеке языка они называются static member callables.

🔌 Навалил фиксов 🔴 убрал фолдинг для backing property (фича появилась в языке) 🔴 убрал инспекцию @TargetApi → @RequiresApi (аналогичная появилась в Android Lint) 🟡 поправил хинты для вараргов и апкастов 🟡 починил фриз при анализе векторов 🟡 заткнул «Heavyweight property delegation» в тестах 🟢 добавил проверки pathData в строковых литералах 😼 GitHub 🛒 JB Marketplace

Корень корня всех зол Потрясающая археологическая работа по поиску истоков известнейшей фразы о преждевременной оптимизаци. T
Корень корня всех зол Потрясающая археологическая работа по поиску истоков известнейшей фразы о преждевременной оптимизаци. Tl;dr заявление о том, что 97% кода можно не оптимизировать, но нужно знать, какие именно 3% кода «горячие», строится на анализе существовавших на момент написания статьи программ, которые по современным меркам считались бы очень простыми и маленькими. Зачастую там действительно был один основной цикл, а производительность всего остального не имела значения.

Selector expression На стыке функций-расширений и функционального типа есть такой замечательный конструктор типа: T.() -> R. Выражения таких типов спокойно кастятся к (T) -> R и обратно, а func(args) привычно раскрывается в func.invoke(args). Но у варианта-расширения есть и особый синтаксис: receiver.func(args). Он логичный, понятный и консистентен с написанием типа и синтаксисом вызова расширений.
val b = String::isBlank
"".b()
А ещё он открывает нам знатный инсайд: между receiver. и (args) может находиться не только идентификатор, но и переменная, вернее, выражение! Для начала — несколько бесполезных способов записать "".isBlank(): • "".(String::isBlank)() — function reference • "".(fun String.() = isBlank())() — анонимная функция • "".({ s: String -> s.isBlank() } as String.() -> Boolean)() — лямбда • "".(providePredicate())() — произвольный вызов, возвращающий функцию Уверен, что это побочка, вызванная там, как устроен синтаксис expr.variable(). Тем не менее, в следующем примере:
value
    .validate()
    .let {
        val t =
            if (simple) transform
            else provideComplicatedTransform()
        it.t(args)
    }
    .serialize()
можно избавиться от let с лишней переменной:
value
    .validate()
    .(
        if (simple) transform
        else provideComplicatedTransform()
    )(args)
    .serialize()
что читается более последовательно и выглядит менее мусорно.

Что значит «no-cache», хвостовики, 1xx коды состояния, опечатки и прочие необычности в привычном HTTP. В статье с просторов интернета: HTTPWTF. Необычное в обычном протоколе.

Что в сигнатуре тебе моей? Есть в kotlin-stdlib функция text.ifEmpty { default }. Реализована она крайне просто: if (isEmpty()) defaultValue() else this. Интересна же функция тем, как она обходится с типами:
"".ifEmpty { "" } // String
"".ifEmpty { null } // String?
stringBuilder.ifEmpty { "" } // CharSequence & …
"".ifEmpty { 1 } // Comparable<*> & Serializable
Такой вывод типов следует из сигнатуры. Давайте разберём поэтапно, в чём там волшебство. Например, если бы сигнатура была String.ifEmpty(() -> String): String то такое использование было бы невозможно: stringBuilder.ifEmpty { null } ❗️ ресивер должен быть строкой, а не билдером, ❗️ лямбда должна возвращать строку, а не null. То есть к функции можно сформулировать такие требования: 🔹 тип ресивера должен быть как можно шире. Функция isEmpty() объявлена у CharSequence — он-то нам и нужен, 🔹 тип дефолта должен быть ещё шире: там хоть null, хоть попугаи, хоть козявки — мы с ними никак не взаимодействуем, 🔹 возвращаемый тип должен сужаться по типам параметров: если там нет нуллов — их не должно быть на выходе и так далее. Пусть будет такая сигнатура: fun <C : CharSequence, T, R> C.ifEmpty(() -> T): R. Что будем писать в where? Объединений и пересечений в Котлине нет, то есть на месте R нельзя написать ни C & T, ни C | T. Интуитивно хочется написать R : C, R : T. Но это неверно: тут написано «возврат у́же, чем ресивер, и у́же, чем дефолт». У нас же требование противоположное: C и T должны «пролезть» на выход, через R. Правильное напрваление — C : R, T : R. Правда, компилятор корёжит, что C и так : CharSequence, куда тут ещё баунд. Настоящая сигнатура:
@kotlin.internal.InlineOnly
public inline fun <C, R> C.ifEmpty(
    defaultValue: () -> R,
): R where C : CharSequence, C : R {
Ошибку компиляции здесь затыкает приватная аннотация InlineOnly. Нам так делать нельзя, зато можно @Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER"). Обсуждение синтаксиса, который позволял бы писать подобные сигнатуры без изврата: KT-209

— Добро пожаловать на курс экстремистского программирования! — Может, экстремального? — Может...

Двуязычный API Положим, есть функция на Kotlin: fun <R> exec(f: () -> R): R её, конечно, можно использовать из Java: exec(() -> { return Unit.INSTANCE; }); но выглядит это убого. Для создания более дружелюбного интерфейса можно сделать следующее: 1. добавить перегрузку с «родным» для Java функциональным интерфейсом fun <R> exec(f: Supplier<R>): R 2. добавить частный случай для R = Unit fun exec(f: Runnable) 3. спрятать ЪКотлинЪ-вариант от Джавы @JvmSynthetic fun … либо сделать его менее заметным @JvmName("exec_4k") fun … 4. сделать ЪДжаваЪ-вариант менее заметным в Котлине @JvmName("exec") fun exec_4j(… Итого:
@JvmName("exec_4k")
fun <R> exec(f: () -> R): R = f()

@JvmName("exec")
fun <R> exec_4j(f: Supplier<R>): R = f.get()

@JvmName("exec")
fun exec_4j(f: Runnable) = f.run()

🔌 Поддержка K2 готова, а вместе с ней • улучшен фикс в инспекции по ловле регекспов [а-яА-Я] • жалуюсь на "#ЦЦВВЕЕТТ".toColorInt() так же, как на Color.parseColor • блюду порядок сортировки атрибутов стиля при вызове context.withStyledAttributes так же, как и для context.obtainStyledSttributes В целом получился чисто технический релиз, основная фишка лишь в том, что теперь плагин работает 😌

Роберт Лафоре. Структуры данных и алгоритмы Java, второе издание Чувствуется, что Java — не родной, не любимый язык автора: кодстайл не соблюдается, классы и файлы порой называются со строчной буквы. Имена методов часто не передают их сути. Первый же пример кода (с. 32) не скомпилируется. Скоупы переменных чересчур широкие. Положение фигурных скобок выбрано рандомом (попробуй разбери вложенность на с. 167). Вместо стандартного toString (EJ3*, Item 12) у многих классов есть функция, которая печатает содержимое в stdout. Наличие псевдополя length у массивов игнорируется, хотя во введении в Джаву автор объяснял про него. Модификатор final не используется. int[] после создания вручную заполняется нулями. Ну и так далее — короче, с языком полный провал. С момента выхода оригинала (2003, SAMS, времена Java 1.4) до перевода в моих руках (2019, Питер, Java 11) прошла целая эпоха. Было очень наивными решением издавать книгу без изменений. Например, для демонстрации материала автор написал несколько апплетов — олды здесь? Непосвещённый читатель задолбается их запускать. Также автор по какой-то причине упоминает классы Stack, Vector и Hashtable. Хоть дженерики пока не появились, но привычные нам коллекции тогда уже завезли. Работа с массивами. Понимаю, что пример, где делают всё ручками, быть должен — это закон жанра. Но как насчёт использования методов indexOf*, System.arraycopy, Arrays.binarySearch в дальнейшем? Без них в реальном мире жить неуютно, как и говорить о сортировке, не упоминая компараторов. Везде, где автор использует массивы, нет примеров ресайза — а неискушённый читатель может и не знать про Arrays.copyOf. Не понятно, что автору мешало в своих коллекциях сделать инициализацию готовым массивом. Десять строчек с arr.insert() — это ж боль. Кстати, видите утечку памяти на с. 77? А она есть! См. EJ3*, Item 29, первый листинг. Дэку уделён один абзац — мол, такая штука есть, но не очень нужна. На самом деле ArrayDeque (кольцевой буфер) — это самая универсальная коллекция во всём Котлине: она тебе и вектор, и обе очереди. О хорошем. В книге доходчиво объясняется асимптотика, вплоть до изображений графиков и разъяснения, что такое логарифм — если кто не знал, а потом забыл. Алгоритмы сортировки хорошо объясняются на словах и вдобавок визуализируются. Деревья и графы визуализируются — а это очень важно для понимания темы. Двоичные деревья (поиска и пирамида a.k.a. куча), красно-чёрные, В-деревья — это всё очень интересно и увлекательно (хотя к-ч я так до конца и не понял). Есть перевод математических выражений в префиксную нотацию, есть префиксное дерево. Можно было бы и про AST немного поговорить :) Связанный список. Автор утверждает, что эта структура данных, в отличие от вектора, использует память эффективно, ведь пустой хвост массива отсутствует. Посчитаем? Полупустой массив занимает 2n памяти. Узлы же, в каждом из которых есть ссылка на данные, следующий узел, невидимая ссылка на класс и пэддиннг, занимает 4n. Частота и сложность аллокаций, локальность — тут только свернуться клубочком и плакать. Ещё один момент из реальной жизни забыт: проверки корректности входных данных. Книжный стек считает, что пользователь сам проверит, не пустой ли он. Наивно! (EJ3*, Item 49) В рекомендациях по выбору структуры данных делается нелепое допущение: проще — лучше, ведь тебе придётся реализовывать алгоритм самому! Доходит до того, что вектор при малом объёме данных стоит предпочесть хэш-таблице. На деле же критерий выбора звучит так: нужен порядок вставки или доступ по ключу? Вердикт: интересная книга. Читать только под присмотром взрослых. *EJ3 — Joshua Bloch. Effective Java, Third Edition *Arrays.indexOf не существует. Его надо написать один раз либо использовать методы из ArraysKt

Ещё тезис: не доверяйте людям, которые не рискуют собственной шкурой. Например, инвестиционные рекомендации ценны только тогда, когда исходят от человека, сделавшего на этом состояние. Это обобщение принципа, который я давно исповедую: обучать должен только тот, поднялся на практике предмета, а не на преподавании. Легко учить убивать драконов: никто не заявит, что твои приёмы против них неэффективны. Только вот в этом лесу нет никаких драконов.

Довольно интересная книга, и, как видно по закладкам, вызывающая у меня обилие разнообразных реакций. Близко к сердцу пришлас
Довольно интересная книга, и, как видно по закладкам, вызывающая у меня обилие разнообразных реакций. Близко к сердцу пришлась мысль, что большое — хрупко: оно сложное, внесение изменений аукается неожиданными последствиями, а внезапные встряски могут всё разрушить непредсказуемым образом. Что в очередной раз возвращает нас к философии Unix. :)

Раздельная компиляция, min, compile, target В целом Java-технологии привычные к раздельной компиляции. Серверные приложения компилируются с Java Class Library, которая подкладывается в compile classpath при сборке, а запускаются с другим экземпляром JCL, которая лежит на сервере. Однако, эта раздельность почти не чувствуется. Она незримо присутствует и заключается в том, что в итоговом jar у нас не лежит весь java.lang, java.util и что мы там ещё любим. Мы запускаем приложение с той же версией JDK, с которой собирали, и всё просто работает. Таким образом, приложение разрабатывается под одну версию платформы, без оглядки на более старые и с надеждой на совместимость с новыми. В desktop-приложениях (кто-то их ещё пишет?) всё ещё проще: зачастую JVM приносят с собой, и раздельной компиляции как не бывало. В Android же, как известно, зоопарк: на конечных устройствах много разных версий. И вот тут система сборки спроектирована очень удачно: • compile SDK — последняя версия, известная разработчику приложения на момент сборки. Она подкладывается в compile classpath, за счёт чего разработчик видит все новые фичи платформы; • min SDK — минимальная поддерживаемая версия. Старой платформе, которую разработчик не хочет поддерживать, позволяет отклонять установку приложений. Инструментарию позволяет подсказать программисту, что те или иные declarations недоступны в min и могут отсутствовать в runtime classpath, поэтому нужно обернуть их использование в if; • target SDK — версия, на поведение которой рассчитывает приложение. Позволяет более новой версии платформы сохранить поведение старой в старом приложении. Таким образом, в быстро меняющемся мире Android-приложение компилируется для свежей версии платформы (compile SDK), ифами поддерживает более старые версии (вплоть до min SDK), а платформа, если она новее, чем ожидаемая (target SDK), сохраняет старое совместимое поведение (тоже ифами). Это прекрасно. Это шедевр. Для сравнения, ситуация в мире плагинов для IntelliJ: выбираешь одну версию, которая и будет твоим compile и min. Хочешь поддержать постарше — опускаешь версию, перестаёшь видеть новые declarations. Если в новых версиях что-то deprecated — ты об этом узнаешь на этапе валидации плагина, где-то после компиляции и упаковки. Именно поэтому при обновлении IDE плагины часто либо отключаются (разработчик указал максимальную поддерживаемую версию), либо разваливаются (мой вариант:). Зависишь от других плагинов? Вообще страдай. Там может быть установлена любая версия. Например, если зависишь от Android-плагина для IDE, то при компиляции видишь версию, которой полгода, а в бетах Android Studio уже много раз переименовали классы, поменяли на интерфейсы, переместили в другой пакет. (И сделали это в стенах той же компании, где придумали min, compile, target.) Удачи!

Раздельная компиляция, min, compile, target В целом Java-технологии привычные к раздельной компиляции. Серверные приложения компилируются с Java Class Library которая подкладывается в compile classpath при сборке, а запускаются с другим экземпляром JCL, которая лежит на сервере. Однако, эта раздельность почти не чувствуется. Она незримо присутствует и заключается в том, что в итоговом jar у нас не лежит весь java.lang, java.util и что мы там ещё любим. Мы запускаем приложение с той же версией JDK, с которой собирали, и всё просто работает. В desktop-приложениях (кто-то их ещё пишет?) всё ещё проще — зачастую JVM приносят с собой, и раздельной компиляции как не бывало. В Android же, как известно, зоопарк: на конечных устройствах много разных версий. И вот тут система сборки спроектирована очень удачно: • compile SDK — последняя версия, известная разработчику приложения на момент сборки. Она подкладывается в compile classpath, за счёт чего разработчик видит все новые фичи платформы.

Размножение массивов String::repeat — произведение искусства. Arrays.fill же написан банально и буднично, но я подозреваю, что он успешно векторизуется.

Пару слов про зерокодинг и ни одной шутки про зерозарплатинг Да, из готовых компонентов можно собрать сайт, приложение или круд. Оно даже будет работать. Я смотрю на это просто как на программирование на более высоком уровне. Что здесь примечательно для нас, разработчиков? А то, что компоненты «среднего» уровня написаны такими же программистами. И если менеджер мышкой натыкивает приложение быстрее, чем профессиональный разработчик, то у нас, коллеги, наблюдается проблема с job security качеством и доступностью компонентов. Если OAuth из конструктора интегрируется быстрее и работает лучше, чем решение с гитхаба, значит, мы залайкали не тот репозиторий, автор его недодокументировал или вообще бросил на полпути. Если нам данные приходят по одной схеме, и мы, чтобы трансформировать их в другую, удобную нам, выражаем обе через ДТОшки и пишем маппер между ними, значит, библиотека, которой мы намазываем данные на классы, не решает нашу задачу. Если прокладка для работы с БД заставляет нас… ну и так далее. Зерокодинг никогда не станет мощнее и гибче, чем «настоящее» программирование. А наша задача — делать так, чтобы последнее не отставало по скорости и качеству. Хочу надеяться, что конкуренция со стороны зерокодинга поспособствует повышению качества нашего родного программирования.

Один мой коллега постоянно забывал, как кого зовут, из-за чего в нашем лексиконе появились два собирательных персонажа — Кирилл Гладков и Алексей Розов. Дошутились таки!

Если вы когда-нибудь искали способ поставить бряку для одного конкретного экземпляра, то… вот он!
Если вы когда-нибудь искали способ поставить бряку для одного конкретного экземпляра, то… вот он!

Привет, я подсяду? Поговорим о корпоративной коррупции? Давайте разберёмся, как в опенсорс попадают всякие технологии. Вот мне кусочек рационального мышления подсказывает, что компания решает свои задачи, свои проблемы, выпускает что-то для себя, внедряет технологию в свои процессы, извлекает из этого коммерческую выгоду, и — почему бы нет — делится с другими, собирая багрепорты от широкой аудитории и повышая качество — а это снова выгода. Ну, логично, мне кажется. Вроде как-то так должно работать. Теперь давайте прикола ради глянем библиотеку, которая не работает: она должна помогать следить за внешним видом приложения, но даже стандартную кнопочку со стандартным stateListAnimator'ом в дефолтной теме скриншотит неправильно, не говоря уже про скруглённые аватарки, которые у них в собственном приложении таки есть, судя по нагугленным скриншотам. Чистая прибыль в 23 гигабакса даёт о себе знать, нужно срочно стартовать новый проект, обосновать его целесообразность, выбить финансирование. Написанный код потом можно выкинуть — не прижился. Ах да. PixelCopy.request. Шах и мат, аферисты.

ой
ой