Почему я не могу использовать шаблон с методами, получающими параметризованный аргумент? - PullRequest
2 голосов
/ 28 января 2010

Например, я использую метод Measure.doubleValue(Unit<?> unit), который возвращает значение double измерения, выраженное в указанном Unit. Если я передаю ей переменную Unit<?>, я получаю все еще очень загадочное (для меня) сообщение об ошибке:

The method doubleValue(Unit<capture#27-of ?>) in the type Measurable<capture#27-of ?> is not applicable for the arguments (Unit<capture#28-of ?>)

Буду признателен, если кто-нибудь сможет объяснить, что означает это #27-of ? (или любое другое число), и если есть изящный способ избавиться от этого. До сих пор я удаляю <?> и устанавливаю вызывающий метод @SuppressWarnings("unchecked") (поэтому я передаю непроверенный Unit вместо Unit<?>), и все работает, как нужно, но мне просто интересно, и я ощущение, что подавление предупреждений не является хорошей практикой (разве это не похоже на пустые блоки catch?).

Спасибо!

Редактировать: добавление некоторого кода.

(извините, это довольно долго, но подробно объясняет, на чем я застрял.)

Я использую JSR-275 версия 0.9.4 (последняя версия).

Итак ... Если я напишу это (очень тупой пример):

Measure measure = Measure.valueOf("3 m");
measure = Measure.valueOf(measure.doubleValue(Unit.valueOf("km")), Unit.valueOf("km"));
System.out.println(measure);

Работает и печатает "0,0030 км". Но я получаю предупреждение " Мера является необработанным типом. Ссылки на универсальный тип Мера должны быть параметризованы " в течение первого Measure вхождения и предупреждения " Безопасность типов: метод doubleValue (Unit ) относится к необработанному типу Измеримый. Ссылки на универсальный тип Измеримый должны быть параметризованы"более measure.doubleValue(Unit.valueOf("km")).

Увидев эти предупреждения, я подумал, что смогу скорректировать это (только первая строка):

Measure<Length> measure = Measure.valueOf("3 m");

И тогда я получаю сообщение об ошибке в правой части назначения " Несоответствие типов: невозможно преобразовать из Меры В Меру ". Он (Затмение) предлагает мне привести правильную часть к (Measure&lt;Length>). Но затем в правой части появляется предупреждающее сообщение « Безопасность типа: не проверено приведение от Меры <захват № 1-из?> К Мере ». Предлагаемое исправление: @SuppressWarnings, которого я предпочел бы избежать (я полагаю, по параноидальным причинам).

Итак, я возвращаюсь к Measure measure = Measure.valueOf("3 m"); и пытаюсь присвоить подстановочный знак Measure, поскольку, очевидно, он не знает, что означает "3 м" в данный момент. Это может быть Length, но также Mass или Time. Итак, я получаю:

Measure<?> measure = Measure.valueOf("3 m");

И нет предупреждений или ошибок в этой строке; фантастика. Но во второй строке:

measure = Measure.valueOf(measure.doubleValue(Unit.valueOf("km")), Unit.valueOf("km"));

Я получаю сообщение об ошибке для doubleValue: " Метод doubleValue (Unit ) В типе Measurable Не применим для аргументов ( Юнит <захват № 4-из?>)". Он предлагает разыграть Unit.valueOf("km") как (Unit<?>). Хорошо. Теперь я получаю сообщение об ошибке в том же месте: " Метод doubleValue (Unit ) В типе Measurable Не применим для аргументов (Unit < захватить № 5-из?>)". Обратите внимание, что числа изменились, так что это не совсем те же параметры, но по той же причине. Затем он делает то же самое предложение, которое не приводит к каким-либо изменениям в коде, поскольку это уже было сделано.

Так вот что меня беспокоит. Единственный способ заставить его работать, кажется, @SuppressWarnings или просто игнорировать их. Разве это не странно?

Ответы [ 4 ]

3 голосов
/ 28 января 2010

Класс меры выглядит примерно так?

class Measure<T> {

  double doubleValue(Unit<T> unit) {
    ...
  }

}

В этом случае наличие ссылочного типа Measure<?> не означает, что вам разрешено передавать любой тип Unit методу doubleValue. Скорее это означает, что экземпляр Measure имеет неизвестный универсальный тип, и небезопасно передавать Unit в его метод doubleValue, поскольку компилятор не может гарантировать совместимость типов.

Подстановочный знак не означает «любой тип»; это означает «неизвестный тип».


Обновление:

valueOf(CharSequence) возвращает неизвестный тип Measure - Measure<?>. Чтобы безопасно преобразовать это в тип Measure, который вы ожидаете, вы должны использовать метод Measure.asType(). Аналогично с целью Unit, созданной методом Unit.valueOf(CharSequence).

Measure<?> unknownMeasure = valueOf("3 m");
Unit<?> unknownUnit = Unit.valueOf("km");
Measure<Length> length = unknownMeasure.asType(Length.class);
Unit<Length> kilometer = unknownUnit.asType(Length.class);
length.doubleValue(kilometer);

Посмотрите примеры в документации класса Measure. Они обеспечат некоторую дополнительную глубину.

2 голосов
/ 28 января 2010

Подстановочный знак в шаблонах Java означает «неизвестный тип». Здесь вы определяете метод doubleValue() как принимающий Unit<something1>, где something1 не указан методом. Затем вы передаете значение, которое вызывающий абонент знает как Unit<something2> для некоторого неизвестного something2. Сообщение об ошибке компилятора означает, что нет ничего, что гарантировало бы, что something1 и something2 обозначают одно и то же.

Попробуйте это:

<T> double doubleValue(Unit<T> unit)
{
    ...
}

, что означает, что doubleValue() не волнует о том, что такое T.

1 голос
/ 28 января 2010

Чтобы расширить другие (превосходные) ответы, но быть немного более специфичным для JSR-275 (сейчас я использую его для проекта).

Этот бит интересен

Исправление предложило: @SuppressWarnings, которых я предпочел бы избегать (по параноидальным причинам, я думаю).

Вы правы быть параноиком, но подумайте об этом: вы говорите классу Measure, что нужно проанализировать произвольный String и вернуть Measure любого типа. Очевидно, что в этом случае вы можете получить или не получить Measure<Length> (вы можете передать «3 кг»), поэтому единственная опция, оставленная библиотеке, - это возврат Measure<?>. Если вы ХОТИТЕ Measure<Length>, то вам придется каким-то образом привести его к одному - это не может быть гарантировано безопасным во время компиляции.

В этом случае я бы сказал, что @SuppressWarnings вполне приемлемо, при условии, что вы знаете, что строка всегда будет действительной длины. Если нет, вы задерживаете неизбежное ClassCastException до позднего времени, что довольно плохо.

Но еще лучше (ура!) JSR-275 действительно дает вам способ обойти это, что толкает обработку ошибок в «правильное» место (во время получения Measure, а не в какой-то момент после этого, когда он используется). Попробуйте

Measure<Length> = Measure.valueOf("3 m").asType(Length.class);

asType возвращает соответствующую универсальную версию Measure или завершается неудачно с исключением, если размер анализируемой единицы НЕ равен длине - это почти наверняка то, что вы хотите, верно?

Я думаю, что это решит вашу проблему.

0 голосов
/ 28 января 2010

Я не могу воспроизвести вашу проблему, т.е. следующие компиляторы нормально (по крайней мере с затмением):

static double doubleValue(Unit<?> unit) {
    return 0;
}

static void bla(Unit<?> u) {
    doubleValue(u);
}

public static void main(String[] args) {
    doubleValue(new Unit<String>());
}
...