java - type - o que diferencia um construtor de um método qualquer




O que os argumentos do tipo de construtor significam quando colocados*antes*do tipo? (2)

Recentemente encontrei essa sintaxe Java incomum (para mim) ... aqui está um exemplo:

List list = new <String, Long>ArrayList();

Observe o posicionamento dos argumentos do tipo <String, Long> ... não é depois do tipo como normal, mas antes. Não me importo de admitir que nunca vi essa sintaxe antes. Observe também que há dois argumentos de tipo quando ArrayList só tem 1.

O posicionamento dos argumentos de tipo tem o mesmo significado que colocá-los após o tipo? Se não, o que significa o posicionamento diferente?

Por que é legal ter 2 argumentos de tipo quando o ArrayList tem apenas 1?

Eu procurei nos lugares habituais, por exemplo. Angelika Langer e aqui, mas não podemos encontrar qualquer menção a essa sintaxe em lugar algum além das regras gramaticais no arquivo de gramática Java no projeto ANTLR.


Chamando um construtor genérico

Isso é incomum, mas Java é totalmente válido. Para entender, precisamos saber que uma classe pode ter um construtor genérico, por exemplo:

public class TypeWithGenericConstructor {

    public <T> TypeWithGenericConstructor(T arg) {
        // TODO Auto-generated constructor stub
    }

}

Suponho que, na maioria das vezes, ao instanciar a classe através do construtor genérico, não precisamos tornar o argumento de tipo explícito. Por exemplo:

    new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

Agora T é claramente LocalDate . No entanto, pode haver casos em que o Java não possa inferir (deduzir) o argumento de tipo. Então nós fornecemos explicitamente usando a sintaxe da sua pergunta:

    new <LocalDate>TypeWithGenericConstructor(null);

É claro que também podemos fornecê-lo, mesmo que não seja necessário se acharmos que ele ajuda a legibilidade ou por qualquer motivo:

    new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

Na sua pergunta, você parece estar chamando o construtor java.util.ArrayList . Esse construtor não é genérico (apenas a classe ArrayList como um todo é, isso é outra coisa). Por que o Java permite que você forneça argumentos de tipo na chamada quando eles não são usados, veja minha edição abaixo. Meu Eclipse me dá um aviso :

Argumentos de tipo não utilizados para o construtor não genérico ArrayList () do tipo ArrayList; não deve ser parametrizado com argumentos

Mas não é um erro, e o programa roda bem (eu também recebo avisos sobre argumentos de tipo ausentes para List e ArrayList , mas isso novamente é uma história diferente).

Classe genérica versus construtor genérico

O posicionamento dos argumentos de tipo tem o mesmo significado que colocá-los após o tipo? Se não, o que significa o posicionamento diferente?

Não, é diferente. O tipo usual de argumento / s após o tipo ( ArrayList<Integer>() ) é para a classe genérica . Os argumentos de tipo antes são para o construtor .

As duas formas também podem ser combinadas:

    List<Integer> list = new <String, Long>ArrayList<Integer>();

Eu consideraria isso um pouco mais correto, já que agora podemos ver que a lista armazena objetos Integer (eu ainda prefiro deixar de fora o significado <String, Long> , é claro).

Por que é legal ter 2 argumentos de tipo quando o ArrayList tem apenas 1?

Primeiro, se você fornecer argumentos de tipo antes do tipo, deverá fornecer o número correto para o construtor, não para a classe, portanto, não tem nada a ver com quantos argumentos de tipo a classe ArrayList possui. Isso realmente significa que, neste caso, você não deve fornecer nenhum, pois o construtor não aceita argumentos (não é genérico). Quando você fornece alguns de qualquer maneira, eles são ignorados, e é por isso que não importa quantos ou quantos você fornece.

Por que argumentos de tipo sem sentido são permitidos?

Edite com agradecimentos ao @Slaw pelo link: Java permite argumentos de tipo em todas as chamadas de método. Se o método chamado for genérico, os argumentos de tipo serão usados; se não, eles são ignorados. Por exemplo:

    int length = "My string".<List>length();

Sim, é um absurdo A JLS (Java Language Specification) fornece essa justificativa na subseção 15.12.2.1:

Esta regra resulta de questões de compatibilidade e princípios de substituibilidade. Como interfaces ou superclasses podem ser generificadas independentemente de seus subtipos, podemos substituir um método genérico por um não genérico. No entanto, o método de substituição (não genérico) deve ser aplicável a chamadas para o método genérico, incluindo chamadas que explicitamente transmitem argumentos de tipo. Caso contrário, o subtipo não seria substituível por seu supertipo generificado.

O argumento não é válido para os construtores, uma vez que eles não podem ser diretamente sobrescritos. Mas eu suponho que eles queriam ter a mesma regra para não complicar as regras já complicadas. Em qualquer caso, a seção 15.9.3 sobre instanciação e new mais de uma vez refere-se a 15.12.2.

Links


Aparentemente, você pode prefixar qualquer método / construtor não genérico com qualquer parâmetro genérico que desejar:

new <Long>String();
Thread.currentThread().<Long>getName();

O compilador não se importa, porque não tem que corresponder a argumentos do tipo teses para parâmetros genéricos reais.

Assim que o compilador tem que verificar os argumentos, ele reclama de uma incompatibilidade:

Collections.<String, Long>singleton("A"); // does not compile

Parece um erro de compilador para mim.





generic-type-argument