1. Todas as classes herdamObject
Todas as classes em Java herdam implicitamente a Object
classe.
Analisaremos o que é herança e como ela funciona em Java na missão Java Core. Por enquanto, vamos considerar um fato simples que decorre disso:
Um objeto de qualquer classe pode ser atribuído a uma Object
variável. Exemplo:
Código | Observação |
---|---|
|
A o variável armazena uma referência a um Scanner objeto |
|
A o variável armazena uma referência a um String objeto |
|
A o variável armazena uma referência a um Integer objeto |
|
A o variável armazena uma referência a um String objeto |
É aqui que as boas notícias terminam. O compilador não rastreia o tipo original de objeto salvo em uma Object
variável, portanto, você não poderá chamar métodos no objeto salvo que não sejam os métodos da Object
classe.
Se você precisar chamar os métodos associados ao tipo original do objeto, precisará primeiro salvar uma referência a ele em uma variável do tipo correto e, em seguida, chamar os métodos nessa variável:
Código | Observação |
---|---|
|
O programa não irá compilar. A Object classe não tem nextInt() método. |
|
Isso vai funcionar. Aqui salvamos uma referência a um Scanner objeto em uma Scanner variável usando um operador typecast . |
Você não pode simplesmente atribuir uma Object
variável a uma variável Scanner, mesmo que a Object
variável armazene uma referência a um Scanner
objeto. Mas você pode fazer isso se usar o typecast operator , que você já conhece. Esta é a sua aparência geral:
Type name1 = (Type) name2;
Onde name1
é o nome de uma Type
variável e name2
é o nome de uma Object
variável que armazena uma referência a um Type
objeto.
Datilografia
Se o tipo da variável e o tipo do objeto não corresponderem, ClassCastException
será lançado um. Exemplo:
Código | Observação |
---|---|
|
Ocorrerá um erro no tempo de execução: um ClassCastException será lançado aqui |
Existe uma maneira de evitar esse erro em Java: fazemos isso verificando o tipo do objeto armazenado em uma variável :
name instanceof Type
O instanceof
operador verifica se a name
variável é um Type
objeto.
Como exemplo, vamos encontrar uma string em um array de diversos objetos:
Código | Observação |
---|---|
|
O Autoboxing converterá esses valores em Integer , String e Double , respectivamente. Percorrer a matriz de objetos Se o objeto for String Salve-o em uma String variável Exiba a variável na tela. |
2. Por que surgiram os genéricos — coleções
Voltemos às coleções.
Assim que os desenvolvedores Java criaram a ArrayList
classe, eles quiseram torná-la universal, para que pudesse armazenar qualquer tipo de objeto. Então eles usaram um array de Object
s para armazenar os elementos.
A força dessa abordagem é que você pode adicionar um objeto de qualquer tipo à coleção.
Claro, existem vários pontos fracos.
Desvantagem 1.
Sempre foi necessário escrever um operador de conversão de tipo ao recuperar elementos de uma coleção:
Código | Observação |
---|---|
|
Crie uma coleção para armazenar referências a Object objetos Preencha a coleção com números 10 , 20 , ... 100 ; Somar os elementos da coleção Typecasting é necessário |
Desvantagem 2.
Não havia garantia de que uma coleção contém um tipo específico de elemento
Código | Observação |
---|---|
|
Crie uma coleção para armazenar referências a Object objetos Nós preenchemos a coleção com números representados como Double objetos: 0.0 , 2.5 , 5.0 , ... Somar os elementos da coleção Ocorrerá um erro: a Double não pode ser convertido em umInteger |
Os dados podem ser colocados na coleção em qualquer lugar:
- em outro método
- em outro programa
- de um arquivo
- pela rede
Desvantagem 3.
Os dados na coleção podem ser alterados acidentalmente.
Você pode passar uma coleção preenchida com seus dados para algum método. Esse método, escrito por um programador diferente, adiciona seus dados à sua coleção.
O nome da coleção não indica claramente quais tipos de dados podem ser armazenados nela. E mesmo se você der um nome claro à sua variável, uma referência a ela pode ser passada para uma dúzia de métodos, e esses métodos definitivamente não saberão nada sobre o nome original da variável.
3. Genéricos
Em Java, todos esses problemas são eliminados por essa coisa legal chamada genéricos.
Em Java, genéricos significam a capacidade de adicionar parâmetros de tipo a tipos. O resultado é um tipo composto complexo. A visão geral de tal tipo composto é esta:
ClassName<TypeParameter>
Esta é uma classe genérica. E pode ser usado onde quer que você normalmente use as aulas.
Código | Descrição |
---|---|
|
Criando variáveis |
|
Criando objetos |
|
Criando matrizes |
Somente Integer
variáveis podem ser armazenadas em tal coleção:
Código | Descrição |
---|---|
|
ArrayList coleção com Integer elementos Isso é permitido E isso também funcionará
Autoboxing
Mas isso não é permitido: erro de compilação |
Você aprenderá como criar suas próprias classes com parâmetros de tipo na missão Java Collections. Por enquanto, veremos como usá-los e como eles funcionam.
4. Como funcionam os genéricos
Na verdade, os genéricos são terrivelmente primitivos.
O compilador simplesmente substitui tipos genéricos por tipos comuns. Mas quando métodos de um tipo genérico são usados, o compilador adiciona um operador typecast para converter parâmetros para os parâmetros de tipo:
Código | O que o compilador faz |
---|---|
|
|
|
|
|
|
|
|
Suponha que temos um método que soma os números em uma coleção de inteiros:
Código | O que o compilador faz |
---|---|
|
|
Em outras palavras, os genéricos são uma espécie de açúcar sintático, assim como o autoboxing, mas um pouco mais. Com o autoboxing, o compilador adiciona métodos para converter um int
em um Integer
e vice-versa, e para genéricos adiciona operadores typecast.
Depois que o compilador compila suas classes genéricas com parâmetros de tipo, elas são simplesmente convertidas em classes comuns e operadores typecast. As informações sobre os argumentos de tipo passados para variáveis de tipos genéricos são perdidas. Esse efeito também é chamado de apagamento de tipo .
Às vezes, os programadores que escrevem classes genéricas (classes com parâmetros de tipo) realmente precisam das informações sobre os tipos passados como argumentos. Na missão Java Collections, você aprenderá como lidar com isso e o que isso implica.
5. Alguns fatos sobre genéricos
Aqui estão alguns fatos mais interessantes sobre genéricos.
As classes podem ter vários parâmetros de tipo. Parece algo assim:
ClassName<TypeParameter1, TypeParameter2, TypeParameter3>
Na verdade, isso não é realmente surpreendente. Em qualquer lugar que o compilador possa adicionar um operador para converter em um tipo, ele pode adicionar vários operadores typecast.
Exemplos:
Código | Observação |
---|---|
|
O put primeiro parâmetro do método é um Integer , e o segundo é umString |
Tipos genéricos também podem ser usados como parâmetros . Parece algo assim:
ClassName<TypeParameter<TypeParameterParameter>>
Suponha que queremos criar uma lista que armazenará listas de strings. Neste caso, teremos algo assim:
// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");
// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");
// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);
Tipos genéricos (tipos com parâmetros de tipo) também podem ser usados como tipos de matriz. Parece algo assim:
ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];
Não há nada de mágico acontecendo aqui: os colchetes indicam apenas o nome do tipo:
Código | Contraparte não genérica |
---|---|
|
|
|
|
|
|
GO TO FULL VERSION