Category Archives: Мысли вслух

Generics, type inference и casting в java

Written by elwood

Написал я тут обобщённый метод, который выглядит так:

private <T> T getParamValue(ProceedingJoinPoint joinPoint, String name) {
    if (! (joinPoint.getSignature() instanceof CodeSignature))
        throw new RuntimeException( "JoinPoint is not a method." );
    String[] parameterNames = (( CodeSignature ) joinPoint.getSignature()).getParameterNames();
    int index = -1;
    for (int i = 0; i < parameterNames.length; i++) {
        if (parameterNames[i].equals( name )) {
            index = i;
            break;
        }
    }
    if (-1 == index)
        throw new RuntimeException( String.format( "Parameter '%s' not found.", name ) );
    return ( T ) joinPoint.getArgs()[index];
}

В принципе, тело метода мало чем интересно. Интересен оператор return, который выполняет приведение типа к T. Мне захотелось разобраться, как работает этот cast и почему эта конструкция работоспособна с любым типом T. Известно, что в java оператор приведения типа может привести к ClassCastException во время выполнения, однако с другой стороны мы знаем, что джавские дженерики компилируются таким образом, что в class-файлах информации о типах не остаётся, и компилятор не может добавить в код метода инструкцию приведения типа к типу T. Значит, скорее всего, приведение типа к типу-аргументу не порождает байткод, в отличие от обычного приведения к конкретному типу, известному на момент компиляции. Эту гипотезу легко проверить, берём декомпилятор, и – да, действительно, он декомпилирует последнюю строчку как

return joinPoint.getArgs()[index];

Как мы видим, компилятор сгенерировал метод, возвращающий Object, а для того, чтобы что-то превратить в Object, не нужно выполнять checkcast (байткод операции проверки приведения типа). Так и работают обобщения в Java. Компилятор превращает переменные типов T в переменные типа Object, и при присвоении

T var = someObject;

компилятор генерирует код

Object var = someObject;

А вот когда наоборот объект обобщённого типа мы присваиваем переменной конкретного типа, то компилятор добавляет байткод checkcast:

String str = this.<String>getParamValue();

превращается в

String str = (String) this.getParamValue(); // this.getParamValue() возвращает Object

Так всё и работает. Поэтому в принципе не так уж и важно, правильно ли компилятор выведет тип при вызове getParamValue(). В любом случае будет вызов метода, возвращающего Object, и последующий каст к конкретному типу переменной (поля, аргумента функции).

Примитивные типы обрабатываются несколько особым образом. Допустим, мы вызываем наш метод и пытаемся присводить результат переменной-примитиву:

long id = getParamValue();

Кажется, что здесь может потребоваться явное указание типа T, так как long не может выступать в качестве типа-аргумента, однако компилятор достаточно умён и выполнит вывод ссылочного типа Long, соответствующего примитивному типу long, автоматически, и ещё добавит анбоксинг:

long id = ((Long) getParamValue()).longValue();

Осталось рассмотреть предупреждение компилятора “Unchecked cast: X to Y”, которое выдается при компиляции кода, преобразующего Object в обобщённый тип, либо в тип, зависящий от обобщённого типа:

List<String> list = (List<String>) map.get("list");

Теперь нам понятно, почему компилятор выдаёт это предупреждение. Инструкция checkcast добавляется в сгенерированный байткод, но она проверяет только то, что объект является списком List. Но то, что этот список был создан с типом-аргументом String, эта инструкция проверить не может. Аналогичное предупреждение мы получаем в строчке

return ( T ) joinPoint.getArgs()[index];

– и тут компилятор вообще ничем не может нам помочь, поскольку информация о T будет недоступна во время выполнения, переменная будет иметь тип Object, и мы не сможем быть уверены, что там хранится объект типа T, а не что-либо другое.

Размышления о dura lex

Written by elwood

assets-newsletter-copyrightlarge

Навеяно хабрапостом http://habrahabr.ru/post/194740/

Captain mode on. Все проблемы оттого, что у каждого человека своё понимание “справедливости” оценки того или иного труда. Каждый стремится максимизировать свою выгоду. Издатели выступают против “пиратов”, “пираты” – против издателей. Авторы, посредники, пираты и потребители – все лишь ищут способ, как бы урвать побольше. Можно и не ломать голову над тем, какой должна быть “справедливая система”. Её нельзя описать в рамках текущих представлений. Каждый в любом случае будет тянуть одеяло на себя. Возможно, справедливым распределением вознаграждения за труды была бы оценка трудоемкости этого труда. Вне зависимости от степени гениальности получившегося. Соответственно, при этой системе каждый человек бы получал свое вознаграждение пропорционально затраченному труду, а результат его деятельности тут же становился доступным всем остальным людям. Но это коммунизм получается, и мало кто на это согласится, все же мечтают стать гениями и обеспечить себя сверхприбылями (даже несмотря на осознание маловероятности этого события). Поэтому ничего не изменится. Скорее всего, со временем, вектор продолжит сдвигаться в сторону большинства, то есть, потребителей. А гениям и издателям останутся большие, но не сверхбольшие прибыли.

Хочу нормальную систему размещения в HTML

Written by elwood

В очередной раз ковыряясь с CSS-ужасом, задумывались ли вы о том, что всё это можно было бы сделать гораздо проще ? Простой пример. Есть div, занимающий некоторое пространство. Внутри него нужно разместить 2 div’a так, чтобы первый занимал по высоте столько, сколько нужно его содержимому, а второй – всё оставшееся пространство. Если вы полезли в гугл и стекофервлоу, могу вам сразу сказать – это невозможно. Да, вы не ослышались, нельзя никак этого добиться. Вы можете указывать абсолютное значение высоты первого div’a, можете использовать трюки с overflow:hidden (правда, почему-то советующие это забывают указать, что если ваше содержимое не влезет в блок, то вы его уже не увидите), можете пытаться что-то сделать с абсолютным позиционированием (тут скорее всего у вас отвалится скроллинг), можете играться с padding-top: -30000; margin-top: 30000. Но всегда будете сталкиваться с проблемами. Потому что по-нормальному это сделать вообще никак нельзя. В CSS3 появилась возможность определить размеры используя вычисления типа 100% – 25px. Но чтобы вычесть что-то, нужно это что-то знать, а узнать размер заголовка или другого блока можно только из джаваскрипта. То есть, в 21 веке в 2013 году HTML стал настолько крут, что в нём нельзя описать простейший случай размещения. Facepalm.

Ещё один пример (из недавнего). Есть div, плавающий слева. Есть плавающий справа. Тот, который слева, занимает чуть больше места по высоте, а правый выровнен с ним по верхней границе. А хочется, чтобы они были на одной линии снизу. Опять же, после получаса поисков по stackoverflow приходим к выводу, что этого тоже нельзя сделать! Ни vertical-align, ни различные комбинации вложенных блоков не спасают отца русской демократии. Самым лучшим способом, который работает, опять же становится явное задание line-height в пикселях. Я не знаю, чем думали проектировщики CSS и HTML, но это просто феерия абсурда. Расскажите об этом любому человеку, кто имел дело с WPF, и он будет смеяться гомерическим хохотом.

В общем, я тут подумываю над тем, как можно эту ситуацию исправить, и пока что не нашел более разумного решения, чем написание джаваскриптового движка, который бы поддерживал “Advanced” сценарии размещения, активизируясь при загрузке страницы. Сами правила задавались бы либо атрибутами DOM-элементов, либо как-то отдельно через js. Поискал в интернете что-то подобное, пока ничего не нашел. Как я понимаю, сейчас народ предпочитает продолжать обниматься с кактусом, в лучшем случае используя CSS-фреймворки с сеточной версткой. Но если вы случайно наткнётесь на что-то в этом роде, дайте знать.