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:
<T> void foo(T... args);
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 tipo X[]; 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 forma new X[] { ...(arguments go here)... }.
Isso funciona bem quando o tipo varargs é concreto String.... Quando é uma variável de tipo como T..., também funciona quando Té conhecido como um tipo concreto para essa chamada. por exemplo, se o método acima fizesse parte de uma classe Foo<T>e você tivesse uma Foo<String>referência, então chamar fooisso seria bom, porque sabemos que Té Stringnesse 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 usa new Object[] { ... }(aqui Objectestá o limite superior de T; 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 de new 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:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
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:
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");
}
}
O problema aqui é que dependemos do tipo de argsser T[]para retornar como T[]. Mas, na verdade, o tipo de argumento em tempo de execução não é uma instância de T[].
3) Se o seu método tiver um argumento do tipo T...(onde T é qualquer parâmetro do tipo), então:
- Seguro: se o seu método depender apenas do fato de que os elementos da matriz são instâncias de
T
- Inseguro: se depender do fato de que a matriz é uma instância de
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 tipo T[], obtendo o tipo de matriz .getClass(), passando-o para métodos que dependem do tipo de tempo de execução da matriz, como List.toArray()e Arrays.copyOf(), etc.
2) A distinção que mencionei acima é muito complicada para ser facilmente distinguida automaticamente.