Recentemente, deparei com a @SafeVarargs
anotação java . Pesquisar no Google o que torna uma função variável em Java insegura me deixou um pouco confuso (envenenamento por heap? Tipos apagados?), Então gostaria de saber algumas coisas:
O que torna uma função Java variável insegura no
@SafeVarargs
sentido (de preferência explicada na forma de um exemplo detalhado)?Por que essa anotação é deixada a critério do programador? Não é algo que o compilador possa verificar?
Existe algum padrão a que se deve seguir para garantir que sua função seja de fato segura? Caso contrário, quais são as melhores práticas para garantir isso?
retType myMethod(Arg first, Arg... others)
. Se você não especificarfirst
, uma matriz vazia será permitida e você poderá muito bem ter um método com o mesmo nome com o mesmo tipo de retorno sem argumentos, o que significa que a JVM terá dificuldade em determinar qual método deve ser chamado.Respostas:
1) Existem muitos exemplos na Internet e no StackOverflow sobre um problema específico com genéricos e varargs. Basicamente, é quando você tem um número variável de argumentos de um tipo de parâmetro de tipo:
Em Java, varargs são um açúcar sintático que passa por uma "reescrita" simples em tempo de compilação: um parâmetro do tipo varargs
X...
é convertido em um parâmetro do tipoX[]
; e toda vez que uma chamada é feita para esse método varargs, o compilador coleta todos os "argumentos da variável" que vão no parâmetro varargs e cria uma matriz da mesma formanew X[] { ...(arguments go here)... }
.Isso funciona bem quando o tipo varargs é concreto
String...
. Quando é uma variável de tipo comoT...
, também funciona quandoT
é conhecido como um tipo concreto para essa chamada. por exemplo, se o método acima fizesse parte de uma classeFoo<T>
e você tivesse umaFoo<String>
referência, então chamarfoo
isso seria bom, porque sabemos queT
éString
nesse ponto do código.No entanto, ele não funciona quando o "valor" de
T
é outro parâmetro de tipo. Em Java, é impossível criar uma matriz de um tipo de componente de parâmetro de tipo (new T[] { ... }
). Então, o Java usanew Object[] { ... }
(aquiObject
está o limite superior deT
; se o limite superior fosse algo diferente, seria isso em vez deObject
) e, em seguida, fornece um aviso do compilador.Então, o que há de errado em criar, em
new Object[]
vez denew T[]
ou o que for? Bem, matrizes em Java conhecem seu tipo de componente em tempo de execução. Portanto, o objeto de matriz passado terá o tipo de componente errado no tempo de execução.Provavelmente, para o uso mais comum de varargs, simplesmente para iterar sobre os elementos, isso não é problema (você não se importa com o tipo de tempo de execução da matriz), portanto, isso é seguro:
No entanto, para qualquer coisa que dependa do tipo de componente de tempo de execução da matriz passada, não será seguro. Aqui está um exemplo simples de algo que não é seguro e trava:
O problema aqui é que dependemos do tipo de
args
serT[]
para retornar comoT[]
. Mas, na verdade, o tipo de argumento em tempo de execução não é uma instância deT[]
.3) Se o seu método tiver um argumento do tipo
T...
(onde T é qualquer parâmetro do tipo), então:T
T[]
As coisas que dependem do tipo de tempo de execução da matriz incluem: retorná-lo como tipo
T[]
, passando-o como um argumento para um parâmetro do tipoT[]
, obtendo o tipo de matriz.getClass()
, passando-o para métodos que dependem do tipo de tempo de execução da matriz, comoList.toArray()
eArrays.copyOf()
, etc.2) A distinção que mencionei acima é muito complicada para ser facilmente distinguida automaticamente.
fonte
FOO<T extends Something>
seria seguro retornar o T [] de um método varargs como uma matriz deSomthing
s?@SafeVarargs
é quando a única coisa que você faz com a matriz é passá-la para outro método que já está tão anotado (por exemplo: eu frequentemente me pego escrevendo vários métodos que existe para converter seu argumento em uma lista usandoArrays.asList(...)
e passá-lo para outro método; esse caso sempre pode ser anotado com@SafeVarargs
porqueArrays.asList
possui a anotação).@SafeVarargs
, a menos que o método sejastatic
oufinal
, porque, caso contrário, poderia ser substituído em uma classe derivada por algo inseguro.private
métodos que também não podem ser substituídos.Para melhores práticas, considere isso.
Se você tem isso:
Altere para este:
Descobri que normalmente apenas adiciono varargs para torná-lo mais conveniente para meus chamadores. Quase sempre seria mais conveniente para minha implementação interna usar a
List<>
. Então, para pegar caronaArrays.asList()
e garantir que não há como introduzir o Heap Pollution, é isso que faço.Eu sei que isso apenas responde ao seu número 3. newacct deu uma ótima resposta para os itens 1 e 2 acima, e eu não tenho reputação suficiente para deixar isso como um comentário. : P
fonte