Аннотация Java SafeVarargs, существует ли стандартная или лучшая практика?
Я недавно столкнулся с java @SafeVarargs Примечание. Googling для того, что делает вариативную функцию в Java небезопасной, оставил меня довольно запутанным (отравление кучи? стертые типы?), поэтому я хотел бы знать несколько вещей:
Что делает функция с переменным числом аргументов Java небезопасный в @SafeVarargs смысл (желательно объяснить в виде углубленного примера)?
Почему эта аннотация оставлена на усмотрение программист? Это не то, что компилятор должен быть в состоянии проверить?
есть ли какой-то стандарт, которого нужно придерживаться, чтобы гарантировать, что его функция действительно безопасна для варагов? Если нет, то каковы наилучшие методы его обеспечения?
2 ответов:
1) Есть много примеров в интернете и на StackOverflow о конкретной проблеме с generics и varargs. В принципе, это когда у вас есть переменное количество аргументов типа-параметр типа:
void foo(T... args);в 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 невозможно создать массив типа-параметр component type (new T[] { ... }). Поэтому Java вместо этого используетnew Object[] { ... }(здесьObjectверхний пределT; если бы верхняя граница была чем-то другим, это было бы то, что вместоObject), а затем дает вам предупреждение компилятора.так что же не так с созданием
new Object[]вместоnew T[]или что? Ну, массивы в Java знают свой тип компонента во время выполнения. Таким образом, переданный объект массива будет иметь неправильный тип компонента во время выполнения.для, Вероятно, наиболее распространенного использования varargs, просто перебирать элементы, это не проблема (вы не заботитесь о типе времени выполнения массива), так что это безопасно:
@SafeVarargs final void foo(T... args) { for (T x : args) { // do stuff with x } }однако для всего, что зависит от типа компонента времени выполнения переданного массива, это не будет безопасно. Вот простой пример чего-то, что небезопасно и падает:
class UnSafeVarargs { static <T> T[] asArray(T... args) { return args; } static <T> T[] arrayOfTwo(T a, T b) { return asArray(a, b); } public static void main(String[] args) { String[] bar = arrayOfTwo("hi", "mom"); } }проблема здесь в том, что мы зависим от типа
argsнаT[]чтобы вернуть его какT[]. Но на самом деле тип аргумента во время выполнения не является экземпляромT[].3) Если ваш метод имеет аргумент типа
T...(где T-любой параметр типа), то:
- Safe: если ваш метод зависит только от того, что элементы массива являются экземплярами
T- небезопасно: если это зависит от того, что массив является экземпляром
T[]вещи, которые зависят от типа времени выполнения массива включают в себя: возвращая его как тип
T[], передавая его в качестве аргумента параметру типаT[], получение типа массива с помощью.getClass(), передавая его в методы, которые зависят от типа времени выполнения массива, напримерList.toArray()иArrays.copyOf()и т. д.2) различие, о котором я упоминал выше, тоже сложно быть легко различимым автоматически.
для лучших практик, рассмотрим это.
если у вас есть это:
public <T> void doSomething(A a, B b, T... manyTs) { // Your code here }измените его на это:
public <T> void doSomething(A a, B b, T... manyTs) { doSomething(a, b, Arrays.asList(manyTs)); } private <T> void doSomething(A a, B b, List<T> manyTs) { // Your code here }я обнаружил, что обычно добавляю только varargs, чтобы сделать его более удобным для моих абонентов. Почти всегда было бы удобнее для моей внутренней реализации использовать
List<>. Так что Хрюша-обратно наArrays.asList()и убедитесь, что я не могу ввести загрязнение кучи, это то, что я делаю.Я знаю, что это только ответ на ваш #3. newacct имеет учитывая отличный ответ на #1 и #2 выше, и у меня недостаточно репутации, чтобы просто оставить это в качестве комментария. : P
Comments