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 foo
isso seria bom, porque sabemos que T
é 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 usa new Object[] { ... }
(aqui Object
está 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 args
ser 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.