Я недавно сталкивался с @SafeVarargs
аннотацией Java . Поиск в Google, что делает небезопасным функцию с переменным числом аргументов, оставил меня в замешательстве (отравление кучи? Стертые типы?), Поэтому я хотел бы знать несколько вещей:
Что делает вариативную функцию Java небезопасной в этом
@SafeVarargs
смысле (желательно, в форме подробного примера)?Почему эта аннотация оставлена на усмотрение программиста? Разве это не то, что должен проверять компилятор?
Есть ли какой-то стандарт, которому нужно следовать, чтобы гарантировать, что его функция действительно безопасна? Если нет, каковы лучшие методы для обеспечения этого?
retType myMethod(Arg first, Arg... others)
. Если вы не укажетеfirst
, пустой массив будет разрешен, и у вас вполне может быть метод с тем же именем и с тем же типом возврата, не принимающий аргументов, а это означает, что JVM будет трудно определить, какой метод следует вызывать.Ответы:
1) В Интернете и на StackOverflow есть много примеров конкретной проблемы с дженериками и переменными. По сути, это когда у вас есть переменное число аргументов типа параметр типа:
В Java varargs - это синтаксический сахар, который подвергается простой «перезаписи» во время компиляции: параметр типа varargs
X...
преобразуется в параметр типаX[]
; и каждый раз, когда вызывается этот метод varargs, компилятор собирает все «переменные аргументы», которые входят в параметр varargs, и создает массив точно так жеnew X[] { ...(arguments go here)... }
.Это хорошо работает, когда тип varargs подобен бетону
String...
. Когда это переменная типаT...
, например , она также работает, когдаT
известно, что это конкретный тип для этого вызова. например , если метод выше , были частью классаFoo<T>
, и у вас естьFoo<String>
ссылка, то вызовfoo
на это будет хорошо , потому что мы знаем ,T
этоString
в тот момент в коде.Однако он не работает, когда «значение»
T
является другим параметром типа. В Java невозможно создать массив типа компонента с параметром типа (new T[] { ... }
). Таким образом, Java вместо этого используетnew Object[] { ... }
(здесьObject
верхняя границаT
; если бы верхняя граница была чем-то другим, это было бы вместоObject
), а затем выдает предупреждение компилятора.Так что не так с созданием
new Object[]
вместоnew T[]
или что-то еще? Ну, массивы в Java знают свой тип компонента во время выполнения. Таким образом, переданный объект массива будет иметь неправильный тип компонента во время выполнения.Вероятно, для наиболее распространенного использования varargs, просто для итерации по элементам, это не проблема (вас не волнует тип массива во время выполнения), так что это безопасно:
Однако для всего, что зависит от типа компонента среды выполнения переданного массива, это будет небезопасно. Вот простой пример того, что небезопасно и дает сбой:
Проблема здесь в том, что мы зависим от типа,
args
чтобы бытьT[]
, чтобы вернуть его какT[]
. Но на самом деле тип аргумента во время выполнения не является экземпляромT[]
.3) Если ваш метод имеет аргумент типа
T...
(где T - любой параметр типа), то:T
T[]
Вещи, которые зависят от типа времени выполнения массива, включают: его возвращение как тип
T[]
, передачу его в качестве аргумента параметру типаT[]
, получение типа массива с использованием.getClass()
, передачу его методам, которые зависят от типа времени выполнения массива, например,List.toArray()
иArrays.copyOf()
, и т.д.2) Различие, которое я упомянул выше, слишком сложно, чтобы его можно было легко различить автоматически.
источник
FOO<T extends Something>
было бы безопасно возвращать T [] метода varargs в виде массиваSomthing
s?@SafeVarargs
- это когда единственное, что вы делаете с массивом, - это передаете его другому методу, который уже так аннотирован (например: я часто нахожу себя пишущим методы vararg, которые Существуют, чтобы преобразовать свой аргумент в список с помощьюArrays.asList(...)
и передать его другому методу; такой случай всегда может быть аннотирован,@SafeVarargs
потому чтоArrays.asList
имеет аннотацию).@SafeVarargs
если метод не являетсяstatic
илиfinal
, потому что в противном случае он мог бы быть переопределен в производном классе с чем-то небезопасным.private
методов, которые также не могут быть переопределены.Для лучшей практики, учтите это.
Если у вас есть это:
Измените это на это:
Я обнаружил, что обычно добавляю varargs, чтобы сделать его более удобным для моих абонентов. Для моей внутренней реализации почти всегда было бы удобнее использовать a
List<>
. Таким образом, чтобы закрепитьArrays.asList()
и убедиться, что я не могу представить загрязнение кучи, это то, что я делаю.Я знаю, что это только отвечает на ваш # 3. newacct дал отличный ответ для № 1 и № 2 выше, и у меня недостаточно репутации, чтобы оставить это как комментарий. :П
источник